2. SPI 传输模式:模式 0/1/2/3 的时序图对比、全双工与半双工通信、单主多从拓扑结构

SPI(Serial Peripheral Interface)总线协议的核心在于其灵活的时钟极性和相位配置,这定义了四种标准传输模式。理解这些模式的区别、通信方式以及拓扑结构,是编写正确且高效 SPI 驱动的基础。本章将深入剖析这些概念,并通过可视化图表帮助建立直观认知。

2.1 SPI 模式 0/1/2/3 时序对比

SPI 模式由时钟极性(CPOL)和时钟相位(CPHA)两个参数决定:

  • CPOL:决定 SCK 时钟信号在空闲状态时的电平。CPOL=0 表示空闲低电平,CPOL=1 表示空闲高电平。
  • CPHA:决定数据采样(锁存)发生在 SCK 时钟的哪个边沿。CPHA=0 表示在第一个边沿采样,CPHA=1 表示在第二个边沿采样。

下表总结了四种标准模式的配置关系:

SPI 模式 CPOL CPHA 空闲时钟电平 数据采样边沿 数据输出边沿
模式 0 0 0 低电平 上升沿(第一个边沿) 下降沿
模式 1 0 1 低电平 下降沿(第二个边沿) 上升沿
模式 2 1 0 高电平 下降沿(第一个边沿) 上升沿
模式 3 1 1 高电平 上升沿(第二个边沿) 下降沿
实践要点: 绝大多数 SPI 从设备(如 Flash 存储器、传感器)默认工作在模式 0 或模式 3。模式 0 和模式 3 的采样边沿都是上升沿,区别仅在于空闲时钟电平。在驱动开发中,务必查阅从设备数据手册确认其支持的 SPI 模式,错误的模式配置将导致通信数据完全错位。

下面通过内联 SVG 时序图直观展示四种模式的波形差异:

SPI 四种模式时序对比图(CPOL/CPHA) 模式 0 (CPOL=0, CPHA=0) SCK MOSI 采样点 模式 1 (CPOL=0, CPHA=1) SCK 采样点 模式 2 (CPOL=1, CPHA=0) SCK 采样点 模式 3 (CPOL=1, CPHA=1) SCK 采样点 SCK 时钟边沿 数据采样点 MOSI 数据线

2.2 全双工与半双工通信

SPI 总线在物理层上天然支持全双工通信,但在实际应用中,根据从设备能力和数据传输需求,可以选择不同的通信模式。

2.2.1 全双工模式

标准 SPI 通信是全双工的。主设备在发送数据(MOSI)的同时,从设备也在发送数据(MISO)。这意味着每个时钟周期内,数据在两个方向上同时传输。这种模式适用于需要高吞吐量的场景,如音频数据流、高速 ADC 采集等。

2.2.2 半双工模式

在某些情况下,从设备可能只支持单向通信(如只读传感器),或者为了节省引脚,将 MOSI 和 MISO 合并为一条双向数据线(3 线制 SPI)。此时通信退化为半双工模式,数据在同一根线上分时传输。

驱动开发注意: 在 Linux 内核 SPI 子系统中,全双工传输是默认行为。如果需要实现半双工或 3 线制 SPI,需要在设备树中设置 spi-3wire 标志,并在驱动中正确配置 SPI 控制器的模式寄存器。

下面通过 SVG 图对比全双工与半双工的数据流向:

全双工 vs 半双工通信架构 主设备 从设备 MOSI (主→从) MISO (从→主) 全双工:同时双向传输 主设备 从设备 DATA (分时双向) 半双工:分时单向传输

2.3 单主多从拓扑结构

SPI 总线采用主从架构,一个主设备可以连接多个从设备。常见的拓扑结构有两种:独立片选和菊花链。

2.3.1 独立片选拓扑

这是最常用的方式。主设备为每个从设备分配一个独立的片选信号(CS/SS)。当主设备需要与某个从设备通信时,将该从设备的 CS 线拉低,其他从设备的 CS 保持高电平(高阻态)。所有从设备共享 SCK、MOSI 和 MISO 信号线。

2.3.2 菊花链拓扑

在菊花链中,所有从设备共享一个片选信号。数据从主设备发出,经过第一个从设备处理后,再传递给下一个从设备,依次类推。这种拓扑减少了主设备的引脚需求,但增加了通信延迟和复杂性,通常用于移位寄存器链或某些特定的 ADC 芯片组。

信号完整性警告: 在独立片选拓扑中,当总线上挂载多个从设备时,MISO 线是“线与”关系。所有从设备的 MISO 输出必须为三态(高阻),否则多个从设备同时驱动 MISO 会导致总线冲突,损坏设备。确保从设备在 CS 为高时 MISO 输出为高阻态。

下面通过 SVG 图展示单主多从的两种拓扑结构:

单主多从拓扑结构 独立片选拓扑 主设备 (Master) 从设备 1 从设备 2 从设备 3 从设备 4 SCK, MOSI, MISO CS1 CS2 CS3 CS4 菊花链拓扑 主设备 从设备 1 从设备 2 从设备 3 DO→DI DO→DI DO→DI 共享 CS 和 SCK

2.4 驱动配置示例

在 Linux 内核中,配置 SPI 模式通过设置 spi_device 结构体的 mode 字段实现。以下代码片段展示了如何在设备驱动中设置模式 0 和全双工通信:

#include <linux/spi/spi.h>

static int my_spi_probe(struct spi_device *spi)
{
    int ret;

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

    /* 设置最大时钟频率为 10 MHz */
    spi->max_speed_hz = 10 * 1000 * 1000;

    /* 设置每个字的位数为 8 位 */
    spi->bits_per_word = 8;

    /* 应用配置 */
    ret = spi_setup(spi);
    if (ret < 0) {
        dev_err(&spi->dev, "spi_setup failed: %d\n", ret);
        return ret;
    }

    /* 执行全双工传输 */
    struct spi_transfer t = {
        .tx_buf = tx_buffer,
        .rx_buf = rx_buffer,
        .len = 32,
    };
    struct spi_message m;
    spi_message_init(&m);
    spi_message_add_tail(&t, &m);
    ret = spi_sync(spi, &m);

    return 0;
}
调试技巧: 使用逻辑分析仪或示波器抓取 SCK、MOSI、MISO 和 CS 信号,是验证 SPI 模式配置是否正确的最直接方法。观察 SCK 空闲电平(CPOL)和数据变化相对于时钟边沿的位置(CPHA),可以快速定位模式不匹配问题。