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 段,支持连续片选下的多段传输。

SPI 消息队列与传输流程 设备驱动 SPI 核心层 控制器驱动 spi_sync/async transfer_one_message 消息队列 (FIFO) struct spi_message • transfers 链表头 • complete 回调函数 • status 传输状态 struct spi_transfer • tx_buf / rx_buf • len (字节数) • speed_hz / delay_usecs SPI 硬件总线 完成回调 / 唤醒 唤醒等待队列

2.4.1 消息队列调度流程

  1. 消息提交:设备驱动调用 spi_sync() 或 spi_async(),核心层将 spi_message 加入控制器的 queue 链表。
  2. 队列处理:核心层的 spi_pump_messages() 工作队列从队列头部取出消息,调用控制器驱动的 transfer_one_message()。
  3. 分段传输:控制器驱动遍历消息中的 spi_transfer 链表,逐段启动硬件传输。每段可独立配置速度、延迟和字长。
  4. 完成通知:每段传输完成后,控制器驱动调用 spi_finalize_current_transfer();整个消息完成后,执行 complete 回调并唤醒等待队列。
车载实时性风险:如果多个传感器共享同一条 SPI 总线,长消息(如 > 256 字节)会阻塞后续高优先级传感器的传输。建议为实时传感器(如安全气囊加速度计)使用独立的 SPI 控制器或片选,或通过 spi_async() 配合 RT 线程避免优先级反转。

2.4.2 传输参数对比

参数 同步传输 (spi_sync) 异步传输 (spi_async)
阻塞行为 调用线程休眠直到传输完成 立即返回,通过回调通知完成
适用场景 低速传感器、配置寄存器读写 高速数据流、多传感器并发
上下文要求 可在进程上下文使用 可在中断/软中断上下文使用
延迟抖动 受调度延迟影响较大 可通过 RT 线程降低抖动
典型车载用例 温度传感器轮询、EEPROM 读写 IMU 数据流、激光雷达点云

2.5 分层架构总结