LinuxSPI驱动实验核心要点(ICM-20608开发实战)
一、Linux SPI驱动框架核心架构
Linux SPI驱动采用“主机控制器驱动 + 设备驱动”的分层结构,与I²C驱动逻辑类似。
1. SPI主机驱动(SPI Master)
- 对应SOC的SPI控制器驱动,由芯片厂商预开发,使用者无需重复编写。
- 核心是`spi_master`结构体,关键成员为`transfer()`函数,负责实现SPI总线的数据传输调度,不同SOC需根据控制器寄存器特性定制该函数。
- 操作流程:先通过`spi_alloc_master()`申请主机驱动结构体,初始化后通过`spi_register_master()`或厂商封装的`spi_bitbang_start()`注册到内核;注销时使用`spi_unregister_master()`释放资源。
2. SPI设备驱动(SPI Driver)
- 对应外设驱动,需开发者自主编写,核心载体是`spi_driver`结构体,结构与I²C、platform驱动一致。
- 核心逻辑:定义匹配规则与`probe()`、`remove()`函数,驱动注册后,当与SPI设备匹配成功,`probe()`会自动执行完成外设初始化。
- 操作流程:初始化`spi_driver`后,通过`spi_register_driver()`注册到内核,注销时调用`spi_unregister_driver()`清理资源。
3. SPI设备与驱动匹配规则
- 匹配由SPI总线的`spi_bus_type`完成,核心匹配函数为`spi_match_device()`,规则分三层优先级:
-设备树匹配:比较设备节点`compatible`属性与驱动`of_match_table`中的值;
- ACPI匹配:适配特定硬件的电源管理匹配规则;
- 传统匹配:无设备树时,对比驱动`id_table`的设备名,或直接校验`spi_device`的`modalias`与驱动`name`是否一致。
二、I.MX6U SPI主机驱动核心机制
I.MX6U的SPI主机驱动为ECSPI驱动,底层代码位于`drivers/spi/spi-imx.c`,采用platform驱动框架,匹配靠设备树的`compatible`属性。
1. 驱动匹配逻辑
- 设备树节点中`compatible`属性标注为`"fsl,imx6ul-ecspi"`,内核通过`spi_imx_driver`的`of_match_table`精准匹配,匹配成功后自动触发`spi_imx_probe()`函数。
2. 核心初始化与传输
- `spi_imx_probe()`会从设备树读取配置参数,初始化并注册`spi_master`,调用`spi_bitbang_start()`完成主机驱动的启动。
- 数据收发的核心链路为`spi_imx_transfer()`→`spi_imx_pio_transfer()`→`spi_imx_push()`,最终通过读写SOC的ECSPI控制器寄存器完成数据传输,支持8位、16位、32位不同位宽的数据收发操作。
三、ICM-20608 SPI设备驱动完整开发流程
本实验的核心目标是驱动I.MX6U-ALPHA开发板上的ICM-20608六轴传感器,实现原始传感器数据的读取,具体流程如下:
1. 设备信息描述
- 先根据实际硬件引脚,在设备树中创建或修改pinctrl子节点,确保GPIO引脚正确配置为SPI功能。
- 在对应SPI总线节点下添加ICM-20608的子节点,配置SPI时序模式、片选信号、频率等参数,并将`compatible`属性设为`"alientek,icm20608"`,为后续驱动匹配打基础。
2. SPI驱动注册实现
- 构建`spi_driver`结构体,核心包含传统匹配ID表、设备树匹配表、`probe()`初始化函数、`remove()`清理函数。
- 在驱动入口函数中调用`spi_register_driver()`注册驱动,出口函数中调用`spi_unregister_driver()`完成注销,确保驱动随模块加载卸载生效。
3. Probe与Remove函数核心职责
- `probe()`函数:完成字符设备的全流程初始化。先分配和注册字符设备号,初始化`cdev`并添加到系统,创建内核类与设备节点;接着配置SPI设备的工作模式,将`spi_device`设为驱动私有数据;最后调用寄存器初始化函数,完成ICM-20608的片内配置。
- `remove()`函数:负责资源回收,销毁设备节点与内核类,注销字符设备号,清理`cdev`,确保模块卸载后无资源残留。
4. 寄存器读写与初始化
- 驱动的核心是实现传感器寄存器的读写,再以此为基础完成初始化和数据采集:
- 多寄存器读写:`icm20608_read_regs()`用于连续读取多个寄存器,SPI全双工特性决定操作需封装N+1字节,首字节为读标志(寄存器地址位7置1),后N字节为读取的数据;
`icm20608_write_regs()`类似,首字节为写标志,后续传输待写入的数据。
- 单寄存器读写:基于多寄存器函数封装,简化对单一寄存器的读写操作。
- 数据采集:`icm20608_readdata()`读取传感器的六轴和温度原始数据,存入设备结构体供后续调用。
- 初始化:`icm20608_reginit()`负责配置传感器核心寄存器,包括唤醒、时钟选择、量程设定、采样率配置等,确保传感器进入正常工作状态。
5. 字符设备驱动框架实现
- 定义文件操作集合`file_operations`,核心是`icm20608_read()`函数。
- 当应用层调用`read()`读取设备文件时,该函数从传感器获取原始数据,通过`copy_to_user()`拷贝到用户空间。注意驱动层需避免浮点运算,保持内核高效,物理量转换由应用层完成。
四、测试App开发与运行
1. App开发要点
- 程序先校验参数,打开对应的设备节点;在循环中定期调用`read()`读取原始数据,随后根据公式将原始值转换为实际物理量。
- 关键转换规则:陀螺仪原始值除以16.4得到角速度(单位°/S);加速度计原始值除以2048得到加速度(单位g);温度原始值按公式换算为摄氏度,转换过程启用硬件浮点提升运算效率。
2. 编译与运行
- 编译驱动:编写Makefile,设置`obj-m`为驱动模块名,通过内核构建系统编译生成`.ko`模块文件。
- 编译App:针对I.MX6U的硬件浮点特性,编译时加入`-march=armv7-a -mfpu=neon -mfloat-abi=hard`参数,启用硬件浮点单元加速计算,生成可执行程序。
- 运行测试:加载驱动模块,运行测试App,程序会持续输出传感器的原始数据与换算后的实际值。开发板静止时,Z轴加速度约1g;晃动时,陀螺仪和加速度计数值会随运动变化,以此验证驱动功能。