Linux驱动如何支持SPI协议
导读:Linux 驱动支持 SPI 协议的整体思路 在 Linux 中,支持 SPI 协议通常分为三层:底层 SPI 控制器驱动(实现 struct spi_controller,负责时钟、FIFO、DMA、片选等)、中间 SPI 核心层(提供总...
Linux 驱动支持 SPI 协议的整体思路
在 Linux 中,支持 SPI 协议通常分为三层:底层 SPI 控制器驱动(实现 struct spi_controller,负责时钟、FIFO、DMA、片选等)、中间 SPI 核心层(提供总线/设备/驱动模型与消息队列)、上层 SPI 设备协议驱动(匹配具体外设,提供 probe/remove 与数据读写接口)。核心数据结构包括:struct spi_controller(主机/从机控制器)、struct spi_device(挂在某条总线上的从设备实例)、struct spi_driver(协议驱动)、struct spi_transfer(一次传输片段)、struct spi_message(由多个 transfer 组成的一次完整事务)。SPI 时序由 CPOL/CPHA 与 bits_per_word、speed_hz 等参数决定,传输为全双工,消息内片选可保持连续。现代内核还支持 Dual/Quad 等多线模式与 DMA 提升吞吐。
实现步骤
-
控制器驱动(主机侧)
- 定义并初始化 spi_controller,设置能力标志(如 SPI_MASTER_HALF_DUPLEX、支持的字长掩码、最大/最小速率等)。
- 实现关键回调:setup(按从设备的 mode/speed/字长配置寄存器)、transfer_one / transfer_one_message(发起一次或整条消息的传输)、可选的 can_dma、以及 prepare_message / unprepare_message、prepare_transfer_hardware / unprepare_transfer_hardware、set_cs(片选控制,若控制器无硬件片选则使用 GPIO)。
- 若使用 GPIO 片选,在控制器中准备 cs_gpios 并在 set_cs 中驱动;也可在设备树中声明片选 GPIO 并由核心或控制器解析使用。
- 注册控制器:分配并注册 spi_master,使其出现在 SPI 总线上,等待设备匹配与消息调度。
-
设备树或板级描述(实例化从设备)
- 设备树方式(推荐):在 SPI 控制器节点下声明从设备子节点,至少包含 compatible、reg(片选号),并按需设置 spi-max-frequency、spi-cpol、spi-cpha、spi-cs-high、bits-per-word 等属性;内核会据此创建 spi_device 并与驱动匹配。
- 旧式板级方式:在板级代码中填充 spi_board_info 并通过 spi_register_board_info 注册,控制器注册时会扫描并创建对应的 spi_device。
-
设备协议驱动(外设侧)
- 定义 spi_driver,用 of_match_table 匹配设备树 compatible,实现 probe/remove。
- 在 probe 中完成设备私有数据分配、寄存器/参数初始化,并可选择使用 Regmap API 简化寄存器访问(尤其多寄存器外设)。
- 数据通信遵循“消息-片段”模型:构造一个或多个 spi_transfer(设置 tx_buf/rx_buf、len、speed_hz、bits_per_word、delay_usecs、cs_change、tx_nbits/rx_nbits 等),用 spi_message_init + spi_message_add_tail 组装,最后通过 spi_sync(同步)或 spi_async(异步回调)提交;必要时在消息间保持 CS 连续以满足时序。
关键数据结构与传输要点
-
常用结构
- spi_device:包含 bus_num、chip_select、mode(CPOL/CPHA)、bits_per_word、max_speed_hz 等,代表挂接在总线上的具体从设备。
- spi_transfer:一次单向(或半双工)数据片段,支持 tx_buf/rx_buf、len、speed_hz、bits_per_word、delay_usecs、cs_change、tx_nbits/rx_nbits(支持 单线/双线/四线)。
- spi_message:由多个 transfer 组成,片选在消息期间可保持,适合一次事务内的多段时序拼接。
-
传输与性能
- 短小数据可用 PIO(如 spi_sync),大数据或高吞吐建议启用 DMA 与消息队列(线程化)以降低 CPU 占用。
- 多线模式:通过 tx_nbits/rx_nbits 选择 DUAL/QUAD 传输(部分控制器/Flash 支持),显著提升带宽。
最小示例骨架
-
设备树节点(示例)
& ecspi1 { status = "okay"; mydev@1 { compatible = "vendor,my-spi-dev"; reg = < 1> ; /* 片选号 */ spi-max-frequency = < 20000000> ; spi-cpol = < 0> ; spi-cpha = < 0> ; spi-cs-high; bits-per-word = < 8> ; } ; } ; -
驱动骨架(示例)
#include < linux/module.h> #include < linux/spi/spi.h> #include < linux/of.h> static const struct of_device_id mydev_of_match[] = { { .compatible = "vendor,my-spi-dev" } , { } } ; MODULE_DEVICE_TABLE(of, mydev_of_match); static int mydev_probe(struct spi_device *spi) { /* 读取设备树或平台数据配置,如 mode/speed/字长 */ spi-> mode = SPI_MODE_0; /* 示例:CPOL=0, CPHA=0 */ spi-> bits_per_word = 8; spi_setup(spi); /* TODO: 分配私有数据、初始化设备、可能用 regmap */ return 0; } static int mydev_remove(struct spi_device *spi) { /* TODO: 资源释放 */ return 0; } static struct spi_driver mydev_driver = { .driver = { .name = "my-spi-dev", .of_match_table = mydev_of_match, } , .probe = mydev_probe, .remove = mydev_remove, } ; module_spi_driver(mydev_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Minimal SPI device driver example");
调试与常见问题
- 确认控制器与设备绑定:检查 /sys/bus/spi/devices/ 与 /sys/bus/spi/drivers/,以及 dmesg 中 probe 输出。
- 片选与 GPIO:若 cs-gpios 未正确配置,可能导致片选不生效;可在控制器中实现 set_cs 或在设备树显式声明片选 GPIO。
- 时序与速率:核对 spi-cpol/spi-cpha、spi-max-frequency、bits-per-word 是否与器件手册一致;必要时在 transfer 中设置 delay_usecs 满足建立/保持时间。
- 传输模式:只读事务仍需提供 tx_buf(可指向 dummy 缓冲区);全双工下 rx_buf 会收到同时钟移出的数据。
- 性能优化:长传输启用 DMA 与消息队列;多线器件使用 DUAL/QUAD 模式提升吞吐。
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Linux驱动如何支持SPI协议
本文地址: https://pptw.com/jishu/763314.html
