2. SPI 模式详解:CPOL 与 CPHA 的四种组合模式(Mode0-3)及其时序波形图分析
SPI 通信协议的核心灵活性来源于时钟极性(CPOL)和时钟相位(CPHA)的配置组合。这两个参数决定了主设备与从设备之间数据采样和移位的精确时序关系。理解这四种模式是进行 SPI 驱动开发、硬件调试以及解决通信兼容性问题的前提。
2.1 CPOL 与 CPHA 的定义
在深入波形图之前,必须明确 CPOL 和 CPHA 各自控制的具体时序要素:
| 参数 | 全称 | 控制对象 | 逻辑值含义 |
|---|---|---|---|
| CPOL | Clock Polarity | SCLK 空闲状态的电平 | 0:空闲为低电平(Low Idle) 1:空闲为高电平(High Idle) |
| CPHA | Clock Phase | 数据采样发生在 SCLK 的哪个边沿 | 0:在第一个边沿采样(数据移出在第二个边沿) 1:在第二个边沿采样(数据移出在第一个边沿) |
关键记忆法: CPOL 决定“时钟睡着时”的电平;CPHA 决定“数据是在时钟跳变时被读取还是被写入”。
2.2 四种模式(Mode 0 ~ Mode 3)速查表
通过组合 CPOL 和 CPHA 的 0/1 状态,可以得到 SPI 的四种标准工作模式。绝大多数 SPI 从设备芯片的数据手册都会明确标注其支持的 SPI Mode。
| SPI Mode | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据移出边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低电平 | 上升沿(第一个边沿) | 下降沿(第二个边沿) |
| Mode 1 | 0 | 1 | 低电平 | 下降沿(第二个边沿) | 上升沿(第一个边沿) |
| Mode 2 | 1 | 0 | 高电平 | 下降沿(第一个边沿) | 上升沿(第二个边沿) |
| Mode 3 | 1 | 1 | 高电平 | 上升沿(第二个边沿) | 下降沿(第一个边沿) |
工程经验: 绝大多数 SPI Flash 和传感器默认支持 Mode 0 和 Mode 3。这两种模式的数据采样边沿都是上升沿,区别仅在于空闲电平。在驱动开发中,如果通信不稳定,首先应检查主从设备的 SPI Mode 是否匹配。
2.3 四种模式的时序波形图分析
以下 SVG 时序图展示了在传输一个字节(0x52,二进制 0101 0010)时,四种模式下的 SCLK、MOSI 和 MISO 信号波形。图中红色虚线箭头表示数据采样点,蓝色实线箭头表示数据移位点。
2.4 模式选择对驱动开发的影响
在 Linux SPI 驱动中,模式配置直接影响数据传输的稳定性和性能。以下是几个关键影响点:
- 数据建立/保持时间: 不同的模式导致数据相对于时钟边沿的建立时间和保持时间不同。例如,Mode 0 在上升沿采样,数据在上升沿之前必须稳定;而 Mode 1 在下降沿采样,数据在下降沿之前必须稳定。如果从设备对建立时间要求苛刻,选错模式可能导致数据采样错误。
- 片选(CS)时序配合: 某些从设备要求在 CS 有效后、第一个时钟边沿之前,数据线必须处于特定状态。CPHA=0 的模式(Mode 0 和 Mode 2)在第一个边沿采样,因此 CS 到第一个时钟的延迟(tCSSU)尤为重要。
- 最高时钟频率: 理论上,四种模式都可以达到 SPI 总线支持的最高频率。但在实际 PCB 布局中,信号反射和串扰可能导致某些模式在高速下更稳定。通常 Mode 0 和 Mode 3 因为采样边沿是上升沿(信号更干净),在高速场景下更受青睐。
常见 Bug 案例: 某项目使用 SPI 驱动 LCD 屏幕,初始化后屏幕显示花屏。排查发现主控默认配置为 Mode 0,而 LCD 控制器数据手册要求 Mode 3。两者 CPOL 不同(空闲电平相反),导致数据在错误的边沿被采样,最终数据错位。将 SPI 模式改为 Mode 3 后问题解决。
2.5 在 Linux 内核中配置 SPI Mode
在 Linux 设备树或板级代码中,通过设置 spi->mode 字段来指定工作模式。以下是一个典型的设备树配置示例:
/* 设备树示例:配置 SPI 从设备工作在 Mode 3 */
&spi1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins>;
spidev@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <10000000>; /* 10 MHz */
spi-cpol; /* CPOL = 1 */
spi-cpha; /* CPHA = 1 */
/* 上述两个属性组合即为 Mode 3 */
};
};
在驱动代码中,也可以通过 API 动态设置:
/* 内核驱动代码中设置 SPI Mode 示例 */
struct spi_device *spi = /* 获取到的 spi_device 指针 */;
/* 设置为 Mode 0: CPOL=0, CPHA=0 */
spi->mode = SPI_MODE_0;
/* 设置为 Mode 3: CPOL=1, CPHA=1 */
spi->mode = SPI_MODE_3;
/* 注意:如果同时设置了 SPI_CS_HIGH 等标志,需要使用位或操作 */
spi->mode = SPI_MODE_3 | SPI_CS_HIGH;
/* 设置完成后,需要重新配置硬件 */
spi_setup(spi);
调试技巧: 使用逻辑分析仪抓取 SCLK 和 MOSI/MISO 波形,对比数据手册中的时序图。如果发现数据在错误的边沿被采样(例如,数据变化沿与采样沿重合),立即检查 CPOL 和 CPHA 的配置是否与从设备要求一致。
2.6 四种模式对比总结
| 对比维度 | Mode 0 | Mode 1 | Mode 2 | Mode 3 |
|---|---|---|---|---|
| CPOL | 0 | 0 | 1 | 1 |
| CPHA | 0 | 1 | 0 | 1 |
| 空闲电平 | 低 | 低 | 高 | 高 |
| 采样边沿 | 上升沿 | 下降沿 | 下降沿 | 上升沿 |
| 移出边沿 | 下降沿 | 上升沿 | 上升沿 | 下降沿 |
| 常见应用 | 绝大多数 SPI Flash、SD 卡、传感器 | 部分 TI 芯片、特定 ADC | 部分 NXP 芯片、特定 DAC | 与 Mode 0 同样广泛,常用于高速场景 |
| 驱动配置宏 | SPI_MODE_0 |
SPI_MODE_1 |
SPI_MODE_2 |
SPI_MODE_3 |