1. SPI 协议基础:从一条总线说起

SPI,全称 Serial Peripheral Interface,串行外设接口。这名字听着挺正式,其实说白了就是「主设备跟从设备之间传数据的一种约定」。我最早接触 SPI 是在做一款温湿度传感器驱动的时候,那时候还不懂什么 CPOL、CPHA,拿着逻辑分析仪一顿抓波形,硬是调了两天才通。后来回头看,其实就是模式没配对。

SPI 是 Motorola 公司搞出来的,现在已经是嵌入式领域最常用的板级通信协议之一。它最大的特点就是快——全双工、同步、主从结构,速度可以跑到几十兆赫兹。你想想看,I2C 通常也就 400kHz 到 1MHz,SPI 直接甩它几条街。

1.1 四线制信号线详解

SPI 总线一共四根线,少一根都不行(除非你只用单向传输)。我见过不少新手把 MOSI 和 MISO 接反了,结果数据死活不对。嗯,这里要注意,接反了不会烧设备,但数据就是乱码。

信号线 全称 方向 作用
SCK Serial Clock 主→从 时钟信号,由主机产生,决定传输速率
MOSI Master Out Slave In 主→从 主机发送数据给从机
MISO Master In Slave Out 从→主 从机发送数据给主机
CS Chip Select 主→从 片选信号,低电平有效,选中对应从机

这里有个细节:SCK 永远由主机控制。从机只是被动接收时钟,它自己不会产生时钟。所以 SPI 通信中,主机是绝对的老大。

我的习惯:画 PCB 的时候,我会把 MOSI 和 MISO 在原理图上标清楚,并且加一个丝印箭头。别小看这个动作,它能帮你省掉至少半小时的调试时间。

1.2 SPI 四种工作模式(CPOL/CPHA)

这是 SPI 最让人头疼的地方,也是面试最爱问的。CPOL 和 CPHA 各占一个 bit,组合起来就是四种模式。

  • CPOL(Clock Polarity):时钟极性。CPOL=0 时,空闲态 SCK 为低电平;CPOL=1 时,空闲态 SCK 为高电平。
  • CPHA(Clock Phase):时钟相位。CPHA=0 时,数据在第一个时钟边沿采样;CPHA=1 时,数据在第二个时钟边沿采样。

我刚开始做项目的时候,经常搞混 CPHA=0 和 CPHA=1 到底哪个边沿采样。后来我记了一个口诀:「CPHA=0,先采样;CPHA=1,后采样」。说白了,CPHA=0 就是第一个边沿(上升沿或下降沿,取决于 CPOL)就把数据读进来,CPHA=1 则是等到第二个边沿才读。

模式 CPOL CPHA 空闲 SCK 电平 数据采样边沿
模式 0 0 0 低电平 上升沿(第一个边沿)
模式 1 0 1 低电平 下降沿(第二个边沿)
模式 2 1 0 高电平 下降沿(第一个边沿)
模式 3 1 1 高电平 上升沿(第二个边沿)
我曾经踩过的坑:有一次用 STM32 驱动一个 LCD 屏幕,数据手册上写的是「模式 2」,我配成了模式 0。结果屏幕能亮,但显示全是乱的。逻辑分析仪一抓才发现,数据在错误的边沿被采样了。所以,一定要先看从机数据手册,再配 SPI 模式,别想当然。

1.3 SPI 数据帧格式(8 位 / 16 位)

SPI 传输的数据帧长度通常是 8 位或 16 位。STM32 的 SPI 外设还支持更灵活的配置,但最常用的就是这两种。

  • 8 位数据帧:最常见,一个字节一个字节地传。适合传感器、ADC、DAC 这类设备。
  • 16 位数据帧:适合需要一次传两个字节的场景,比如某些 LCD 控制器、音频编解码器。

我个人习惯:能用 8 位就别用 16 位。为什么?因为 8 位更通用,很多库函数和 DMA 配置都是按字节对齐的。用 16 位反而容易出字节序的问题。

关键点:STM32 的 SPI 数据帧长度是通过 CR1 寄存器的 DFF 位配置的。DFF=0 是 8 位,DFF=1 是 16 位。注意,修改 DFF 位之前必须先关闭 SPI(SPE=0),否则配置不生效。

1.4 多从机配置:菊花链与独立片选

一个 SPI 主机可以挂多个从机。怎么挂?有两种方式。

独立片选方式

每个从机独占一根 CS 线。主机想跟谁说话,就把谁的 CS 拉低,其他从机的 CS 保持高电平。这种方式最常用,也最可靠。

  • 优点:从机之间互不干扰,通信速度快。
  • 缺点:占用 GPIO 多。挂 N 个从机就需要 N 根 CS 线。

菊花链方式

所有从机共用一根 CS 线,数据从第一个从机的 MOSI 进,从它的 MISO 出,再接到第二个从机的 MOSI,以此类推。最后一个从机的 MISO 接回主机的 MISO。

  • 优点:节省 GPIO,只需要一根 CS 线。
  • 缺点:延迟大,数据要经过所有从机才能回到主机;而且要求从机必须支持菊花链模式。

我在实际项目中几乎只用独立片选。菊花链看着省线,但调试起来太痛苦了——任何一个从机出问题,整条链都废了。

SPI 多从机连接方式对比 独立片选方式 SPI 主机 从机 1 从机 2 从机 3 CS1 CS2 CS3 菊花链方式 SPI 主机 从机 1 从机 2 从机 3 MOSI→SI1 SO1→SI2 SO2→SI3 SO3→MISO 独立片选:每从机独占 CS,并行连接 MOSI/MISO/SCK 菊花链:共用 CS,数据串行经过每个从机
我的建议:如果你只是做原型验证,用独立片选。等产品定型了,再考虑要不要优化 GPIO。别一开始就搞菊花链,调试成本太高。