1. SPI 协议基础:从一条线到四根线的故事

SPI,全称 Serial Peripheral Interface,串行外设接口。说实话,这名字听起来挺唬人,但说白了就是「让两个芯片之间快速聊天」的一种方式。

我最早接触 SPI 是在做一款传感器采集板的时候。那时候刚毕业,看着示波器上的波形一头雾水。后来踩的坑多了,才慢慢摸透它的脾气。今天咱们就把 SPI 的底裤扒干净。

1.1 SPI 总线概述

SPI 是 Motorola 公司在 80 年代推出的。它属于同步串行通信,什么意思?就是发送方和接收方共用同一个时钟信号,时钟跳一下,数据走一位。

跟 I2C 比,SPI 的优势很明显:

  • 速度快:轻松跑到几十 MHz,I2C 一般也就 400kHz 或 1MHz
  • 全双工:可以同时收发,I2C 是半双工
  • 硬件简单:没有复杂的地址仲裁,谁拉低谁说话
  • 灵活:数据位宽、时钟极性、相位都能配

当然它也有短板——引脚多。I2C 两根线搞定的事,SPI 至少四根。

核心要点:SPI 是同步、全双工、主从架构的串行通信协议。速度上限取决于从设备的最高支持频率和 PCB 走线质量。

1.2 四线制信号定义

标准的 SPI 有四根信号线。我习惯把它们记成「一钟一选两数据」:

信号名 全称 方向 作用
SCLK Serial Clock 主→从 时钟信号,由主机产生
MOSI Master Out Slave In 主→从 主机发送数据给从机
MISO Master In Slave Out 从→主 从机发送数据给主机
CS/SS Chip Select / Slave Select 主→从 片选信号,低电平有效

这里有个容易搞混的点:MOSI 和 MISO 的方向是相对于主机而言的。我曾经见过有人把 MOSI 接到从机的 MISO 上,结果调了一整天没通……嗯,别问我怎么知道的。

小技巧:看芯片数据手册时,如果引脚标的是 SDI/SDO,SDI 就是数据输入(对芯片来说),SDO 就是数据输出。对应到 STM32 的 SPI 引脚,SDI 接 MOSI,SDO 接 MISO。

1.3 主从模式与多从机架构

SPI 通信只有一个主机,其他都是从机。主机负责产生时钟和片选信号,从机只能被动响应。

多从机架构有两种接法:

独立片选方式(最常用)

  • 每个从机单独占用一个 CS 引脚
  • 同一时刻只有一个 CS 被拉低
  • SCLK、MOSI、MISO 共用
  • 优点:从机之间完全隔离,互不影响
  • 缺点:主机 GPIO 占用多

菊花链方式(Daisy Chain)

  • 所有从机共用 CS 和 SCLK
  • 数据从主机→从机1→从机2→……→主机
  • 优点:节省引脚
  • 缺点:延迟累加,从机必须支持菊花链模式

我个人在实际项目中几乎只用独立片选方式。菊花链调试起来太痛苦了,一个从机出问题整条链都废掉。

避坑指南:多从机共用一个 SPI 总线时,MISO 线必须确保未被选中的从机输出为高阻态(Hi-Z)。否则多个从机同时驱动 MISO,轻则数据错误,重则烧引脚。我曾经在一个项目里忘了加外部上拉,结果从机切换时 MISO 上出现毛刺,导致连续读错三个字节。

1.4 时钟极性与相位(CPOL/CPHA)详解

这部分是 SPI 协议里最绕的地方,也是面试高频题。我当年第一次看数据手册里 CPOL=0, CPHA=1 这种描述时,直接懵了。

咱们拆开来看:

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

组合起来就是四种模式:

模式 CPOL CPHA 空闲电平 数据采样沿 数据输出沿
模式 0 0 0 低 上升沿 下降沿
模式 1 0 1 低 下降沿 上升沿
模式 2 1 0 高 下降沿 上升沿
模式 3 1 1 高 上升沿 下降沿

你想想看,为什么要有四种模式?因为不同的从设备对时钟的要求不一样。有些芯片要求上升沿采样,有些要求下降沿采样。主从双方必须工作在同一个模式下,否则数据对不上。

记忆口诀:CPOL 看空闲,CPHA 看采样。CPHA=0 第一个沿采样,CPHA=1 第二个沿采样。模式 0 和模式 3 最常用,前者是上升沿采样,后者是上升沿输出。

我习惯的做法是:拿到一个新从设备,先看数据手册里 SPI 时序图,找到它要求的 CPOL 和 CPHA,然后在 STM32 的 SPI 初始化结构体里配好。千万别想当然用默认值,我吃过这个亏——一个温湿度传感器手册上写的是模式 1,我用模式 0 读了一整天全是 0xFF。

// STM32 SPI 配置示例(模式 0)
SPI_HandleTypeDef hspi1;

hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;   // CPOL = 0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;       // CPHA = 0
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;

HAL_SPI_Init(&hspi1);

1.5 SPI 通信时序图解

光说理论不够直观,咱们画一张图看看模式 0 下 SPI 是怎么工作的。

SPI 模式 0 通信时序图 SPI 模式 0 通信时序(CPOL=0, CPHA=0) CS 片选拉低,从机被选中 SCLK ▲ 采样 ▲ 采样 ▲ 采样 ▲ 采样 MOSI D7 D6 D5 D4 D3 D2 D1 D0 MISO D7 D6 D5 D4 D3 D2 D1 D0 时序说明: 1. CS 拉低后,从机开始监听 SCLK 2. 每个 SCLK 上升沿,主机和从机同时采样对方的数据线 3. 每个 SCLK 下降沿,主机和从机更新数据线,准备下一位

从图上可以看得很清楚:在模式 0 下,SCLK 空闲时为低电平,数据在上升沿被采样,在下降沿被更新。主机和从机是同时收发数据的——主机发 D7 的同时,从机也在发 D7,这就是全双工。

1.6 本章小结

SPI 协议基础就这些内容。总结几个关键点:

  • 四根线:SCLK、MOSI、MISO、CS,各司其职
  • 一主多从,独立片选最可靠
  • CPOL 和 CPHA 决定时序模式,主从必须一致
  • 模式 0 和模式 3 最常用,但一定要以数据手册为准