4. SPI 四种模式详解:Mode0~Mode3 归纳总结
SPI 通信里最让人头疼的,就是时钟极性和相位。说白了就是两个位:CPOL(Clock Polarity) 和 CPHA(Clock Phase)。组合起来正好四种模式,每个模式对应一套严格的时序规则。搞不清它们,设备就连不上——我踩过这个坑,所以这一节咱们彻底讲透。
4.1 CPOL 与 CPHA 到底在控制什么?
- CPOL:时钟空闲时的电平。0 = 空闲低电平,1 = 空闲高电平。
- CPHA:数据采样发生在哪个边沿。0 = 第一个边沿采样,1 = 第二个边沿采样。
你想想看,CPOL 决定了时钟闲下来时是高还是低;CPHA 决定了数据是上升沿读还是下降沿读。这两个组合起来,就对应四种截然不同的时序行为。
快速记忆口诀:
CPOL 看空闲,高1低0;CPHA 看采样,一0二1。
4.2 逐个拆解四种模式
Mode 0(CPOL=0, CPHA=0)—— 最常用的“默认模式”
这是大多数 SPI 设备默认使用的模式。时钟空闲为低电平,数据在时钟的第一个边沿(上升沿)采样,第二个边沿(下降沿)更新。简单、直观、兼容性好。
我记得有一次调试一个温度传感器,手册上说“支持 SPI Mode0”,我直接就配了 Mode0,一次搞定。很多单片机的主机模式默认也是 Mode0,比如 STM32 的 SPI 初始化如果不设置 CPOL/CPHA,就是 Mode0。
// 伪代码:配置 Mode0
SPI_InitTypeDef spi;
spi.CPHA = SPI_CPHA_1Edge; // 第一个边沿采样
spi.CPOL = SPI_CPOL_Low; // 空闲低电平
Mode 1(CPOL=0, CPHA=1)—— 数据在下降沿采样
时钟空闲还是低电平,但采样发生在第二个边沿(下降沿)。这种模式比较少用,但有些老式 EEPROM 或 ADC 会用它。
有一次客户反馈说一片 flash 读写偶尔出错,我抓了波形才发现设备要的是 Mode1,而我配成了 Mode0。时钟空闲电平相同,只是采样移了一个边沿,数据就错位了。嗯,这里要注意,眼睛看不仔细很容易翻车。
// 伪代码:配置 Mode1
spi.CPHA = SPI_CPHA_2Edge; // 第二个边沿采样
spi.CPOL = SPI_CPOL_Low;
Mode 2(CPOL=1, CPHA=0)—— 空闲高电平,上升沿采样
时钟空闲时为高电平,数据在第一个边沿(下降沿)采样。这里要小心:因为时钟空闲高,第一个边沿是下降沿。所以采样发生在下降沿。
我曾经被一个国产加速度计坑过,手册上写了“SCLK idle high, data latch on first edge”,但没写 Mode 编号。我一开始想当然配了 Mode0,结果读数全乱。后来抓波形才意识到是 Mode2。
// 伪代码:配置 Mode2
spi.CPOL = SPI_CPOL_High;
spi.CPHA = SPI_CPHA_1Edge;
Mode 3(CPOL=1, CPHA=1)—— 空闲高电平,下降沿采样
时钟空闲高,第二个边沿采样。这是什么?上升沿更新,下降沿采样。和 Mode0 正好是反相的极性。很多 SD 卡、LCD 驱动芯片默认就是 Mode3。
我个人习惯:如果遇到一个全新的从设备,我会先用逻辑分析仪抓它发的波形,看看空闲时 SCLK 是高还是低,再结合数据在哪个边沿跳变,反推出 Mode。这比翻手册快得多。
// 伪代码:配置 Mode3
spi.CPOL = SPI_CPOL_High;
spi.CPHA = SPI_CPHA_2Edge;
4.3 四种模式关键参数对比
| 模式 | CPOL | CPHA | SCK 空闲电平 | 采样边沿 | 数据更新边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低 | 上升沿 | 下降沿 |
| Mode 1 | 0 | 1 | 低 | 下降沿 | 上升沿 |
| Mode 2 | 1 | 0 | 高 | 下降沿 | 上升沿 |
| Mode 3 | 1 | 1 | 高 | 上升沿 | 下降沿 |
核心总结:
- CPOL 决定空闲电平,CPHA 决定采样边沿顺序。
- Mode0 和 Mode3 采样边沿都是上升沿,但空闲电平相反。
- Mode1 和 Mode2 采样边沿都是下降沿,空闲电平也相反。
4.4 实战中如何识别和切换模式?
用逻辑分析仪抓取 SCLK 和 MOSI 波形,看两个关键点:
- SCLK 空闲时是高还是低 → 确定 CPOL。
- 观察 MOSI 或 MISO 数据变化边沿:数据通常在什么时刻稳定?从设备在哪个边沿采样?
举个例子:如果空闲时是低电平,并且在时钟的第一个边沿(上升沿)数据稳定,那就是 Mode0。如果第一个边沿数据还在变化,要到第二个边沿才稳定,那就是 Mode1。
我曾经踩过的坑:
有一次我需要用 STM32 和一片 AD 芯片通信,手册写“SPI Mode2”。我配置了 CPOL=1, CPHA=0,但数据就是不对。折腾半天发现芯片内部对上电顺序有要求,必须在 CS 拉低前先让 SCLK 拉到高电平,否则芯片默认 CPOL=0。所以不要只看模式编号,还要看启动时序。
4.5 模式选择:主机跟着从机走
这是铁律。主机 SPI 的模式必须和从设备完全一致,否则时序错位,数据全是乱码。实际项目中,优先查阅从设备的数据手册,找到“SPI Timing Characteristics”表格,里面会明确写出所需的 CPOL 和 CPHA,或者直接给出 Mode 编号。
如果手册里只给了时序图,那就按上面的方法反推。我通常会把四种模式的配置宏都写进代码里,用一个 switch 来选,方便调试时快速切换。
// 实用小函数:根据模式号配置
void SPI_SetMode(SPI_HandleTypeDef *hspi, uint8_t mode) {
switch(mode) {
case 0: hspi->Init.CPOL = SPI_POLARITY_LOW;
hspi->Init.CPHA = SPI_PHASE_1EDGE; break;
case 1: hspi->Init.CPOL = SPI_POLARITY_LOW;
hspi->Init.CPHA = SPI_PHASE_2EDGE; break;
case 2: hspi->Init.CPOL = SPI_POLARITY_HIGH;
hspi->Init.CPHA = SPI_PHASE_1EDGE; break;
case 3: hspi->Init.CPOL = SPI_POLARITY_HIGH;
hspi->Init.CPHA = SPI_PHASE_2EDGE; break;
default: break;
}
}
你可能会问:如果主从都支持多种模式,选哪个好?我个人习惯首选 Mode0 或 Mode3,因为它们采样都在上升沿,和大多数数字逻辑的边沿一致,抗干扰相对好一些。Mode1 和 Mode2 采样在下降沿,有时会遇到建立时间不足的问题。
4.6 本节小结
四种模式其实就两个参数,记住:CPOL 看空闲,CPHA 看采样位置。实测中别光信手册上的模式编号,一定用逻辑分析仪确认波形。我建议你把四种模式代码都写好,调试时动态切换,省得反复改代码编译。
另外,如果用了 DMA 或 FIFO,切换模式时注意重新初始化外设,不然配置可能不生效。