3. SPI 总线协议详解:物理层与时序核心

SPI(Serial Peripheral Interface,串行外设接口)是一种由 Motorola 公司提出的全双工、同步、主从式串行通信总线。本章深入剖析 SPI 的物理层信号定义、四线制工作机制、时钟极性(CPOL)与相位(CPHA)的配置逻辑,以及由此衍生的四种数据传输模式。理解这些底层细节是后续驱动开发和传感器适配的基础。

3.1 SPI 物理层与四线制信号

SPI 总线在物理层上通过四条信号线实现主设备(Master)与从设备(Slave)之间的数据交换。与 I2C 的开漏极架构不同,SPI 使用推挽输出,因此支持更高的传输速率(通常可达数十 MHz)。

信号线 全称 方向(主→从) 功能描述
SCLK Serial Clock 主 → 从 由主设备产生的同步时钟,决定数据传输速率
MOSI Master Out Slave In 主 → 从 主设备发送数据至从设备的数据线
MISO Master In Slave Out 从 → 主 从设备发送数据至主设备的数据线
CS Chip Select 主 → 从 片选信号,低电平有效,用于选中特定的从设备
关键概念:SPI 是全双工通信。在每个时钟周期内,主设备通过 MOSI 发送一位数据,同时从设备通过 MISO 发送一位数据。这意味着一次时钟脉冲即可完成一次双向数据交换。

下图展示了 SPI 四线制的基本连接拓扑。主设备通过独立的 CS 线分别控制多个从设备,而 SCLK、MOSI、MISO 则共享连接。

SPI 四线制主从连接拓扑 主设备 (Master) SCLK 发生器 从设备 1 (Slave) 从设备 2 (Slave) SCLK MOSI MISO CS1 / CS2 连接要点 • SCLK/MOSI/MISO 共享 • CS 线独立,低电平选中 • 全双工同时收发 • 无应答机制,硬件简单 • 速率由主设备 SCLK 决定

3.2 时钟极性(CPOL)与相位(CPHA)

SPI 协议的核心灵活性体现在时钟极性(CPOL)和时钟相位(CPHA)的配置上。这两个参数决定了数据采样和切换的时序边界。

  • CPOL(Clock Polarity):定义 SCLK 在空闲状态(无数据传输时)的电平。
    • CPOL = 0:空闲时 SCLK 为低电平。
    • CPOL = 1:空闲时 SCLK 为高电平。
  • CPHA(Clock Phase):定义数据采样发生在 SCLK 的哪个边沿。
    • CPHA = 0:在第一个时钟边沿(上升沿或下降沿,取决于 CPOL)采样数据。
    • CPHA = 1:在第二个时钟边沿采样数据。
驱动开发提示:CPOL 和 CPHA 必须与从设备的数据手册严格匹配。配置错误将导致数据移位或采样错误,表现为读取到的传感器数据全为 0xFF 或 0x00。

3.3 四种 SPI 数据传输模式

根据 CPOL 和 CPHA 的组合,SPI 定义了四种标准传输模式(Mode 0 ~ Mode 3)。这是嵌入式工程师必须熟记的配置表。

模式 CPOL CPHA 空闲时钟电平 数据采样边沿 数据输出边沿
Mode 0 0 0 低电平 上升沿 下降沿
Mode 1 0 1 低电平 下降沿 上升沿
Mode 2 1 0 高电平 下降沿 上升沿
Mode 3 1 1 高电平 上升沿 下降沿

下图以 Mode 0 和 Mode 3 为例,展示了 SCLK、MOSI 和 MISO 的时序关系。注意数据在采样边沿必须保持稳定。

SPI Mode 0 与 Mode 3 时序对比 Mode 0 (CPOL=0, CPHA=0) SCLK MOSI D0 D1 D2 D3 采样 Mode 3 (CPOL=1, CPHA=1) SCLK MOSI D0 D1 D2 D3 采样 时序规则总结 1. Mode 0:空闲低电平,上升沿采样 2. Mode 1:空闲低电平,下降沿采样 3. Mode 2:空闲高电平,下降沿采样 4. Mode 3:空闲高电平,上升沿采样 数据输出边沿与采样边沿相反 数据在采样边沿必须稳定 CS 低电平期间传输有效 传输结束后 CS 拉高

3.4 数据传输流程与代码示例

一次完整的 SPI 数据传输遵循以下步骤:

  1. 片选使能:主设备将对应从设备的 CS 引脚拉低,选中从设备。
  2. 时钟启动:主设备开始产生 SCLK 时钟信号。
  3. 数据移位:在每个时钟周期,主设备通过 MOSI 发送一位数据,同时从设备通过 MISO 发送一位数据。数据通常以 MSB(最高有效位)先行。
  4. 传输完成:发送完指定字节数后,主设备停止 SCLK,并将 CS 拉高,释放从设备。

以下是一个 Linux 内核 SPI 驱动中配置模式和传输数据的典型代码片段:

#include <linux/spi/spi.h>

/* 配置 SPI 设备 */
static int sensor_spi_setup(struct spi_device *spi)
{
    int ret;

    /* 设置 SPI 模式:Mode 0 (CPOL=0, CPHA=0) */
    spi->mode = SPI_MODE_0;

    /* 设置最大时钟频率:1 MHz */
    spi->max_speed_hz = 1000000;

    /* 设置每字位数:8 位 */
    spi->bits_per_word = 8;

    ret = spi_setup(spi);
    if (ret < 0) {
        pr_err("SPI setup failed\n");
        return ret;
    }

    return 0;
}

/* 发送和接收数据 */
static int sensor_spi_transfer(struct spi_device *spi,
                               u8 *tx_buf, u8 *rx_buf, size_t len)
{
    struct spi_transfer t = {
        .tx_buf = tx_buf,
        .rx_buf = rx_buf,
        .len    = len,
        .speed_hz = 1000000,
        .bits_per_word = 8,
    };
    struct spi_message m;

    spi_message_init(&m);
    spi_message_add_tail(&t, &m);

    return spi_sync(spi, &m);
}
常见错误:在配置 SPI 时,如果 spi->mode 设置错误(例如传感器要求 Mode 1 但配置为 Mode 0),会导致数据采样边沿错位。此时应使用逻辑分析仪抓取 SCLK 和 MOSI/MISO 波形,对比数据手册确认时序。

3.5 本章小结