4. SPI 协议详解:四线机制与时钟配置
SPI 这玩意儿,说实话,是我在嵌入式开发里用得最多的同步串口之一。它简单、高效,而且几乎所有 MCU 都自带硬件外设。但很多人只停留在「会调库」的阶段,一旦遇到通信不稳定、数据错位,就抓瞎了。
今天咱们就把 SPI 的四根线、时钟极性(CPOL)和相位(CPHA)彻底讲透。我保证,听完这节课,你不仅能调通 SPI,还能自己排查 90% 的通信问题。
4.1 四线机制:SCK、MOSI、MISO、CS
SPI 全称是 Serial Peripheral Interface,同步、全双工、主从架构。它用四根线搞定一切:
| 信号线 | 全称 | 方向(主→从) | 作用 |
|---|---|---|---|
| SCK | Serial Clock | 主→从 | 时钟信号,由主机产生,决定数据传输速率 |
| MOSI | Master Out Slave In | 主→从 | 主机发送数据给从机 |
| MISO | Master In Slave Out | 从→主 | 从机发送数据给主机 |
| CS | Chip Select | 主→从 | 片选信号,低电平有效,选中特定从机 |
这里有个关键点:SPI 是全双工的。什么意思?主机在发送一个字节的同时,也在接收从机发来的一个字节。你想想看,这比 I2C 那种半双工要快一倍。
核心理解:SPI 的收发是同时发生的。主机每发出一个 SCK 脉冲,MOSI 上送出一位数据,MISO 上同时采样一位数据。所以 SPI 的「发送」和「接收」其实是同一件事的两个面。
我个人习惯把 SPI 比作「乒乓球对打」:主机发一个球(MOSI),从机同时回一个球(MISO),SCK 就是击球的节奏。CS 则是喊「轮到你了」的开关。
4.2 时钟极性(CPOL)和相位(CPHA)
好,重点来了。CPOL 和 CPHA 这两个参数,我见过太多工程师在这上面翻车。说白了,它们决定了 SCK 在空闲时的电平,以及数据在哪个边沿被采样。
4.2.1 CPOL:时钟极性
CPOL 定义的是 SCK 在空闲状态(没有数据传输时)的电平。
- CPOL = 0:空闲时 SCK 为低电平。
- CPOL = 1:空闲时 SCK 为高电平。
嗯,就这么简单。但别急,它要和 CPHA 配合起来看。
4.2.2 CPHA:时钟相位
CPHA 定义的是数据在 SCK 的哪个边沿被采样。
- CPHA = 0:在 SCK 的第一个边沿采样数据(上升沿或下降沿,取决于 CPOL)。
- CPHA = 1:在 SCK 的第二个边沿采样数据。
你可能会问:「第一个边沿是上升还是下降?」这取决于 CPOL。我整理了一张表,你一看就明白:
| SPI 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据输出边沿 |
|---|---|---|---|---|---|
| 模式 0 | 0 | 0 | 低 | 上升沿(第一个边沿) | 下降沿 |
| 模式 1 | 0 | 1 | 低 | 下降沿(第二个边沿) | 上升沿 |
| 模式 2 | 1 | 0 | 高 | 下降沿(第一个边沿) | 上升沿 |
| 模式 3 | 1 | 1 | 高 | 上升沿(第二个边沿) | 下降沿 |
实战技巧:大多数 SPI 从机芯片默认使用模式 0(CPOL=0, CPHA=0)。但总有些「刺头」芯片用模式 3。我建议你拿到 datasheet 后,第一件事就是查 SPI 模式。我曾经因为没看 datasheet,调一个 LCD 驱动调了整整一下午,最后发现是模式配反了。
4.3 数据采样与建立的时序关系
为什么 CPOL 和 CPHA 这么重要?因为它直接决定了数据能否被正确采样。你想想看,如果主机在某个边沿输出数据,而从机却在同一个边沿采样,那采到的可能是上一个位,或者是不稳定的跳变。
正确的做法是:数据在一个边沿建立(输出),在另一个边沿采样(捕获)。这样数据有半个时钟周期的时间稳定下来。
以模式 0 为例:
- SCK 空闲为低。
- 主机在 SCK 的下降沿更新 MOSI 上的数据。
- 从机在 SCK 的上升沿采样 MOSI 上的数据。
这样,数据从更新到被采样,中间有半个时钟周期的建立时间,足够信号稳定。
注意:如果 CPOL 和 CPHA 配置错误,数据可能会在跳变边沿被采样,导致读到错误的值。这种错误非常隐蔽,因为数据看起来「大部分是对的」,但偶尔会跳变。我曾经在一个传感器项目中遇到过,读出来的温度值每隔几秒就跳一下,最后用示波器一看,原来是 CPHA 配反了。
4.4 多从机连接方式
SPI 支持一主多从。有两种接法:
- 独立片选:每个从机独占一根 CS 线。主机想和谁通信,就把对应的 CS 拉低。这是最常用的方式。
- 菊花链:所有从机共用 CS 和 SCK,但 MOSI 和 MISO 串联起来。数据像流水线一样依次经过每个从机。这种方式用得少,一般只在移位寄存器场景下用。
我个人强烈建议用独立片选。菊花链虽然省 IO,但调试起来太痛苦了——任何一个从机出问题,整条链都废了。
4.5 知识结构图:SPI 协议核心逻辑
下面这张图帮你把 SPI 的整个知识脉络串起来:
4.6 实战中的避坑指南
最后,分享几个我这些年踩过的坑:
- CS 的拉高时机:很多从机要求在最后一个 SCK 边沿之后保持 CS 低电平一段时间(称为 CS hold time)。如果 CS 拉高太快,从机可能还没完成内部处理,导致数据丢失。我一般会在传输结束后加一个微秒级的延时再拉高 CS。
- 时钟频率上限:SPI 的速率不是越高越好。从机有最大时钟频率限制,而且 PCB 走线太长时,高速信号会反射。我遇到过在 20MHz 下跑得好好的,换了一根长排线就乱码了。降频到 10MHz 就稳了。
- MISO 竞争:多从机共用 MISO 线时,必须保证未选中的从机把 MISO 置为高阻态,否则多个从机同时驱动 MISO 会打架。有些老芯片不支持高阻,那就得加三态缓冲器。
调试利器:逻辑分析仪是调试 SPI 最好的工具。把 SCK、MOSI、MISO、CS 四根线都抓下来,对照 datasheet 的时序图看。我曾经用这个方法,半小时就找到了一个隐藏了三天的问题——原来是 CS 的毛刺导致从机被误选中。