3. SPI 总线协议详解:四种工作模式、全双工与拓扑

SPI 这玩意儿,说实话,在嵌入式系统里太常见了。我最早接触它是在做一款工业传感器采集板的时候,当时被时钟极性和相位折腾得够呛。今天咱们就把 SPI 的底裤扒干净,从工作模式到拓扑结构,一次说透。

3.1 SPI 的四种工作模式:CPOL 和 CPHA 说了算

SPI 通信的核心就是主设备控制时钟信号(SCLK),然后数据在时钟的边沿被采样或输出。这里有两个关键参数:CPOL(时钟极性)和 CPHA(时钟相位)。

  • CPOL:决定时钟空闲时的电平。CPOL=0 表示空闲低电平,CPOL=1 表示空闲高电平。
  • CPHA:决定数据在哪个边沿被采样。CPHA=0 表示在第一个边沿采样,CPHA=1 表示在第二个边沿采样。

组合起来就是四种模式。我习惯用一张表来记,清晰明了:

模式 CPOL CPHA 空闲时钟电平 数据采样边沿 数据输出边沿
模式 0 0 0 低电平 上升沿 下降沿
模式 1 0 1 低电平 下降沿 上升沿
模式 2 1 0 高电平 下降沿 上升沿
模式 3 1 1 高电平 上升沿 下降沿
我的小窍门: 大多数 SPI 从设备默认工作在模式 0。如果你不确定,先试试模式 0,不行再换模式 3。这两种模式最常用,我项目中 80% 的情况都是它们。

为什么会这样?因为模式 0 和模式 3 的采样边沿都是上升沿,只是空闲电平不同。很多从设备对空闲电平不敏感,所以能兼容。

3.2 全双工通信机制:同时收发才是真本事

SPI 最牛的地方就是全双工。什么意思?就是主设备在发送数据的同时,也能接收从设备的数据。这得益于它独立的 MOSI(主出从入)和 MISO(主入从出)两条数据线。

通信过程其实很简单:

  1. 主设备拉低片选(CS),选中从设备。
  2. 主设备在 SCLK 上产生时钟脉冲。
  3. 在每个时钟周期,主设备通过 MOSI 发送一位,同时从设备通过 MISO 发送一位。
  4. 8 个时钟周期后,一个字节传输完成。

这里有个坑,我当年踩过。SPI 的收发是同步的,也就是说,你想读一个字节,必须先写一个字节。这个写出去的字节可以是任意值,通常叫“哑字节”(dummy byte)。

关键点: SPI 没有应答机制。主设备发完数据,它不知道从设备有没有收到。所以很多从设备会通过 MISO 返回状态寄存器,你需要主动去读。

看个简单的代码示例,用 STM32 的 HAL 库读一个 SPI 从设备的 ID 寄存器:

// 假设从设备 ID 寄存器地址为 0x01
uint8_t tx_data[2] = {0x01, 0x00};  // 先发地址,再发哑字节
uint8_t rx_data[2] = {0};

HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);  // 拉低片选
HAL_SPI_TransmitReceive(&hspi1, tx_data, rx_data, 2, 100); // 同时收发
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);    // 拉高片选

uint8_t device_id = rx_data[1];  // 第二个字节才是读回来的数据

你看,发两个字节,收两个字节。第一个字节是地址,第二个字节才是我们想要的 ID。这就是全双工的精髓——收发同时进行。

3.3 多从机片选与菊花链拓扑

一个 SPI 主设备可以带多个从设备。怎么带?两种方式:独立片选和菊花链。

3.3.1 独立片选(Parallel Topology)

这是最常用的方式。每个从设备有一个独立的片选引脚,主设备通过拉低某个从设备的 CS 来选中它。其他从设备的 CS 保持高电平,它们的 MISO 引脚会处于高阻态,不会干扰通信。

优点:简单、可靠、每个从设备独立寻址。
缺点:占用主设备 GPIO 多,从设备多了布线麻烦。

我曾经踩过的坑: 多个从设备共享 MISO 线时,一定要确保未选中的从设备 MISO 是高阻态。有些老芯片不支持高阻,需要加三态缓冲器,否则数据会打架。

3.3.2 菊花链拓扑(Daisy Chain)

菊花链就省 GPIO 了。所有从设备共享 SCLK 和 CS,但 MOSI 和 MISO 是串联的。数据从主设备发出,经过第一个从设备,再传到第二个,以此类推,最后回到主设备。

工作原理:

  1. 主设备发送 N 个字节(N 等于从设备数量)。
  2. 每个从设备在自己的内部移位寄存器中处理数据。
  3. 第一个从设备接收第一个字节,同时把之前的数据传给下一个。
  4. N 个时钟周期后,所有从设备都收到了自己的数据。

我画了个图,帮你理解这两种拓扑的区别:

SPI 多从机拓扑对比 独立片选拓扑 主设备 (Master) 从设备 1 从设备 2 从设备 3 SCLK MOSI MISO CS1/CS2/CS3 菊花链拓扑 主设备 (Master) 从设备 1 从设备 2 从设备 3 SCLK MOSI MISO CS (共用) 独立片选:每个从设备独立 CS,并行连接 MOSI/MISO。菊花链:共用 CS,数据串联,适合从设备数量多但 GPIO 少的场景。

菊花链的优点是省引脚,但缺点也很明显:

  • 延迟大:数据要经过每个从设备,传输 N 个字节才能完成一次操作。
  • 可靠性差:一个从设备出问题,整个链就断了。
  • 速度受限:每个从设备都会引入延迟,总线的最高频率会降低。
我的建议: 如果从设备数量不超过 3 个,用独立片选。超过 3 个且 GPIO 紧张,再考虑菊花链。另外,有些从设备(比如移位寄存器)天生就支持菊花链,用起来很方便。

3.4 实际项目中的避坑指南

最后,分享几个我实际项目中遇到的 SPI 问题:

  • 时钟极性不匹配:从设备手册说支持模式 0,结果死活不通。后来发现是手册写错了,实际是模式 1。所以,先看波形,别全信手册。
  • 片选毛刺:主设备 CS 引脚在初始化时会有短暂的低电平,导致从设备误触发。解决办法是加个上拉电阻,或者初始化时先拉高 CS。
  • 高速通信信号质量差:SPI 频率超过 10MHz 时,线长超过 10cm 就要考虑信号完整性。我一般加个 22Ω 的串联电阻,或者降低频率。

嗯,SPI 的内容就这些。记住四个模式、全双工的本质、两种拓扑的取舍,你就能搞定大部分 SPI 项目了。