四种SPI模式详解:CPOL与CPHA的组合艺术
开始实战前,咱们先把 SPI 的四种模式彻底捋清楚。我见过不少工程师,拿了片子就写代码,波形不对了才回头查手册。其实搞懂 CPOL 和 CPHA,比背时序图重要得多。
说白了,CPOL 决定了时钟空闲时是高电平还是低电平;CPHA 决定数据是在时钟的上升沿采样,还是下降沿采样。两两组合,就是 Mode0~3。
CPOL(Clock Polarity)—— 时钟极性
- CPOL = 0:空闲时时钟线为低电平
- CPOL = 1:空闲时时钟线为高电平
你想想看,主设备输出时钟,从设备会在每个时钟边沿采样数据。空闲电平不影响传输,但它决定了第一个有效边沿的类型。我记得第一次调一个 MPU9250 模块,默认是 Mode3,我偏用 Mode0配置,结果读出来的陀螺仪数据全是错的。
CPHA(Clock Phase)—— 时钟相位
- CPHA = 0:数据在时钟的第一个边沿采样(上升沿或下降沿取决于 CPOL)
- CPHA = 1:数据在时钟的第二个边沿采样
这里注意:第一个边沿是什么?如果 CPOL=0,时钟从低变高,那么第一个边沿就是上升沿;如果 CPOL=1,时钟从高变低,第一个边沿就是下降沿。所以 CPHA 实际上定义的是“用哪个边沿来采样数据”。
四种模式速查表
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据输出边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低 | 上升沿 | 下降沿 |
| Mode 1 | 0 | 1 | 低 | 下降沿 | 上升沿 |
| Mode 2 | 1 | 0 | 高 | 下降沿 | 上升沿 |
| Mode 3 | 1 | 1 | 高 | 上升沿 | 下降沿 |
看这张表,我建议你把它打印出来贴墙上。前两年带一个新人调 LCD 屏幕,他死活读不到初始化成功的状态,查了一下午发现是从设备要求 Mode2,主机配成了 Mode1。白白浪费时间。
如何快速记忆?
- Mode0:最常用,和 SPI 标准最先规定的时序一致,很多 ADC、DAC、存储芯片默认就是 Mode0
- Mode3:也很常用,尤其是一些 FRAM、EEPROM 偏爱 Mode3,因为时钟空闲是高,可以搭配开漏输出
- Mode1 和 Mode2:少见,但有些专用传感器或自定义协议会用到
核心结论:除非手册明确要求,否则优先选 Mode0 或 Mode3。它们兼容性最好,调起来最省心。
典型应用场景对比
| 器件类型 | 常见模式 | 备注 |
|---|---|---|
| 串行 Flash(W25Q系列) | Mode0 / Mode3 | 大多数支持两种,初始化需要匹配 |
| SD 卡(SPI模式) | Mode0 | 有些卡兼容 Mode3,但推荐 Mode0 |
| MPU9250 / ICM20948 | Mode3 | 这一点容易忽略,我用 STM32 的库默认是 Mode0,必须手动改 |
| MCP3008 ADC | Mode0 | 经典 SPI 接口,直接配 0 就好 |
| MAX31855 热电偶 | Mode1 | 冷端补偿芯片,很多新手在这里翻车 |
| AD7794 高精度 ADC | Mode2 | 工业仪表中常见,注意和主机对齐 |
💡 我的习惯:拿到一个从设备,第一件事就是去手册里搜“SPI Mode”或“CPOL, CPHA”关键词。不要猜,手册说了算。
波形实战:手画四种模式
光看表格还是抽象,我用波形图帮你梳理一下。这张图里,时钟线、数据线上的电平变化一目了然。
代码配置示例(以STM32 HAL库为例)
// 配置 SPI1 为 Mode0
SPI_HandleTypeDef hspi1;
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL = 0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA = 0
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);
// 如果要改为 Mode3,只需改两行:
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; // CPOL = 1
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // CPHA = 1
⚠️ 我曾经踩过的坑:在同一个 SPI 总线上挂了两个从设备,一个用 Mode0,一个用 Mode1。看似只用一根 CS 片选,但时钟空闲电平不一样,切换设备时总线状态混乱,数据全错。后来我老老实实加了个 GPIO 模拟的时钟反相器,或者改用两个独立的 SPI 外设。
如何确定从设备需要哪种模式?
- 去手册查找:通常会有表格或文字描述“SPI Mode 0/3”或“CPOL, CPHA configuration”。
- 看时序图:如果手册给了时序图,观察空闲时钟线是低还是高,采样箭头标在上升还是下降。
- 用示波器实测:把主机配成 Mode0,发一个字节,看从设备是否有响应;没反应就换 Mode3。我有一次调恩智浦的 NFC 模块,手册没写模式,我就是靠这个方法试出来的。
经验法则:大部分串行 Flash、EEPROM、ADC 都支持 Mode0 或 Mode3。如果两者都支持,优先选 Mode0,因为它的时序更直观,示波器上看波形也容易判断。
常见误区
- 误区1:“CPOL=0就是上升沿采样”——不对,还得看 CPHA。CPHA=0才是第一个边沿采样;如果CPHA=1,那就是第二个边沿。
- 误区2:“四种模式可以随意切换”——不行。不匹配的话,数据会移位、丢失,甚至损坏从设备。
- 误区3:“软件模拟 SPI 可以不用关心模式”——错。软件模拟你更得仔细写,因为时钟和数据的时序全靠代码控制,一个延时不对就翻车。
嗯,到这里你应该能分清四种模式了。记住:最关键的不是背,而是学会看手册、看波形。下次调 SPI 设备,先问自己:这个器件要什么模式?我配对了没有?把这个问题刻在脑子里,能少加无数个班。