2. Linux SPI 子系统架构
SPI(Serial Peripheral Interface)子系统在 Linux 内核中采用分层设计,将协议细节、硬件控制与设备逻辑解耦。理解这一分层架构是开发车载 SPI 驱动与调优传感器通信参数的基础。本章从核心层、控制器驱动层、设备驱动层以及消息队列与传输机制四个维度展开。
2.1 SPI 核心层(SPI Core)
SPI Core 位于 drivers/spi/spi.c,是 SPI 子系统的中枢。它向上提供统一的设备驱动注册接口,向下抽象控制器硬件差异,并管理总线、消息传递与同步。
| 核心职责 | 说明 |
|---|---|
| 总线注册与管理 | 维护 SPI 总线类型 spi_bus_type,管理总线上的设备与驱动匹配 |
| 设备与驱动模型 | 提供 spi_register_driver() / spi_alloc_device() 等接口 |
| 消息队列调度 | 将 spi_message 排队并提交给控制器驱动执行 |
| 传输参数校验 | 检查 bits_per_word、speed_hz、mode 等参数合法性 |
| DMA 与 PIO 选择 | 根据传输长度与控制器能力自动选择传输方式 |
关键数据结构:
struct spi_controller 是核心层与控制器驱动之间的桥梁,包含 transfer_one_message、setup、prepare_message 等回调函数指针。
2.2 SPI 控制器驱动层
控制器驱动层直接操作 SPI 控制器的硬件寄存器,实现时钟极性/相位配置、片选控制、FIFO 管理以及中断/DMA 传输。在车载场景中,常见的控制器包括 NXP i.MX8 的 ecspi、TI Jacinto 的 mcspi 以及 Renesas R-Car 的 msiof。
- 初始化流程:在 probe 中分配
struct spi_controller,设置 transfer_one、setup、prepare_message 等回调,调用spi_register_controller()注册到核心层。 - 时钟配置:通过
setup()回调根据设备树中的spi-max-frequency和spi-cpol/spi-cpha配置寄存器。 - 传输执行:
transfer_one()负责单次 SPI 传输的硬件启动,通常基于 FIFO 或 DMA 完成。 - 中断处理:完成中断或错误中断中调用
spi_finalize_current_transfer()通知核心层。
车载调优要点:控制器驱动的 FIFO 阈值设置直接影响中断频率与 CPU 负载。对于高速传感器(如 10 MHz 以上的 IMU),建议将 FIFO 阈值设为 8 或 16 字节以减少中断次数。
2.3 SPI 设备驱动层
设备驱动层位于 SPI 子系统的最上层,直接与传感器或外设芯片交互。开发者只需关注协议解析与数据转换,无需关心 SPI 控制器的硬件细节。
// 设备驱动模板示例
static const struct spi_device_id sensor_ids[] = {
{ "bmi088", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, sensor_ids);
static int sensor_probe(struct spi_device *spi)
{
// 设置 SPI 模式、最大频率、字长
spi->mode = SPI_MODE_3;
spi->max_speed_hz = 10 * 1000 * 1000; // 10 MHz
spi->bits_per_word = 8;
spi_setup(spi);
// 注册 IIO 或 input 子系统设备
// ...
return 0;
}
static struct spi_driver sensor_driver = {
.driver = {
.name = "bmi088",
.of_match_table = of_match_ptr(sensor_of_match),
},
.probe = sensor_probe,
.id_table = sensor_ids,
};
module_spi_driver(sensor_driver);
- 设备树匹配:通过 compatible 字符串与设备树节点绑定,自动获取片选号、时钟频率等参数。
- 数据传输:使用
spi_write()、spi_read()或spi_sync_transfer()发起同步传输。 - 异步传输:对于需要高吞吐的传感器(如激光雷达点云数据),使用
spi_async()配合 completion 或 callback 实现非阻塞通信。
2.4 SPI 消息队列与传输机制
SPI 消息队列是核心层与控制器驱动之间的传输载体。每个 spi_message 包含一个或多个 spi_transfer 段,支持连续片选下的多段传输。
2.4.1 消息队列调度流程
- 消息提交:设备驱动调用
spi_sync()或spi_async(),核心层将spi_message加入控制器的queue链表。 - 队列处理:核心层的
spi_pump_messages()工作队列从队列头部取出消息,调用控制器驱动的transfer_one_message()。 - 分段传输:控制器驱动遍历消息中的
spi_transfer链表,逐段启动硬件传输。每段可独立配置速度、延迟和字长。 - 完成通知:每段传输完成后,控制器驱动调用
spi_finalize_current_transfer();整个消息完成后,执行 complete 回调并唤醒等待队列。
车载实时性风险:如果多个传感器共享同一条 SPI 总线,长消息(如 > 256 字节)会阻塞后续高优先级传感器的传输。建议为实时传感器(如安全气囊加速度计)使用独立的 SPI 控制器或片选,或通过
spi_async() 配合 RT 线程避免优先级反转。
2.4.2 传输参数对比
| 参数 | 同步传输 (spi_sync) | 异步传输 (spi_async) |
|---|---|---|
| 阻塞行为 | 调用线程休眠直到传输完成 | 立即返回,通过回调通知完成 |
| 适用场景 | 低速传感器、配置寄存器读写 | 高速数据流、多传感器并发 |
| 上下文要求 | 可在进程上下文使用 | 可在中断/软中断上下文使用 |
| 延迟抖动 | 受调度延迟影响较大 | 可通过 RT 线程降低抖动 |
| 典型车载用例 | 温度传感器轮询、EEPROM 读写 | IMU 数据流、激光雷达点云 |