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 通信的基本流程如下:

  1. 主设备将目标从设备的 CS 引脚拉低,激活从设备。
  2. 主设备在 SCLK 线上产生连续的时钟脉冲。
  3. 在每个时钟周期内,主设备通过 MOSI 线发送 1 bit 数据,同时从设备通过 MISO 线发送 1 bit 数据。
  4. 数据传输完成后,主设备将 CS 引脚拉高,释放从设备。
SPI 四线制连接拓扑 主设备 (Master) SCLK / MOSI / MISO 从设备 (Slave) SCLK / MOSI / MISO SCLK MOSI MISO CS (低电平有效) SPI 总线特点:同步时钟驱动、全双工同时收发、无应答机制 CS 线独立选择从设备,适合短距离高速通信(典型速率 1~50 MHz)

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:在第二个时钟边沿(下降沿或上升沿)采样数据。
SPI 四种模式时序对比(Mode 0 ~ Mode 3) Mode 0 (CPOL=0, CPHA=0) SCLK 采样↑ 输出↓ Mode 1 (CPOL=0, CPHA=1) SCLK 采样↓ 输出↑ Mode 2 (CPOL=1, CPHA=0) SCLK 采样↓ 输出↑

1.3 全双工通信模式解析

SPI 的核心优势在于其全双工(Full-Duplex)通信能力:在每个时钟周期内,主设备通过 MOSI 发送数据的同时,从设备通过 MISO 发送数据,两者互不干扰。这种机制使得 SPI 在同等时钟频率下,有效数据吞吐量是 I²C 或 UART 的两倍。

全双工 vs 半双工对比:
  • 全双工(SPI):发送和接收同时进行,无需切换方向,硬件设计简单。
  • 半双工(I²C、单线 UART):发送和接收分时进行,需要方向切换,协议开销较大。

全双工通信的数据流模型如下:

SPI 全双工数据流模型 主设备发送缓冲区 Tx Buffer 主设备接收缓冲区 Rx Buffer 从设备发送缓冲区 Tx Buffer 从设备接收缓冲区 Rx Buffer MOSI: 主→从 MISO: 从→主 每个时钟周期同时完成 1 bit 发送和 1 bit 接收,有效数据率 = 时钟频率 × 2

在 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 以产生时钟。