当前位置: 首页 > news >正文

嵌入式学习 day62 SPI子系统、adxl345驱动、驱动回顾

一、SPI

        1、特性:

        2、spi组成:


SCLK:时钟信号线。与I2C类似的,所有设备都都需要时钟信号线进行同步,该信号由主机负责提供;
MOSI:主出从入,相当于主机的数据发送线;
MISO:主入从出,相当于主机的数据接收线;
CS:片选信号,与I2C不同,SPI设备没有所谓的从机地址,连接在总线上的设备通过CS片选信号选择。在同一时刻,总线上只有一个外设被选择,CS信号线通常是低电平有效的。

注:需要注意的是,对于MOSI和MISO来说不是必须存在的,可以只有MOSI或者只有MISO。当然这样的话就只能实现单工通信方式了,这样的SPI成为三线SPI。至于到底采用三线方式还是四线方式取决于外设本身。例如,OLCD只需要主机发送数据而不需要从机发送数据,就可以使用三线SPI的连接方式。

        3、时序:


(1)时钟极性(Clock Polarity 简称CPOL):规定了时钟信号线在空闲时的状态,CPOL=0表示时钟信号线在空闲时为低电平;CPOL=1表示时钟信号线在空闲是为高电平;

        (2)时钟相位(Clock Phase简称CPHA):与I2C不同,SPI的数据接收方并不是在时钟高或者低电平时采样的,而是在时钟信号处于沿时,至于是在上升沿还是下降沿取决于CPHA。CPHA=0表示是在时钟信号变化的第一个沿时采样,CPHA=1表示是在时钟信号变化的第二个沿时采样。

注:这样的排列组合总共能够组合出四种方式,具体使用哪种方式是由外设厂家规定的

        4、spi结构

        5、spi框架流程

        与i2c类似

        6、设备树
(1)imx6ull.dtsi 

(2) arch/arm/boot/dts/imx6ull-alientek-emmc.dts   

二、adxl345

        1、概念:

        ADXL345 是由 ADI(亚德诺半导体,Analog Devices) 推出的一款低成本、低功耗、高分辨率的 三轴 MEMS(微机电系统)加速度传感器,广泛用于消费电子、可穿戴设备、工业监测、汽车电子等领域,核心功能是检测物体在 X、Y、Z 三个正交方向的线性加速度(包括重力加速度),并输出数字信号供处理器解析。

        2、连接方式:

        3、时序:
        (1)写时序

        (2)读时序

注:MB:是单个寄存器操作,还是多个寄存器操作

4、寄存器

        5、驱动编写
        (1)配置设备树

vim arch/arm/boot/dts/imx6ull-alientek-emmc.dts

        (2)驱动层编写
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/spi/spi.h>/* ADXL345 寄存器定义 */
#define ADXL345_DEVID          0x00  // 设备ID寄存器
#define ADXL345_DEVID_VAL      0xE5   // 预期设备ID值
#define ADXL345_DATA_FORMAT    0x31  // 数据格式寄存器
#define ADXL345_POWER_CTL      0x2D  // 电源控制寄存器
#define ADXL345_DATAX0         0x32  // X轴数据起始地址static ssize_t adxl345_read(struct file *fp, char __user *puser, size_t n, loff_t *off);
static int adxl345_probe(struct spi_device *spi);
static int adxl345_remove(struct spi_device *spi);
static struct spi_device *adxl345_spi;static struct of_device_id  adxl345_matchtable[] = {{.compatible = "pute,puteadxl345"},{},
};static struct spi_device_id adxl345_table[] = {{.name = "puteadxl345"},{},
};static struct file_operations fops = {.owner = THIS_MODULE,.read = adxl345_read,
};static struct miscdevice miscdevice = {.minor = MISC_DYNAMIC_MINOR,.name = "misc_adxl345",.fops = &fops,
};static struct spi_driver adxl345_driver = {.driver = {.owner = THIS_MODULE,.name = "puteadxl345",.of_match_table = adxl345_matchtable,},.id_table = adxl345_table,.probe = adxl345_probe,.remove = adxl345_remove,
};/* 读取三轴加速度的主函数 */
int adxl345_read_xyz(struct spi_device *spi, int *x, int *y, int *z)
{int ret;u8 tx_buf[7] = {0};  // 发送缓冲区:命令+6字节数据u8 rx_buf[7] = {0};  // 接收缓冲区struct spi_transfer tr = {.tx_buf = tx_buf,.rx_buf = rx_buf,.len = 7,        // 1字节命令 + 6字节数据.delay_usecs = 20,};struct spi_message msg;/* 构造读取命令 (0x80: 读标志 | 0x40: 多字节模式) */tx_buf[0] = ADXL345_DATAX0 | 0x80 | 0x40;  // 连续读取6字节数据spi_message_init(&msg);spi_message_add_tail(&tr, &msg);/* 执行SPI传输 */ret = spi_sync(spi, &msg);if (ret < 0) {pr_err("SPI transfer failed: %d\n", ret);return ret;}/* 解析原始数据 (小端模式: LSB在前) */*x = (s16)((rx_buf[2] << 8) | rx_buf[1]); *y = (s16)((rx_buf[4] << 8) | rx_buf[3]);*z = (s16)((rx_buf[6] << 8) | rx_buf[5]);return 0;
}/* 初始化ADXL345传感器 */
int adxl345_init(struct spi_device *spi)
{int ret;u8 tx[2], rx[2];/* 验证设备ID */tx[0] = ADXL345_DEVID | 0x80;  // 读命令ret = spi_write_then_read(spi, tx, 1, rx, 1);if (ret < 0 || rx[0] != ADXL345_DEVID_VAL) {pr_info("Device ID check failed (got 0x%02X, expected 0x%02X)\n", rx[0], ADXL345_DEVID_VAL);return -ENODEV;}/* 配置数据格式 (±16g, 全分辨率) */tx[0] = ADXL345_DATA_FORMAT;  // 寄存器地址tx[1] = 0x0B;                 // 0x0B = 0b1011 (全分辨率 + ±16g)[6](@ref)ret = spi_write_then_read(spi, tx, 2, NULL, 0);if (ret < 0) return ret;/* 启动测量模式 */tx[0] = ADXL345_POWER_CTL;tx[1] = 0x08;                 // 0x08 = 测量模式[6](@ref)return spi_write_then_read(spi, tx, 2, NULL, 0);
}static ssize_t adxl345_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{int ret = 0;int buf[3] = {0};if (adxl345_init(adxl345_spi) != 0) {pr_info("ADXL345 initialization failed\n");return -EIO;}/* 读取加速度数据 */if (adxl345_read_xyz(adxl345_spi, &buf[0], &buf[1], &buf[2]) != 0) {pr_info("read adxl345 failed\n");}ret = copy_to_user(puser, buf, sizeof(buf));if (ret)pr_info("copy_to_user failed\n");return sizeof(buf);
}static int adxl345_probe(struct spi_device *spi)
{int ret = 0;adxl345_spi = spi;ret = misc_register(&miscdevice);if (ret)pr_info("misc register failed\n");pr_info("adxl345_spi->bits_per_word = %d\n", adxl345_spi->bits_per_word);pr_info("adxl345_spi->mode = %#x\n", adxl345_spi->mode);pr_info("adxl345 probe ok\n");return 0;
}static int adxl345_remove(struct spi_device *spi)
{int ret = 0;ret = misc_deregister(&miscdevice);if (ret)pr_info("misc deregister failed\n");pr_info("adxl345 remove ok\n");return 0;
}static int __init adxl345_drv_init(void)
{int ret = 0;ret = spi_register_driver(&adxl345_driver);if (ret)pr_info("spi_register_driver failed");return 0;
}static void __exit adxl345_drv_exit(void)
{spi_unregister_driver(&adxl345_driver);return;
}module_init(adxl345_drv_init);
module_exit(adxl345_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("haiersen");
        (3)应用层编写
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "../adxl345_lcd_fb/framebuff.h"int main(void)
{int fd = 0;int data[3] = {0};char buf[100] = {0};fb_t *pfb = NULL;pfb = framebuff_init("/dev/fb0");if (NULL == pfb){printf("framebuff_init failed\n");return -1;}fd = open("/dev/misc_adxl345", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}clear_screen(pfb);while (1){read(fd, data, sizeof(data));sprintf(buf,"x:%05d, y:%05d, z:%05d", data[0], data[1], data[2]);draw_string(pfb,100,200, buf,0,0,0,255, 255, 255);fflush(stdout);usleep(10000);}close(fd);return 0;
}

三、驱动学习回顾

四、framebuffer

        framebuffer:帧缓存的技术,通过申请显存空间,将显存空间写入每个像素点的RGB信息来完成对屏幕像素点颜色的控制

        1、找到裸机自带的驱动代码
        (1)在arch/arm/boot/dts/imx6ull.dtsi找到compatible匹配选项

        (2)使用find . -type f -print0 | xargs -0 grep -l "函数名"找到驱动所在位置

        (3)查看驱动代码
        2、设备树

3、应用层操作framebuffer设备的方法

        (1)打开设备

        (2)ioct获得设备信息(分辨率、RGB888、RGB565)

        (3)申请显存空间

        (4)向显存空间写入数据,对应屏幕显示颜色信息

        (5)封装一些功能函数接口

4、应用层代码编写
        (1)main.c
#include "framebuff.h"
#include <stdio.h>int main(void)
{   fb_t *pfb = NULL;pfb = framebuff_init("/dev/fb0");if (NULL == pfb){printf("framebuff_init failed\n");return -1;}clear_screen(pfb);
//    draw_string(pfb, 200, 300, "Hello World!", 255, 255, 255, 0, 0, 0);
//    draw_hanzi(pfb, 200, 300, "溫", 255, 255, 255, 0, 0, 0);draw_hanzi_string(pfb, 200, 300, "温度", 255, 255, 255, 0, 0, 0);draw_hanzi_string(pfb, 250, 300, "湿度", 255, 255, 255, 0, 0, 0);framebuff_deinit(pfb);return 0;
}
        (2)framebuffer.h
#ifndef __FRAMEBUFF_H__
#define __FRAMEBUFF_H__#include <linux/fb.h>/* 單個像素點類型 */
typedef struct __rgb__ 
{unsigned char B;                    //藍色unsigned char G;                    //綠色unsigned char R;                    //紅色unsigned char Reserved;             
}rgb_t;/* 屏幕操作句柄 */
typedef struct __fb__ 
{struct fb_var_screeninfo vscreeninfo;//屏幕參數rgb_t *prgb;                        //顯存空間首地址int fd;                             //屏幕文件描述符
}fb_t;/* 漢字結構 */
typedef struct __hanzi__ 
{char str[4];                        //漢字的字符串unsigned char zimo[32];             //漢字字模
}hanzi_t;extern unsigned char fontdata_8x16[4096];
extern hanzi_t ghanzi[50];extern fb_t *framebuff_init(const char *pdevname);
extern void framebuff_deinit(fb_t *pfb);
extern int draw_pixel(fb_t *pfb, int y, int x, unsigned char r, unsigned char g, unsigned b);
extern int draw_hor_line(fb_t *pfb, int y, int x, int width, unsigned char r, unsigned char g, unsigned char b);
extern int draw_ver_line(fb_t *pfb, int y, int x, int height, unsigned char r, unsigned char g, unsigned char b);
extern int draw_dict(fb_t *pfb, int y, int x, int width, int height, unsigned char r, unsigned char g, unsigned char b);
extern int fill_dict(fb_t *pfb, int y, int x, int width, int height, unsigned char r, unsigned char g, unsigned char b);
extern void clear_screen(fb_t *pfb);
extern void draw_ascii(fb_t *pfb, int y, int x, unsigned char ch,unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb);
extern void draw_string(fb_t *pfb, int y, int x, char *pstr,unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb);
extern void draw_hanzi(fb_t *pfb, int y, int x, char *pstr, unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb);
extern void draw_hanzi_string(fb_t *pfb, int y, int x, char *pstr, unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb);
#endif
        (3)framebuffer.c
#include "framebuff.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <linux/fb.h>
#include <stdio.h>
#include <sys/mman.h>/* 打開屏幕設備,返回具有屏幕信息的句柄 */
fb_t *framebuff_init(const char *pdevname)
{int ret = 0;fb_t *pfb = NULL;pfb = malloc(sizeof(fb_t));if (NULL == pfb){return NULL;}pfb->fd = open(pdevname, O_RDWR);if (-1 == pfb->fd){return NULL;}ret = ioctl(pfb->fd, FBIOGET_VSCREENINFO, &pfb->vscreeninfo);if (ret) {return NULL;}printf("width:%d, height:%d\n", pfb->vscreeninfo.xres_virtual, pfb->vscreeninfo.yres_virtual);if (32 == pfb->vscreeninfo.bits_per_pixel) {printf("format RGB888!\n");} else if (16 == pfb->vscreeninfo.bits_per_pixel) {printf("format RGB565\n");}pfb->prgb = mmap(NULL, pfb->vscreeninfo.xres_virtual * pfb->vscreeninfo.yres_virtual * pfb->vscreeninfo.bits_per_pixel / 8,PROT_READ | PROT_WRITE, MAP_SHARED, pfb->fd, 0);if (NULL == pfb->prgb) {return NULL;}return pfb;
}/* 銷毀屏幕信息的句柄 */
void framebuff_deinit(fb_t *pfb)
{if (pfb != NULL){free(pfb);}return;
}/* 繪制一個像素點 */
int draw_pixel(fb_t *pfb, int y, int x, unsigned char r, unsigned char g, unsigned b)
{(pfb->prgb + y * pfb->vscreeninfo.xres_virtual + x)->R = r;(pfb->prgb + y * pfb->vscreeninfo.xres_virtual + x)->G = g;(pfb->prgb + y * pfb->vscreeninfo.xres_virtual + x)->B = b;return 0;
}/* 繪制橫線 */
int draw_hor_line(fb_t *pfb, int y, int x, int width, unsigned char r, unsigned char g, unsigned char b)
{int i = 0;for(i = 0; i < width; i++){draw_pixel(pfb, y, x+i, r, g, b);}return 0;
}/* 繪制豎線 */
int draw_ver_line(fb_t *pfb, int y, int x, int height, unsigned char r, unsigned char g, unsigned char b)
{int i = 0;for(i = 0; i < height; i++){draw_pixel(pfb, y+i, x, r, g, b);}return 0;
}/* 繪制矩形 */
int draw_dict(fb_t *pfb, int y, int x, int width, int height, unsigned char r, unsigned char g, unsigned char b)
{draw_hor_line(pfb, y, x, width, r, g, b);draw_hor_line(pfb, y+height, x, width, r, g, b);draw_ver_line(pfb, y, x, height, r, g, b);draw_ver_line(pfb, y, x+width, height, r, g, b);return 0;
}/* 繪制實心矩形 */
int fill_dict(fb_t *pfb, int y, int x, int width, int height, unsigned char r, unsigned char g, unsigned char b)
{int i = 0;for (i = 0; i < height; i++){draw_hor_line(pfb, y+i, x, width, r, g, b);}return 0;
}/* 清屏 */
void clear_screen(fb_t *pfb)
{fill_dict(pfb, 0, 0, pfb->vscreeninfo.xres_virtual, pfb->vscreeninfo.yres_virtual, 0, 0, 0);return;
}/* 繪制ASCII碼字符 */
void draw_ascii(fb_t *pfb, int y, int x, unsigned char ch,unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb)
{int j = 0;int i = 0;unsigned char flag = 0x80;for (j = 0; j < 16; j++){for (flag = 0x80, i = 0; i < 8; i++, flag >>= 1){if (fontdata_8x16[ch*16+j] & flag) {draw_pixel(pfb, y+j, x+i, fr, fg, fb);}else {draw_pixel(pfb, y+j, x+i, br, bg, bb);}}}return;
}/* 繪制字符串 */
void draw_string(fb_t *pfb, int y, int x, char *pstr,unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb)
{int i = 0;while (*pstr != '\0'){draw_ascii(pfb, y, x+i*8, *pstr, fr, fg, fb, br, bg, bb);pstr++;i++;}return;
}/* 顯示中文 */
void draw_hanzi(fb_t *pfb, int y, int x, char *pstr, unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb)
{unsigned char zimo[32] = {0};int i = 0;int j = 0;unsigned char flag = 0x80;int n = 0;for (n = 0; n < sizeof(ghanzi) / sizeof(ghanzi[0]); n++){if (0 == strncmp(ghanzi[n].str, pstr, 3)){break;}}if (n == (sizeof(ghanzi) / sizeof(ghanzi[0]))){return;}for (j = 0; j < 16; j++){for (flag = 0x80, i = 0; i < 8; i++, flag >>= 1){if (ghanzi[n].zimo[2*j] & flag) {draw_pixel(pfb, y+j, x+i, fr, fg, fb);}else {draw_pixel(pfb, y+j, x+i, br, bg, bb);}}for (flag = 0x80, i = 8; i < 16; i++, flag >>= 1){if (ghanzi[n].zimo[2*j+1] & flag) {draw_pixel(pfb, y+j, x+i, fr, fg, fb);}else {draw_pixel(pfb, y+j, x+i, br, bg, bb);}}}return;
}/* 繪制漢字字符串 */
void draw_hanzi_string(fb_t *pfb, int y, int x, char *pstr, unsigned char fr, unsigned char fg, unsigned char fb,unsigned char br, unsigned char bg, unsigned char bb)
{int i = 0;while (*pstr != '\0'){draw_hanzi(pfb, y, x+i*16, pstr, fr, fg, fb, br, bg, bb);pstr += 3;i++;}return;
}
        (4)font_8x16.c(acsii)
      (5)font_16x16.c(汉字)

5、应用

http://www.dtcms.com/a/363973.html

相关文章:

  • 依托深兰科技AI技术生态,深兰教育携手沪上高校企业启动就业科创营
  • CRM数据暴风升级!3步将DataEase可视化神技嵌入Cordys,销售分析直接开挂!
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘flake8’问题
  • 【Vue2 ✨】Vue2 入门之旅(十):Vuex 入门
  • 【机器学习学习笔记】Matplotlib 基本操作
  • 论文解读:基于 77 GHz FMCW 毫米波雷达的舱内占位检测
  • HDI是什么?与普通线路板有何区别?优势在哪?
  • java面试中经常会问到的多线程问题有哪些(基础版)
  • 宋红康 JVM 笔记 Day10|对象实例化
  • 2025全球绿色发展与健康生活方式高峰论坛 推动HLCC国际认证体系全球化实施
  • DuckDB新版rusty_sheet 插件测试
  • Android U Lmkd源码解析
  • 【Unity开发】丧尸围城项目实现总结
  • 自发自用分布式光伏电站进线柜防逆流测控保护装置
  • 什么是数据库管理系统(DBMS)?RDBMS和NoSQL又是什么?
  • vue2中如何使用Ant Design Vue 中的 Tooltip 文字提示
  • C#类对象映射AutoMapper
  • JUC 并发集合:高效处理多线程数据共享的利器
  • 开源的聚合支付系统源码/易支付系统 /三方支付系统
  • More Effective C++ 条款24:理解虚拟函数、多继承、虚继承和RTTI的成本
  • 第一次用pyQt6制作JSON小工具
  • =Windows下VSCode配置SSH密钥远程登录
  • C语言中奇技淫巧08-使用alloca/__builtin_alloca从栈上分配空间
  • 打工人日报#20250902
  • 自动化运维-ansible中的循环应用
  • 机器学习入门,支持向量机
  • etf期权亏几个点就爆仓了?
  • 37.Ansible循环+常用过滤器
  • docker-compose的使用
  • 让AI成为您的眼睛:星眸(StarGaze),为盲人朋友点亮前行之路