北京建设工程交易网站官网wordpress 移动商城主题
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:05
当前位置: 首页 > news >正文
北京建设工程交易网站官网,wordpress 移动商城主题,做自己的网站花多钱,怎么交换友情链接目录 一、Linux 下 SPI 驱动框架简介 1、SPI 主机驱动 2、SPI 设备驱动 SPI 设备数据收发处理流程 3、SPI 设备和驱动匹配过程 二、添加SPI 设备信息 1、添加 ICM20608 所使用的 IO 2、 在 ecspi3 节点追加 icm20608 子节点 三、编写 ICM20608 驱动 1、修改makefile…目录 一、Linux 下 SPI 驱动框架简介 1、SPI 主机驱动 2、SPI 设备驱动 SPI 设备数据收发处理流程 3、SPI 设备和驱动匹配过程 二、添加SPI 设备信息 1、添加 ICM20608 所使用的 IO 2、 在 ecspi3 节点追加 icm20608 子节点 三、编写 ICM20608 驱动 1、修改makefile编辑 2、icm20608reg.h 3、icm20608 设备结构体创建 4、probe 函数 5、icm20608 寄存器读写与初始化 ①icm20608_read_regs函数 ②icm20608_write_regs函数 ③icm20608_read_onereg函数 ④ icm20608_write_onereg函数 ⑤ icm20608_readdata函数 ⑥icm20608_reginit 函数 6、icm20608_read函数 代码如下 四、APP编写 编译验证 一、Linux 下 SPI 驱动框架简介 1、SPI 主机驱动 SPI 主机驱动就是 SOC 的 SPI 控制器驱动类似 I2C 驱动里面的适配器驱动。 Linux 内核 使用 spi_master 表示 SPI 主机驱动 spi_master 是个结构体定义在 include/linux/spi/spi.h 文件 中部分如下 struct spi_master {struct device dev;struct list_head list; …..int (*transfer)(struct spi_device *spi,struct spi_message *mesg); …..int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg); } transfer 函数和 i2c_algorithm 中的 master_xfer 函数一样控制器数据传输函数。 transfer_one_message 函数也用于 SPI 数据发送用于发送一个 spi_messageSPI 的数据会打包成 spi_message然后以队列方式发送出去 SPI 主机端最终会通过 transfer 函数与 SPI 设备进行通信因此对于 SPI 主机控制器的驱动编写者而言 transfer 函数是需要实现的因为不同的 SOC 其 SPI 控制器不同寄存器都不一样。和 I2C 适配器驱动一样 SPI 主机驱动一般都是 SOC 厂商去编写 SPI 主机驱动的核心就是申请 spi_master然后初始化 spi_master最后向 Linux 内核注册 spi_master 2、SPI 设备驱动 spi 设备驱动也和 i2c 设备驱动也很类似 Linux 内核使用 spi_driver 结构体来表示 spi 设备 驱动我们在编写 SPI 设备驱动的时候需要实现 spi_driver。 spi_driver 结构体定义在 include/linux/spi/spi.h 文件中结构体内容如下 struct spi_driver { const struct spi_device_id *id_table; int (*probe)(struct spi_device *spi); int (*remove)(struct spi_device *spi); void (*shutdown)(struct spi_device *spi); struct device_driver driver; }; spi_driver 和 i2c_driver、 platform_driver 基本一样当 SPI 设备和驱动匹配成功以后 probe 函数就会执行。 SPI 设备数据收发处理流程 SPI 设备驱动的核心是 spi_driver当我们向 Linux 内核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。首先是 spi_transfer 结构体此结构体用于描述 SPI 传输信息结构体内容部分如下 struct spi_transfer {1 const void *tx_buf; 2 void *rx_buf; 3 unsigned len; …….. 4 struct list_head transfer_list; }; 第1行 tx_buf 保存着要发送的数据。 第2行 rx_buf 用于保存接收到的数据。 第3行 len 是要进行传输的数据长度 SPI 是全双工通信因此在一次通信中发送和接收的字节数都是一样的所以 spi_transfer 中也就没有发送长度和接收长度之分。 spi_transfer 需要组织成 spi_message spi_message 也是一个结构体在使用spi_message之前需要对其进行初始化spi_message 初始化完成以后需要将 spi_transfer 添加到 spi_message 队列中spi_message 准备好以后既可以进行数据传输了数据传输分为同步传输和异步传输同步传输会阻塞的等待 SPI 数据传输完成异步传输不会阻塞的等到 SPI 数据传输完成下面采用同步传输方式来完成 SPI 数据的传输工作 3、SPI 设备和驱动匹配过程 SPI 设备和驱动的匹配过程是由 SPI 总线来完成的这点和 platform、 I2C 等驱动一样 SPI 总线为 spi_bus_type定义在 drivers/spi/spi.c 文件中内容如下 struct bus_type spi_bus_type { .name spi, .dev_groups spi_dev_groups, .match spi_match_device, .uevent spi_uevent, }; SPI 设备和驱动的匹配函数为 spi_match_device函数内容如下 static int spi_match_device(struct device *dev,struct device_driver *drv) { 1 const struct spi_device *spi to_spi_device(dev); 2 const struct spi_driver sdrv to_spi_driver(drv); 3 4 / Attempt an OF style match / 5 if (of_driver_match_device(dev, drv)) 6 return 1; 7 8 / Then try ACPI / 9 if (acpi_driver_match_device(dev, drv)) 10 return 1; 11 12 if (sdrv-id_table) 13 return !!spi_match_id(sdrv-id_table, spi); 14 15 return strcmp(spi-modalias, drv-name) 0; } 第5 行 of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 SPI 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等如果相当的话就表示 SPI 设备和驱动匹配 第9 行 acpi_driver_match_device 函数用于 ACPI 形式的匹配 第13 行 spi_match_id 函数用于传统的、无设备树的 SPI 设备和驱动匹配过程。比较 SPI设备名字和 spi_device_id 的 name 字段是否相等相等的话就说明 SPI 设备和驱动匹配 第15 行比较 spi_device 中 modalias 成员变量和 device_driver 中的 name 成员变量是否相等。 二、添加SPI 设备信息 1、添加 ICM20608 所使用的 IO 在 imx6ull-alientek-emmc.dts 文件中添加 ICM20608 所使用的 IO 信息在 iomuxc 节点 中添加一个新的子节点来描述 ICM20608 所使用的 SPI 引脚 pinctrl_ecspi3: icm20608 { fsl,pins MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 / CS / MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 / SCLK / MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 / MISO / MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 / MOSI / ; }; 注意的就是检查相应的 IO 有没有被其他的设备所使用分别搜UART2_TX、UART2_RX、UART2_RTS和UART2_CTS有复用的全部屏蔽如下 2、 在 ecspi3 节点追加 icm20608 子节点 打开 imx6qdl-sabresd.dtsi 这个设备树头文件 ecspi3 { 1 fsl,spi-num-chipselects 1; 2 cs-gpios gpio1 20 GPIO_ACTIVE_LOW; 3 pinctrl-names default; 4 pinctrl-0 pinctrl_ecspi3; 5 status okay;6 spidev: icm206080 { 7 compatible my,icm20608; 8 spi-max-frequency 8000000; 9 reg 0;}; }; 第1行设置“fsl,spi-num-chipselects”属性为 1表示只有一个设备 第2行设置“cs-gpios”属性也就是片选信号SPI 主机驱动就会控制片选引脚 第3行设置“pinctrl-names”属性也就是 SPI 设备所使用的 IO 名字 第4行设置“pinctrl-0”属性也就是所使用的 IO 对应的 pinctrl 节点 第5行“status”属性为“okay” 第6行每一个 SPI 设备都采用一个子节点来描述其设备信息icm20608 连接在 ECSPI3 的第 0 个通道上因此后面为 0 第7行SPI 设备的 compatible 属性值用于匹配设备驱动 第8行“spi-max-frequency”属性设置 SPI 控制器的最高频率这个要根据使用的SPI 设备来设置 第9行icm20608 连接在通道 0 上因此 reg 为 0。 三、编写 ICM20608 驱动 新建 icm20608.c 和 icm20608reg.h 这两个文件 icm20608.c 为 ICM20608的驱动代码 icm20608reg.h 是 ICM20608 寄存器头文件 1、修改makefile 2、icm20608reg.h #ifndef __IMC20608REG_H #define __IMC20608REG_H/ ID值 / #define ICM20608G_ID (0XAF) #define ICM20608D_ID (0XAE)/ 定义寄存器 / / ICM20608寄存器 *复位后所有寄存器地址都为0除了*Register 107(0X6B) Power Management 1 0x40Register 117(0X75) WHO_AM_I 0xAF或0xAE/ /* 陀螺仪和加速度自测(出产时设置用于与用户的自检输出值比较 / #define ICM20_SELF_TEST_X_GYRO 0x00 #define ICM20_SELF_TEST_Y_GYRO 0x01 #define ICM20_SELF_TEST_Z_GYRO 0x02 #define ICM20_SELF_TEST_X_ACCEL 0x0D #define ICM20_SELF_TEST_Y_ACCEL 0x0E #define ICM20_SELF_TEST_Z_ACCEL 0x0F/ 陀螺仪静态偏移 / #define ICM20_XG_OFFS_USRH 0x13 #define ICM20_XG_OFFS_USRL 0x14 #define ICM20_YG_OFFS_USRH 0x15 #define ICM20_YG_OFFS_USRL 0x16 #define ICM20_ZG_OFFS_USRH 0x17 #define ICM20_ZG_OFFS_USRL 0x18#define ICM20_SMPLRT_DIV 0x19 #define ICM20_CONFIG 0x1A #define ICM20_GYRO_CONFIG 0x1B #define ICM20_ACCEL_CONFIG 0x1C #define ICM20_ACCEL_CONFIG2 0x1D #define ICM20_LP_MODE_CFG 0x1E #define ICM20_ACCEL_WOM_THR 0x1F #define ICM20_FIFO_EN 0x23 #define ICM20_FSYNC_INT 0x36 #define ICM20_INT_PIN_CFG 0x37 #define ICM20_INT_ENABLE 0x38 #define ICM20_INT_STATUS 0x3A/ 加速度输出 / #define ICM20_ACCEL_XOUT_H 0x3B #define ICM20_ACCEL_XOUT_L 0x3C #define ICM20_ACCEL_YOUT_H 0x3D #define ICM20_ACCEL_YOUT_L 0x3E #define ICM20_ACCEL_ZOUT_H 0x3F #define ICM20_ACCEL_ZOUT_L 0x40/ 温度输出 / #define ICM20_TEMP_OUT_H 0x41 #define ICM20_TEMP_OUT_L 0x42/ 陀螺仪输出 / #define ICM20_GYRO_XOUT_H 0x43 #define ICM20_GYRO_XOUT_L 0x44 #define ICM20_GYRO_YOUT_H 0x45 #define ICM20_GYRO_YOUT_L 0x46 #define ICM20_GYRO_ZOUT_H 0x47 #define ICM20_GYRO_ZOUT_L 0x48#define ICM20_SIGNAL_PATH_RESET 0x68 #define ICM20_ACCEL_INTEL_CTRL 0x69 #define ICM20_USER_CTRL 0x6A #define ICM20_PWR_MGMT_1 0x6B #define ICM20_PWR_MGMT_2 0x6C #define ICM20_FIFO_COUNTH 0x72 #define ICM20_FIFO_COUNTL 0x73 #define ICM20_FIFO_R_W 0x74 #define ICM20_WHO_AM_I 0x75/ 加速度静态偏移 */ #define ICM20_XA_OFFSET_H 0x77 #define ICM20_XA_OFFSET_L 0x78 #define ICM20_YA_OFFSET_H 0x7A #define ICM20_YA_OFFSET_L 0x7B #define ICM20_ZA_OFFSET_H 0x7D #define ICM20_ZA_OFFSET_L 0x7E#endif 3、icm20608 设备结构体创建 #include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/fs.h #include linux/uaccess.h #include linux/io.h #include linux/cdev.h #include linux/device.h #include linux/of.h #include linux/of_address.h #include linux/of_irq.h #include linux/slab.h #include linux/of_address.h #include linux/of_gpio.h #include linux/atomic.h #include linux/timer.h #include linux/jiffies.h #include linux/string.h #include linux/irq.h #include linux/interrupt.h #include linux/input.h #include linux/i2c.h #include linux/delay.h #include linux/spi/spi.h#define ICM20608_CNT 1 #define ICM20608_NAME icm20680/设备结构体/ struct icm20608_dev{dev_t devid;/* 设备号 /int major;/ 主设备号 /int minor;/ 次设备号 /struct cdev cdev;/ cdev */struct class class;/ 类 */struct device device;/ 设备 */void *private_data; /*私有数据 */ }; struct icm20608_dev icm20608dev;static int icm20608_open(struct inode *inode, struct file *filp) {filp-private_data icm20608dev;return 0; } static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) {return 0; } static int icm20608_release(struct inode *inode, struct file filp) {return 0; }/ icm20608操作函数 */ struct file_operations icm20608_fops {.owner THIS_MODULE,.open icm20608_open,.release icm20608_release,.read icm20608_read, };static int icm20608_probe(struct spi_device spi) {int ret 0;/ 1、构建设备号 /icm20608dev.major0;if(icm20608dev.major){icm20608dev.devid MKDEV(icm20608dev.major,0);ret register_chrdev_region(icm20608dev.devid,ICM20608_CNT,ICM20608_NAME);}else{ret alloc_chrdev_region(icm20608dev.devid,0,ICM20608_CNT,ICM20608_NAME);icm20608dev.major MAJOR(icm20608dev.devid);icm20608dev.minor MINOR(icm20608dev.devid);}if(ret 0){printk(icm20608 chrdev_region err!\r\n);goto fail_devid;}printk(icm20608dev major %d, minor %d\r\n,icm20608dev.major,icm20608dev.minor);/ 2、注册设备 /icm20608dev.cdev.owner THIS_MODULE;cdev_init(icm20608dev.cdev,icm20608_fops);ret cdev_add(icm20608dev.cdev,icm20608dev.devid,ICM20608_CNT);if(ret 0){printk(icm20608 cdev_add err!\r\n);goto fail_cdev;}/ 3、创建类 /icm20608dev.class class_create(THIS_MODULE,ICM20608_NAME);if(IS_ERR(icm20608dev.class)){ret PTR_ERR(icm20608dev.class);printk(icm20608 chrdev_class err!\r\n);goto fail_class;}/ 4、创建设备 */icm20608dev.device device_create(icm20608dev.class,NULL,icm20608dev.devid,NULL,ICM20608_NAME);if(IS_ERR(icm20608dev.device)){ret PTR_ERR(icm20608dev.device);printk(icm20608 chrdev_device err!\r\n);goto fail_device;}/设置icm20608私有数据/icm20608dev.private_data spi;printk(icm20608_probe\r\n);return 0; fail_device:class_destroy(icm20608dev.class); fail_class: cdev_del(icm20608dev.cdev); fail_cdev:unregister_chrdev_region(icm20608dev.devid,ICM20608_CNT); fail_devid:return ret; }static int icm20608_remove(struct spi_device spi) {/ 删除字符设备 */cdev_del(icm20608dev.cdev);/注销设备号/unregister_chrdev_region(icm20608dev.devid,ICM20608_CNT);/摧毁设备/device_destroy(icm20608dev.class,icm20608dev.devid);/摧毁类/class_destroy(icm20608dev.class);printk(icm20608_remove\r\n);return 0; }/传统匹配/ struct spi_device_id icm20608_id[] {{my,icm20608,0},{} }; /设备树匹配/ static const struct of_device_id icm20608_of_match[] {{.compatible my,icm20608},{} }; /* SPI驱动结构体 */ static struct spi_driver icm20608_driver {.probe icm20608_probe,.remove icm20608_remove,.driver {.owner THIS_MODULE,.name icm20608,.of_match_table icm20608_of_match, },.id_table icm20608_id, };/驱动入口/ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); } /驱动出口/ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); }module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(ba che kai qi lai); 当 icm20608 设备和此驱动匹配成功以后 icm20608_probe 函数就会执行。同样的当注销此驱动的时候 icm20608_remove 函数会执行。 4、probe 函数 在probe函数里面添加初始化spi_device和icm20608 315行设置 SPI 为模式 0也就是 CPOL0 CPHA0 316行设置好 spi_device 以后需要使用 spi_setup 配置一下 318行设置 icm20608dev 的 private_data 成员变量为 spi_device 321行调用 icm20608_reginit 函数初始化 ICM20608主要是初始化 ICM20608 指定寄存器 5、icm20608 寄存器读写与初始化 SPI 驱动最终是通过读写 icm20608 的寄存器来实现的因此需要编写相应的寄存器读写函数并且使用这些读写函数来完成对 icm20608 的初始化 ①icm20608_read_regs函数 从 icm20608 中读取连续多个寄存器数据注意在这实验中 SPI 为全双工通讯没有所谓的发送和接收长度之分。要读取或者发送 N 个字节就要封装 N1 个字节第 1 个字节是告诉设备我们要进行读还是写后面的 N 个字节才是我们要读或者发送的数据。 因为是读操作因此在第 77行设置第一个数据 bit7 位 1表示读操作 67和71行用kzalloc申请内存的时候 效果等同于先是用 kmalloc() 申请空间 , 然用 memset() 来初始化 ,所有申请的元素都被初始化为 0GFP_KERNEL内核内存的正常分配. 可能睡眠. 81行在使用spi_message之前需要对其进行初始化spi_message初始化函数为spi_message_init 函数原型如下 void spi_message_init(struct spi_message *m) m要初始化的 spi_message。 返回值无。 82行spi_message 初始化完成以后需要将 spi_transfer 添加到 spi_message 队列中这里我们要用 到 spi_message_add_tail 函数此函数原型如下 void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m) t要添加到队列中的 spi_transfer。 mspi_transfer 要加入的 spi_message。 返回值无 83行spi_message 准备好以后既可以进行数据传输了数据传输分为同步传输和异步传输同步 传输会阻塞的等待 SPI 数据传输完成同步传输函数为 spi_sync函数原型如下 int spi_sync(struct spi_device *spi, struct spi_message *message) spi要进行数据传输的 spi_device。 message要传输的 spi_message。 返回值错误返回负值。 89-92行用kfree函数释放内存 ②icm20608_write_regs函数 向 icm20608 连续写入多个寄存器数据。此函数和icm20608_read_regs 函数区别不大。 123行清零表示写操作 ③icm20608_read_onereg函数 读取 icm20608 指定寄存器数据 ④ icm20608_write_onereg函数 向 icm20608 指定寄存器写入数据 ⑤ icm20608_readdata函数 读取 icm20608 六轴传感器和温度传感器原始数据值应用程序读取 icm20608 的时候这些传感器原始数据就会上报给应用程序先在设备结构体中添加对应数据类型 ⑥icm20608_reginit 函数 初始化 icm20608 6、icm20608_read函数 当应用程序调用 read 函数读取 icm20608 设备文件的时候此函数就会执行。此函数调用上面编写好的icm20608_readdata 函数读取 icm20608 的原始数据并将其上报给应用程序。 代码如下 #include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/fs.h #include linux/uaccess.h #include linux/io.h #include linux/cdev.h #include linux/device.h #include linux/of.h #include linux/of_address.h #include linux/of_irq.h #include linux/slab.h #include linux/of_address.h #include linux/of_gpio.h #include linux/atomic.h #include linux/timer.h #include linux/jiffies.h #include linux/string.h #include linux/irq.h #include linux/interrupt.h #include linux/input.h #include linux/i2c.h #include linux/delay.h #include linux/spi/spi.h #include icm20608reg.h#define ICM20608_CNT 1 #define ICM20608_NAME icm20680/设备结构体/ struct icm20608_dev {dev_t devid; /* 设备号 /int major; / 主设备号 /int minor; / 次设备号 /struct cdev cdev; / cdev */struct class class; / 类 */struct device device; / 设备 */void *private_data; /*私有数据 /signed int gyro_x_adc; / 陀螺仪X轴原始值 /signed int gyro_y_adc; / 陀螺仪Y轴原始值 /signed int gyro_z_adc; / 陀螺仪Z轴原始值 /signed int accel_x_adc; / 加速度计X轴原始值 /signed int accel_y_adc; / 加速度计Y轴原始值 /signed int accel_z_adc; / 加速度计Z轴原始值 /signed int temp_adc; / 温度原始值 / }; struct icm20608_dev icm20608dev;/** description : 从 icm20608 读取多个寄存器数据 param – dev : icm20608 设备* param – reg : 要读取的寄存器首地址* param – val : 读取到的数据* param – len : 要读取的数据长度* return : 操作结果*/ static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len) {int ret -1;unsigned char txdata[1];unsigned char * rxdata;struct spi_message m;struct spi_transfer *t;struct spi_device *spi (struct spi_device )dev-private_data;t kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); / 申请内存 */if(!t) {return -ENOMEM;}rxdata kzalloc(sizeof(char) * len, GFP_KERNEL); /* 申请内存 /if(!rxdata) {goto out1;}/ 一共发送len1个字节的数据第一个字节为寄存器首地址一共要读取len个字节长度的数据/txdata[0] reg | 0x80; / 写数据的时候首寄存器地址bit8要置1 表示读/ t-tx_buf txdata; / 要发送的数据 /t-rx_buf rxdata; / 要读取的数据 /t-len len1; / t-len发送的长度读取的长度 /spi_message_init(m); / 初始化spi_message /spi_message_add_tail(t, m);/ 将spi_transfer添加到spi_message队列 /ret spi_sync(spi, m); / 同步发送 /if(ret) {goto out2;}memcpy(buf , rxdata1, len); / 只需要读取的数据 /out2:kfree(rxdata); / 释放内存 / out1: kfree(t); / 释放内存 /return ret; }/** description : 向 icm20608 多个寄存器写入数据 param – dev : icm20608 设备* param – reg : 要写入的寄存器首地址* param – val : 要写入的数据缓冲区* param – len : 要写入的数据长度* return : 操作结果* */ static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, int len) {int ret -1;unsigned char *txdata;struct spi_message m;struct spi_transfer *t;struct spi_device *spi (struct spi_device )dev-private_data;t kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); / 申请内存 /if(!t) {return -ENOMEM;}txdata kzalloc(sizeof(char)len, GFP_KERNEL);if(!txdata) {goto out1;}/ 一共发送len1个字节的数据第一个字节为寄存器首地址len为要写入的寄存器的集合*/txdata reg ~0x80; / 写数据的时候首寄存器地址bit8要清零 /memcpy(txdata1, buf, len); / 把len个寄存器拷贝到txdata里等待发送 /t-tx_buf txdata; / 要发送的数据 /t-len len1; / t-len发送的长度读取的长度 /spi_message_init(m); / 初始化spi_message /spi_message_add_tail(t, m);/ 将spi_transfer添加到spi_message队列 /ret spi_sync(spi, m); / 同步发送 /if(ret) {goto out2;}out2:kfree(txdata); / 释放内存 / out1:kfree(t); / 释放内存 /return ret; } /** description : 读取icm20608指定寄存器值读取一个寄存器 param - dev: icm20608设备* param - reg: 要读取的寄存器* return : 读取到的寄存器值*/ static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg) {u8 data 0;icm20608_read_regs(dev, reg, data, 1);return data; } /icm20608写一个寄存器/ static void icm20608_write_onereg(struct icm20608_dev dev, u8 reg, u8 value) {u8 buf value;icm20608_write_regs(dev, reg, buf, 1); } /** description : 读取ICM20608的数据读取原始数据包括三轴陀螺仪、 : 三轴加速度计和内部温度。* param - dev : ICM20608设备* return : 无。*/ void icm20608_readdata(struct icm20608_dev dev) {unsigned char data[14] {0};icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);dev-accel_x_adc (signed short)((data[0] 8) | data[1]);dev-accel_y_adc (signed short)((data[2] 8) | data[3]);dev-accel_z_adc (signed short)((data[4] 8) | data[5]);dev-temp_adc (signed short)((data[6] 8) | data[7]);dev-gyro_x_adc (signed short)((data[8] 8) | data[9]);dev-gyro_y_adc (signed short)((data[10] 8) | data[11]);dev-gyro_z_adc (signed short)((data[12] 8) | data[13]); }/** description : 打开设备 param - inode : 传递给驱动的inode* param - filp : 设备文件file结构体有个叫做pr似有ate_data的成员变量* 一般在open的时候将private_data似有向设备结构体。* return : 0 成功;其他 失败*/ static int icm20608_open(struct inode *inode, struct file *filp) {filp-private_data icm20608dev;return 0; } /** description : 从设备读取数据 * param - filp : 要打开的设备文件(文件描述符)* param - buf : 返回给用户空间的数据缓冲区* param - cnt : 要读取的数据长度* param - offt : 相对于文件首地址的偏移* return : 读取的字节数如果为负值表示读取失败*/ static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) {signed int data[7];long err 0;struct icm20608_dev *dev (struct icm20608_dev *)filp-private_data;icm20608_readdata(dev);data[0] dev-gyro_x_adc;data[1] dev-gyro_y_adc;data[2] dev-gyro_z_adc;data[3] dev-accel_x_adc;data[4] dev-accel_y_adc;data[5] dev-accel_z_adc;data[6] dev-temp_adc;err copy_to_user(buf, data, sizeof(data));return 0; } static int icm20608_release(struct inode *inode, struct file filp) {return 0; }/ icm20608操作函数 */ struct file_operations icm20608_fops {.owner THIS_MODULE,.open icm20608_open,.release icm20608_release,.read icm20608_read, };/** ICM20608内部寄存器初始化函数 * param : 无* return : 无/ void icm20608_reginit(void) {u8 value 0;icm20608_write_onereg(icm20608dev, ICM20_PWR_MGMT_1, 0x80); / 复位复位后为0x40,睡眠模式 /mdelay(50);icm20608_write_onereg(icm20608dev, ICM20_PWR_MGMT_1, 0x01); / 关闭睡眠自动选择时钟 /mdelay(50);value icm20608_read_onereg(icm20608dev, ICM20_WHO_AM_I);printk(ICM20608 ID %#X\r\n, value);value icm20608_read_onereg(icm20608dev, ICM20_PWR_MGMT_1);printk(ICM20_PWR_MGMT_1 %#X\r\n, value);icm20608_write_onereg(icm20608dev, ICM20_SMPLRT_DIV, 0x00); / 输出速率是内部采样率 /icm20608_write_onereg(icm20608dev, ICM20_GYRO_CONFIG, 0x18); / 陀螺仪±2000dps量程 /icm20608_write_onereg(icm20608dev, ICM20_ACCEL_CONFIG, 0x18); / 加速度计±16G量程 /icm20608_write_onereg(icm20608dev, ICM20_CONFIG, 0x04); / 陀螺仪低通滤波BW20Hz /icm20608_write_onereg(icm20608dev, ICM20_ACCEL_CONFIG2,0x04); / 加速度计低通滤波BW21.2Hz /icm20608_write_onereg(icm20608dev, ICM20_PWR_MGMT_2, 0x00); / 打开加速度计和陀螺仪所有轴 /icm20608_write_onereg(icm20608dev, ICM20_LP_MODE_CFG, 0x00); / 关闭低功耗 /icm20608_write_onereg(icm20608dev, ICM20_FIFO_EN, 0x00); / 关闭FIFO */ } static int icm20608_probe(struct spi_device spi) {int ret 0;/ 1、构建设备号 /icm20608dev.major 0;if (icm20608dev.major){icm20608dev.devid MKDEV(icm20608dev.major, 0);ret register_chrdev_region(icm20608dev.devid, ICM20608_CNT, ICM20608_NAME);}else{ret alloc_chrdev_region(icm20608dev.devid, 0, ICM20608_CNT, ICM20608_NAME);icm20608dev.major MAJOR(icm20608dev.devid);icm20608dev.minor MINOR(icm20608dev.devid);}if (ret 0){printk(icm20608 chrdev_region err!\r\n);goto fail_devid;}printk(icm20608dev major %d, minor %d\r\n, icm20608dev.major, icm20608dev.minor);/ 2、注册设备 /icm20608dev.cdev.owner THIS_MODULE;cdev_init(icm20608dev.cdev, icm20608_fops);ret cdev_add(icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);if (ret 0){printk(icm20608 cdev_add err!\r\n);goto fail_cdev;}/ 3、创建类 /icm20608dev.class class_create(THIS_MODULE, ICM20608_NAME);if (IS_ERR(icm20608dev.class)){ret PTR_ERR(icm20608dev.class);printk(icm20608 chrdev_class err!\r\n);goto fail_class;}/ 4、创建设备 */icm20608dev.device device_create(icm20608dev.class, NULL,icm20608dev.devid, NULL, ICM20608_NAME);if (IS_ERR(icm20608dev.device)){ret PTR_ERR(icm20608dev.device);printk(icm20608 chrdev_device err!\r\n);goto fail_device;}/初始化spi_device/spi-mode SPI_MODE_0;spi_setup(spi);/设置icm20608私有数据/icm20608dev.private_data spi;/初始化icm20608/icm20608_reginit();printk(icm20608_probe\r\n);return 0;fail_device:class_destroy(icm20608dev.class); fail_class:cdev_del(icm20608dev.cdev); fail_cdev:unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT); fail_devid:return ret; }static int icm20608_remove(struct spi_device spi) {/ 删除字符设备 */cdev_del(icm20608dev.cdev);/注销设备号/unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);/摧毁设备/device_destroy(icm20608dev.class, icm20608dev.devid);/摧毁类/class_destroy(icm20608dev.class);printk(icm20608_remove\r\n);return 0; }/传统匹配/ struct spi_device_id icm20608_id[] {{my,icm20608, 0},{} }; /设备树匹配/ static const struct of_device_id icm20608_of_match[] {{.compatible my,icm20608},{} }; /* SPI驱动结构体 */ static struct spi_driver icm20608_driver {.probe icm20608_probe,.remove icm20608_remove,.driver {.owner THIS_MODULE,.name icm20608,.of_match_table icm20608_of_match,},.id_table icm20608_id, };/驱动入口/ static int __init icm20608_init(void) {return spi_register_driver(icm20608_driver); } /驱动出口/ static void __exit icm20608_exit(void) {spi_unregister_driver(icm20608_driver); }module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(ba che kai qi lai); 四、APP编写 #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include unistd.h #include stdlib.h #include string.h #include sys/ioctl.h #include linux/input.h/*argc:应用程序参数个数argv数组元素个数argv:具体参数,也可以写作char **argv./icm20608APP filename ./icm20608APP /dev/icm20608 */int main(int argc, char *argv[]) {int fd;char *filename;signed int databuf[7];unsigned char data[14];signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;signed int accel_x_adc, accel_y_adc, accel_z_adc;signed int temp_adc;float gyro_x_act, gyro_y_act, gyro_z_act;float accel_x_act, accel_y_act, accel_z_act;float temp_act;int ret 0;/判断命令行输入参数是否正确/if(argc ! 2){printf(error usage!\r\n);return -1;}/用指针指向文件/filename argv[1];/打开文件/fd open(filename , O_RDWR);if(fd 0){printf(file open failed\r\n,filename);return -1;}while(1){ret read(fd, databuf, sizeof(databuf));if(ret 0) { /* 数据读取成功 /gyro_x_adc databuf[0];gyro_y_adc databuf[1];gyro_z_adc databuf[2];accel_x_adc databuf[3];accel_y_adc databuf[4];accel_z_adc databuf[5];temp_adc databuf[6];/ 计算实际值 */gyro_x_act (float)(gyro_x_adc) / 16.4;gyro_y_act (float)(gyro_y_adc) / 16.4;gyro_z_act (float)(gyro_z_adc) / 16.4;accel_x_act (float)(accel_x_adc) / 2048;accel_y_act (float)(accel_y_adc) / 2048;accel_z_act (float)(accel_z_adc) / 2048;temp_act ((float)(temp_adc) - 25 ) / 326.8 25;printf(\r\n原始值:\r\n);printf(gx %d, gy %d, gz %d\r\n, gyro_x_adc, gyro_y_adc, gyro_z_adc);printf(ax %d, ay %d, az %d\r\n, accel_x_adc,accel_y_adc, accel_z_adc);printf(temp %d\r\n, temp_adc);printf(实际值:);printf(act gx %.2f°/S, act gy %.2f°/S, act gz %.2f°/S\r\n, gyro_x_act, gyro_y_act, gyro_z_act);printf(act ax %.2fg, act ay %.2fg, act az %.2fg\r\n, accel_x_act, accel_y_act, accel_z_act);printf(act temp %.2f°C\r\n, temp_act);}usleep(100000); /*100ms */}/关闭文件/close(fd);return 0; } 最终将传感器原始数据和得到的实际值显示在终端上。 编译验证 在编译APP的时候编译命令加入如下参数即可使能硬件浮点编译 -march-armv7-a -mfpu-neon -mfloathard 编译之后有没有使用硬件浮点呢使用 arm-linux-gnueabihf-readelf 查看一下编译出来的 icm20608App 就知道了输入如下命令 arm-linux-gnueabihf-readelf -A icm20608App 使用APP测试如下 这是会一直打印的部分截图如上图
相关文章
-
北京建设工程建设交易信息网站网站被挂黑链排名降权
北京建设工程建设交易信息网站网站被挂黑链排名降权
- 技术栈
- 2026年03月21日
-
北京建设高端网站找人做个app需要多少钱
北京建设高端网站找人做个app需要多少钱
- 技术栈
- 2026年03月21日
-
北京建设高端网站卢松松wordpress模板
北京建设高端网站卢松松wordpress模板
- 技术栈
- 2026年03月21日
-
北京建设工程质量总站网站网站内页降权 关键词排名下降
北京建设工程质量总站网站网站内页降权 关键词排名下降
- 技术栈
- 2026年03月21日
-
北京建设集团招聘信息网站烟台电商网站建设
北京建设集团招聘信息网站烟台电商网站建设
- 技术栈
- 2026年03月21日
-
北京建设交易工程信息网站著名的淘宝客网站
北京建设交易工程信息网站著名的淘宝客网站
- 技术栈
- 2026年03月21日






