SPI驱动开发从FIFO模式到DMA传输性能调优 - 第1节
1.1 SPI总线四线制工作原理
SPI(Serial Peripheral Interface,串行外设接口)是一种同步、全双工、主从架构的串行通信总线。其物理层由四条信号线构成,分别承担时钟、数据输出、数据输入和片选功能。
| 信号线 | 全称 | 方向(主设备视角) | 功能描述 |
|---|---|---|---|
| SCLK | Serial Clock | 输出 | 由主设备产生,同步所有数据传输 |
| MOSI | Master Out Slave In | 输出 | 主设备发送数据至从设备 |
| MISO | Master In Slave Out | 输入 | 从设备发送数据至主设备 |
| CS | Chip Select | 输出 | 主设备选择并激活目标从设备(低电平有效) |
关键概念:SPI 总线没有固定的设备地址寻址机制,通过独立的 CS 线实现从设备选择。每增加一个从设备,主设备就需要增加一个 CS 引脚。
SPI 通信的基本流程如下:
- 主设备将目标从设备的 CS 引脚拉低,激活从设备。
- 主设备在 SCLK 线上产生连续的时钟脉冲。
- 在每个时钟周期内,主设备通过 MOSI 线发送 1 bit 数据,同时从设备通过 MISO 线发送 1 bit 数据。
- 数据传输完成后,主设备将 CS 引脚拉高,释放从设备。
1.2 时钟极性与相位(CPOL、CPHA)配置
SPI 协议通过 CPOL(Clock Polarity,时钟极性) 和 CPHA(Clock Phase,时钟相位) 两个参数定义数据采样与输出的时钟边沿。这两个参数组合成四种工作模式(Mode 0 ~ Mode 3)。
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据输出边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低电平 | 上升沿 | 下降沿 |
| Mode 1 | 0 | 1 | 低电平 | 下降沿 | 上升沿 |
| Mode 2 | 1 | 0 | 高电平 | 下降沿 | 上升沿 |
| Mode 3 | 1 | 1 | 高电平 | 上升沿 | 下降沿 |
驱动开发避坑:主设备和从设备的 CPOL 和 CPHA 必须完全一致,否则会导致数据采样错位,通信失败。在 Linux 内核 SPI 设备树中,通过
spi-cpol 和 spi-cpha 属性进行配置。
CPOL 决定 SCLK 空闲时的电平状态:
- CPOL = 0:空闲时 SCLK 为低电平,第一个时钟边沿为上升沿。
- CPOL = 1:空闲时 SCLK 为高电平,第一个时钟边沿为下降沿。
CPHA 决定数据采样发生在哪个时钟边沿:
- CPHA = 0:在第一个时钟边沿(上升沿或下降沿)采样数据。
- CPHA = 1:在第二个时钟边沿(下降沿或上升沿)采样数据。
1.3 全双工通信模式解析
SPI 的核心优势在于其全双工(Full-Duplex)通信能力:在每个时钟周期内,主设备通过 MOSI 发送数据的同时,从设备通过 MISO 发送数据,两者互不干扰。这种机制使得 SPI 在同等时钟频率下,有效数据吞吐量是 I²C 或 UART 的两倍。
全双工 vs 半双工对比:
- 全双工(SPI):发送和接收同时进行,无需切换方向,硬件设计简单。
- 半双工(I²C、单线 UART):发送和接收分时进行,需要方向切换,协议开销较大。
全双工通信的数据流模型如下:
在 Linux 内核 SPI 驱动中,全双工通信通过 spi_transfer 结构体实现:
struct spi_transfer {
const void *tx_buf; // 发送数据缓冲区
void *rx_buf; // 接收数据缓冲区
unsigned len; // 传输字节数
unsigned speed_hz; // 时钟频率
unsigned cs_change; // 传输完成后是否切换 CS
// ... 其他成员
};
驱动开发要点:当
tx_buf 和 rx_buf 同时非空时,SPI 控制器执行全双工传输。如果只需要发送(如写寄存器),可以将 rx_buf 设为 NULL;如果只需要接收(如读数据),可以将 tx_buf 设为 NULL,此时主设备会自动发送 0x00 以产生时钟。