SPI子系统
SPI驱动框架
SPI 驱动框架和 I2C 很类似 ,都分为主机控制器驱动和设备驱动。
SPI主机驱动
SPI 主机驱动就是 SOC 的 SPI 控制器驱动,类似 I2C 驱动里面的适配器驱动。 Linux 内核
使用结构体spi_master
表示 SPI 主机驱动 。
SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册
spi_master。
和 I2C 适配器驱动一样, SPI 主机驱动一般都是半导体厂商去编写的。
SPI 设备驱动
Linux 内核使用 spi_driver 结构体来表示 spi 设备驱动。 SPI 设备驱动的关键就是 spi_driver
,申请,设置,向内核注册 spi_driver
。
1 | static struct spi_driver chip_spi_driver = { |
spi_driver 注册
- 传统方法
在驱动入口init函数中,调用spi_register_driver
来注册 spi_driver。
在驱动出口exit函数中,调用spi_unregister_driver
来注销 spi_driver
- 使用宏定义
module_spi_driver
来直接注册spi_driver
这个宏定义将 spi_register/unregister_driver() 与 module_init 和 module_exit 封装了起来。
注册完成,匹配成功就可调用probe函数
SPI device 与 driver 匹配
同样与I2C子系统非常相似,当匹配成功, probe 函数就会被调用。
常用compatible属性进行匹配。
spi_driver设置
spi设备驱动本质上任属于字符设备驱动范畴。
- 在prob函数中
进行字符设备的注册,设备节点的创建,将file_operations
结构体注册进内核,并初始化spi_device
1 | static struct spi_device *spi; |
- 在remove函数
进行与prob函数中顺序相反的注销。
- 填充
file_operations
结构体的open, read, write,release等函数。
注意:platform_device 中如果不提供 release 函数 ,则在调用 platform_device_unregister 时会出现警告,
如果实在无事可做,可以提供一个空的release 函数。
SPI 设备树节点
设备树添加节点
1 | &ecspi3 { |
pinctrl子系统修改
1 | pinctrl_ecspi3: ecspi3 { |
片选引脚使用软件片选,即使用一个GPIO引脚进行CS引脚模拟。
SPI 通信过程
spi通信步骤
- 申请并初始化 spi_transfer,设置 spi_transfer 的 tx_buf 成员变量, tx_buf 为要发送的数
据。然后设置 rx_buf 成员变量, rx_buf 保存着接收到的数据。最后设置 len 成员变量,也就是
要进行数据通信的长度。 - 使用 spi_message_init 函数初始化 spi_message。
- 使用spi_message_add_tail函数将前面设置好的spi_transfer添加到spi_message队列中。
- 使用 spi_sync 函数完成 SPI 数据同步传输。
1 | /* SPI 多字节发送 */ |
SPI 数据传输也支持异步传输,异步传输不会阻塞地等到完成,异步传输需要设置 spi_message 中的 complete成员变量, complete
是一个回调函数,当 SPI 异步传输完成以后此函数就会被调用。
SPI 异步传输函数为 spi_async,函数原型如下:
1 | int spi_async(struct spi_device *spi, struct spi_message *message) |
spi通信函数封装
参考内核源码 /kernel/driver/spi/spi.c spi.h
实际还是 spi_transfer, spi_message进一步封装
1 | static inline int spi_write(struct spi_device *spi, const void *buf, size_t len) |