2. SPI协议深度解析:模式、时钟极性与相位、多从机片选

SPI协议,说白了就是四根线的事儿——SCLK、MOSI、MISO、CS。但就是这四根线,坑了我不少个通宵。今天咱们把这协议掰开了揉碎了讲,你以后调LCD驱动时,至少能少走一半弯路。

2.1 SPI的四种模式:CPOL与CPHL到底在说啥?

很多新手看到SPI模式0/1/2/3就头大。我刚开始也这样,后来发现其实就两个参数:时钟极性(CPOL)和时钟相位(CPHA)。

SPI模式 CPOL CPHA 空闲时钟电平 数据采样边沿
模式0 0 0 低电平 上升沿采样
模式1 0 1 低电平 下降沿采样
模式2 1 0 高电平 下降沿采样
模式3 1 1 高电平 上升沿采样

核心记忆法:CPOL决定空闲电平,CPHA决定采样边沿。模式0和模式3最常用——LCD驱动芯片八成以上默认模式0。

我遇到过最坑的一次:某国产LCD屏手册写支持模式0,结果示波器一抓,它其实在模式3下才稳定。后来发现是手册翻译错了。所以啊,别信手册,信示波器。

2.2 时钟极性与相位:示波器上怎么看?

调驱动时,你不可能每次都去翻芯片手册。我的习惯是:直接抓波形,一眼判断模式。

  • 看空闲电平:CS拉高后,SCLK停在低电平→CPOL=0;停在高电平→CPOL=1。
  • 看采样边沿:数据在SCLK上升沿变化→CPHA=0(采样在下降沿);数据在下降沿变化→CPHA=1(采样在上升沿)。

实战技巧:用示波器同时抓SCLK和MOSI,把时基放到每格50ns。看MOSI数据变化时刻对应SCLK的哪个边沿——这个边沿就是数据变化边沿,另一个边沿就是采样边沿。

举个例子:你看到SCLK空闲是低,数据在上升沿变化,那下降沿就是采样点。CPOL=0,CPHA=1→模式1。简单吧?

2.3 多从机片选机制:别让CS坑了你

一个SPI总线上挂多个设备,最常见的就是LCD+触摸屏+Flash。片选机制看似简单,其实有门道。

2.3.1 硬件片选 vs 软件片选

  • 硬件片选:每个从机独立CS引脚,MCU的GPIO直接控制。优点是响应快,缺点是占引脚。
  • 软件片选:通过SPI指令中的地址字节来选择从机。省引脚,但协议复杂,速度慢。

LCD驱动几乎都用硬件片选。我建议你也这么干——软件片选在高速传输时容易出时序问题,我曾经被坑过一次,200ns的片选延迟导致LCD刷屏出现条纹。

2.3.2 片选时序的三个关键点

  1. CS拉低到第一个SCLK的时间(tCSS):至少大于从机要求的建立时间,一般5ns~50ns。
  2. 最后一个SCLK到CS拉高的时间(tCSH):保证数据完全锁存,通常10ns以上。
  3. CS拉高到下一次拉低的时间(tCS间):给从机足够时间处理数据,至少100ns。

我曾经踩过的坑:某项目用STM32的硬件SPI驱动LCD,CS用软件控制。结果发现每次传输完最后一个字节,CS立刻拉高,导致LCD没锁存到数据。后来在SPI传输完成后加了50ns的延时才解决。记住:CS拉高不能紧贴着最后一个SCLK边沿。

2.4 SPI协议核心流程图

下面这张图把SPI一次完整传输的流程画清楚了。你对照着看,以后调驱动时心里就有谱了。

SPI单次传输完整流程 SPI单次传输完整流程 主设备拉低CS 等待tCSS(建立时间) SCLK产生时钟脉冲 MOSI发送数据 / MISO接收数据 等待tCSH(保持时间) 主设备拉高CS 传输结束 关键时序参数 tCSS:CS↓ → 第一个SCLK↑ tCSH:最后一个SCLK↓ → CS↑ tCS间:CS↑ → 下一次CS↓ 数据方向 MOSI:主→从(命令/数据) MISO:从→主(状态/数据) 全双工同时传输

2.5 多从机片选的硬件设计建议

如果你要在一条SPI总线上挂多个设备,有几点经验供你参考:

  • CS引脚不要共用:每个从机独立CS,不要用译码器。译码器会引入额外延迟,高速时容易出问题。
  • CS线要短:超过10cm的CS线,建议加33Ω串联电阻抑制反射。我见过CS线上振铃导致从机误触发的案例。
  • 空闲时CS必须拉高:有些从机内部有弱上拉,但别依赖它。GPIO配置成推挽输出,主动拉高。
  • 切换从机时留足时间:CS拉高后,至少等1μs再拉低下一个从机的CS。给总线一个“冷静期”。

我的调试习惯:新板子第一次上电,先用示波器抓CS和SCLK的对应关系。看CS拉低后多久出现第一个SCLK,最后一个SCLK后多久CS拉高。这两个时间决定了你的SPI配置是否匹配从机要求。

2.6 常见SPI模式配置错误与现象

错误类型 现象 排查方法
CPOL配置反了 第一个字节正确,后续全错 抓SCLK空闲电平,对比手册
CPHA配置反了 数据整体偏移一位 看数据变化边沿是否在采样边沿
CS时序太紧 偶尔丢数据,尤其高速时 检查tCSS和tCSH是否满足
SCLK频率过高 数据全是0xFF或0x00 降低频率到1MHz以下测试