1. SPI协议基础:时钟极性(CPOL)和时钟相位(CPHA)的定义与作用
各位做嵌入式开发的朋友,想必都跟SPI打过交道。SPI看似简单,就四根线,但调试起来经常让人抓狂。我碰到过好几次——设备明明连接正确,可数据就是错位。最后发现,问题出在时钟极性(CPOL)和时钟相位(CPHA)上。这两个参数,说白了就是定义什么时候采样数据。搞懂它们,SPI调试能少一半的坑。
1.1 SPI基础回顾
SPI是全双工同步通信。主机控制时钟SCK。数据线MOSI(主机输出从机输入)和MISO(主机输入从机输出)。片选CS用于选择从机。每个时钟周期传输一位。但时钟的“有效边沿”由谁决定?嗯,就是CPOL和CPHA。
1.2 时钟极性 CPOL
CPOL决定SCK在空闲时的电平。你想想看:如果CPOL=0,空闲时SCK为低电平;CPOL=1,空闲时SCK为高电平。这不难理解。
1.3 时钟相位 CPHA
CPHA决定数据是在时钟的第一个边沿采样,还是第二个边沿采样。CPHA=0,第一个边沿采样;CPHA=1,第二个边沿采样。结合CPOL,每个时钟周期就有两个可能的采样边沿。所以一共四种组合。
1.4 四种模式
我们把CPOL和CPHA组合起来,就是标准的SPI模式。
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 |
|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低 | 第一个边沿(上升沿) |
| Mode 1 | 0 | 1 | 低 | 第二个边沿(下降沿) |
| Mode 2 | 1 | 0 | 高 | 第一个边沿(下降沿) |
| Mode 3 | 1 | 1 | 高 | 第二个边沿(上升沿) |
1.5 为什么CPOL和CPHA如此重要?
假设主机设置了Mode0,从机要求Mode3。这时时钟空闲电平不同,采样边沿也差半个周期。数据会在错误时刻被锁存。我碰过一块温度传感器,规格书上写的是Mode0,但生产批次不一样,实际跑起来必须用Mode3才能读到正确温度。折腾了一下午才发现是CPHA反了。
再举个例子:高速SPI(几十MHz)时,如果从机的建立保持时间不够,稍微错一点边沿,数据就全乱。所以调SPI的第一步,就是确认主从机的CPOL和CPHA是否匹配。
1.6 理解CPOL和CPHA的数据锁存逻辑
我用一张图来记忆。看下面这张“SPI模式选择树”,你从CPOL选边,再从CPHA选边,就能定位到具体模式。
从上图你能快速找到对应模式。我个人习惯在代码里写一个初始化函数,把CPOL和CPHA作为参数传入,这样换从机时只需改宏定义。
// SPI初始化示例(STM32 HAL库风格)
void SPI_Init(SPI_HandleTypeDef *hspi, uint8_t mode)
{
hspi->Init.CLKPolarity = (mode & 0x02) ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW;
hspi->Init.CLKPhase = (mode & 0x01) ? SPI_PHASE_2EDGE : SPI_PHASE_1EDGE;
HAL_SPI_Init(hspi);
}
1.7 本章小结
CPOL和CPHA是SPI通信的基石。你只需要记住:CPOL定空闲电平,CPHA定采样边沿,两者组合就是四种模式。调试时先确认主从机的模式是否一致。我早期踩过的坑,百分之八十都是从这里开始的。希望这一章能帮你打好基础。