4、SPI通信协议详解:SPI四线制(SCK/MOSI/MISO/CS)、四种模式(CPOL/CPHA)、多从机级联与菊花链
各位同学好,我是老张。今天咱们来聊聊SPI这个协议。说实话,SPI是我在嵌入式开发中用得最多的串行接口之一,比I2C简单直接,比UART灵活。很多压传感器、ADC、显示屏都爱用它。我个人习惯是,只要从机数量不多、距离不远,首选SPI。
SPI全称是Serial Peripheral Interface,摩托罗拉公司搞出来的。它是个同步、全双工的通信协议。什么叫同步?就是有专门的时钟线来同步数据。全双工呢?就是可以同时收发数据。嗯,这一点比I2C和UART都强。
4.1 SPI四线制:SCK、MOSI、MISO、CS
标准的SPI有四根线,少一根都不行(除非你搞特殊)。咱们一个一个说:
- SCK(Serial Clock):时钟线,由主机产生。说白了就是心跳,数据跟着它跳。
- MOSI(Master Out Slave In):主机输出,从机输入。数据从主机流向从机。
- MISO(Master In Slave Out):主机输入,从机输出。数据从从机流回主机。
- CS(Chip Select):片选线,低电平有效。主机拉低哪个从机的CS,就跟哪个从机说话。
我在项目中遇到过一个问题:有个同事把MOSI和MISO接反了,结果调了一整天没通。你想想看,数据发出去收不回来,能不头疼吗?所以布线时一定要仔细核对。
核心要点:SPI是主从结构,一个主机可以带多个从机。每个从机独占一根CS线,但共享SCK、MOSI、MISO。
4.2 四种模式:CPOL和CPHA
SPI有四种工作模式,由两个参数决定:CPOL(时钟极性)和CPHA(时钟相位)。
CPOL:决定时钟空闲时的电平。
- CPOL=0:空闲时SCK为低电平
- CPOL=1:空闲时SCK为高电平
CPHA:决定数据在哪个时钟沿采样。
- CPHA=0:第一个时钟沿采样(上升沿或下降沿取决于CPOL)
- CPHA=1:第二个时钟沿采样
组合起来就是四种模式:
| 模式 | CPOL | CPHA | 采样沿 | 数据变化沿 |
|---|---|---|---|---|
| 模式0 | 0 | 0 | 上升沿 | 下降沿 |
| 模式1 | 0 | 1 | 下降沿 | 上升沿 |
| 模式2 | 1 | 0 | 下降沿 | 上升沿 |
| 模式3 | 1 | 1 | 上升沿 | 下降沿 |
我曾经踩过一个坑:某款压传感器手册上写的是模式0,但我用模式1也能通信,就是数据偶尔错位。后来用示波器一看,原来是时序差了一个沿。所以一定要严格按照手册来,别想当然。
我的建议:调试SPI时,先用示波器抓一下SCK和MOSI的波形,看看数据对齐在哪个沿。这比盯着代码看有效得多。
4.3 多从机连接方式
当你有多个从机时,有两种连接方式:独立片选和菊花链。
4.3.1 独立片选(标准方式)
每个从机单独一根CS线,主机有多少个GPIO就能带多少个从机。这种方式最简单,也最常用。我做的压传感器采集板,一个MCU带了4个传感器,就是用的独立片选。
// 伪代码示例:独立片选方式
void spi_write_read(uint8_t cs_pin, uint8_t *tx_data, uint8_t *rx_data, uint16_t len) {
GPIO_Reset(cs_pin); // 拉低片选
for (uint16_t i = 0; i < len; i++) {
while(!SPI_IsTxEmpty()); // 等待发送缓冲区空
SPI_SendData(tx_data[i]); // 发送一个字节
while(!SPI_IsRxNotEmpty()); // 等待接收数据
rx_data[i] = SPI_ReceiveData(); // 读取接收数据
}
GPIO_Set(cs_pin); // 拉高片选
}
4.3.2 菊花链(Daisy Chain)
菊花链就有点意思了。所有从机共享SCK和CS,但数据线是串联的。主机的MOSI接第一个从机的SI,第一个从机的SO接第二个从机的SI,以此类推。最后一个从机的SO接主机的MISO。
为什么要用菊花链?因为有些场景下GPIO不够用,或者布线空间有限。但代价是延迟增加了——数据要经过每个从机才能到达主机。
注意:不是所有SPI从机都支持菊花链。必须看芯片手册里有没有提到"daisy-chain capable"。我曾经买了一批便宜的ADC,手册里没写支持菊花链,结果死活连不上。
4.4 知识体系总览
下面我用一张图来总结SPI的核心知识点,方便你理解整体结构:
4.5 避坑指南
做SPI驱动这么多年,我总结了几条血泪教训:
- 片选时序要留余量:拉低CS后,别急着发数据,等几个微秒。有些从机反应慢,你发太快它还没准备好。
- 时钟频率别太高:理论值归理论值,实际布线有寄生电容,频率高了波形就变形了。我一般留50%的余量。
- MISO要加上拉:如果从机是三态输出,CS没选中时MISO是高阻态。不加个上拉电阻,主机可能读到不确定的电平。
- 注意字节顺序:有的芯片是MSB first,有的是LSB first。搞反了数据全乱套。
调试小技巧:先用逻辑分析仪抓一下CS、SCK、MOSI的波形,确认时序对不对。我每次调SPI都是这么干的,比看代码快多了。
好了,SPI协议的核心内容就这些。四线制、四种模式、多从机连接,这三个点搞明白了,SPI驱动就没什么大问题了。下一节咱们会结合具体的压传感器芯片,手把手写一个完整的SPI驱动代码。
公众号:蓝海资料掘金营,微信deep3321