3. 时钟极性(CPOL)和时钟相位(CPHA)的数学定义归纳总结
SPI 通信中,两个设备能正常对话的前提是什么?说白了,就是它们对“什么时候采样数据”以及“空闲时时钟是什么样”这两个问题的答案完全一致。这两个问题分别对应时钟极性(CPOL)和时钟相位(CPHA)。我从数学上给它俩一个干净的定义,这样调试时就不用靠猜了。
3.1 什么是时钟极性 (CPOL)?——电平基准的数学约定
CPOL 决定的是 SCK 在空闲状态下的电平。数学上可以看作一个偏置量:
- CPOL = 0:空闲时 SCK 为低电平(0 V)。第一个有效边沿是上升沿;
- CPOL = 1:空闲时 SCK 为高电平(VDD)。第一个有效边沿是下降沿。
我习惯把 CPOL 理解为“时钟的初始状态”。你想想看,如果两个设备的 CPOL 不一致,从设备会把高电平当成刚上电的静态电平,时钟唤醒那一刻的跳变方向就反了。我曾经在一个 NFC 模块上遇到过这种问题,示波器一看,主设备时钟空闲低,从设备却等一个下降沿作为开始,结果第一个字节全是乱的。
3.2 什么是时钟相位 (CPHA)?——采样时刻的数学约定
CPHA 规定的是 数据采样发生在哪个边沿。数学上说,它选择的是每个时钟周期内的第几个跳变沿:
- CPHA = 0:数据在 第一个边沿 被采样(采样沿);
- CPHA = 1:数据在 第二个边沿 被采样(采样沿)。
这里“第一个边沿”和“第二个边沿”是相对于每个时钟周期而言的。举个例子,CPOL=0 且 CPHA=0 时,第一个边沿是上升沿,此时采样数据;而 CPOL=0 且 CPHA=1 时,采样发生在下降沿。我调试时经常被问:“CPHA 是不是就是上升沿还是下降沿?”其实不是——CPHA 只决定采样的是第几个沿,具体是上升还是下降还得结合 CPOL 看。
3.3 四种模式的数学组合
把 CPOL 和 CPHA 的两种取值一一组合,得到四种 SPI 模式。下表把它们的数学特征、采样边沿和空闲电平整理清楚了:
| 模式 | CPOL | CPHA | 空闲电平 | 采样边沿 | 典型应用设备(我见过的) |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低 | 第一个边沿(上升沿) | 多数 Flash、OLED 屏 |
| Mode 1 | 0 | 1 | 低 | 第二个边沿(下降沿) | 部分 ADC 芯片 |
| Mode 2 | 1 | 0 | 高 | 第一个边沿(下降沿) | 某些汽车级传感器 |
| Mode 3 | 1 | 1 | 高 | 第二个边沿(上升沿) | SD 卡、W5500 网络芯片 |
这个表我建议你打印出来贴在工位旁。因为调试时遇到莫名其妙的数据错位,十有八九是主从模式不匹配。我自己至少因为 Mode0 和 Mode3 搞混翻过三次车——你想想看,Mode0 和 Mode3 的开关波形连方向都一样,只是空闲电平不同,示波器上如果不看直流电平很容易忽视。
3.4 数学归纳:如何一眼判断主从匹配?
从数学上说,主从设备的 CPOL 和 CPHA 必须分别相等。强记一个公式:
master.CPOL == slave.CPOL AND master.CPHA == slave.CPHA
如果主设备用 Mode0,从设备也必须是 Mode0。为什么不能自动协商?因为 SPI 没有握手协议,全靠硬件约定。我曾看到有工程师用软件去“猜”从设备的模式,最后发现只是配置寄存器写错了位……嗯,老老实实查手册最稳。
下面这张图把 CPOL 和 CPHA 的数学关系、以及它们如何组合成四种模式清晰地画了出来。你可以把它当作“CPOL/CPHA 调参路线图”:
3.5 避坑指南:我的三次翻车现场
光讲理论不过瘾,我来分享几个因为 CPOL/CPHA 翻车的真实例子——你以后遇到类似情况,直接排查这两个寄存器就行。
第一次:LCD 屏花屏
用 STM32 的 SPI1 驱动一个 1.8 寸 TFT,默认配置是 Mode0(CPOL=0, CPHA=0)。屏幕能亮,但显示出来全是彩色噪点。我怀疑是时钟太快,降到 1 MHz 照旧。后来拿逻辑分析仪一看,原来屏幕芯片手册里写的是 Mode3(CPOL=1, CPHA=1)。当时我那个郁闷——就是因为没看手册第 18 页的“SPI Timing Diagram”。改成 Mode3 后画面秒变清晰。
第二次:温度传感器读数跳变
一个 MAX31865 测温模块,读出来的值每隔几秒就跳几十度。排查电源、走线都没问题。后来我用示波器看 MOSI/MISO 与 SCK 的相对位置,发现采样边沿正好落在数据变化点上——CPHA 配反了。我记得当时系统用的是 CPHA=0,但传感器要求 CPHA=1。改完一个 bit 后数据稳如泰山。
第三次教训:双机通信时 CRC 始终校验失败
两个 MCU 用 SPI 对接,一个做主一个做从。主发数据从机能收到,但 CRC 从不对。我当时怀疑是字节顺序问题,折腾了一天。最后同事提醒:“看看时钟极性?”一量,主设备 CPOL=0(空闲低),从设备因为某开发板默认配置成了 CPOL=1(空闲高)。两个时钟空闲电平相反,数据即使能收,采样时刻也有偏差。修复后 CRC 一次过。
这三次经历让我养成了一个习惯:在 SPI 初始化代码里,显式地写死 CPOL 和 CPHA 的数值,而不是依赖默认值。你可以参考下面的片段:
/* 以 Mode0 为例,显式配置 */
spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL = 0
spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA = 0
把这两个参数做成宏定义放在头文件里,每次换从设备只需改头文件,不用到处找寄存器。我也建议在硬件设计阶段就把所有从设备的模式列成一个表,贴在原理图上,这样 Layout 时就能避免因调试改线而留下的坑。
好了,CPOL 和 CPHA 的数学定义就聊到这儿。记住一句话:它们俩决定了 SPI 的“时钟骨架”和“采样眼睛”。当你下次调试设备不兼容时,第一个查的就是这对组合。熟记四种模式的状态,再加上示波器验证,99% 的问题都能当场解决。