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 是怎么工作的。
从图上可以看得很清楚:在模式 0 下,SCLK 空闲时为低电平,数据在上升沿被采样,在下降沿被更新。主机和从机是同时收发数据的——主机发 D7 的同时,从机也在发 D7,这就是全双工。
1.6 本章小结
SPI 协议基础就这些内容。总结几个关键点:
- 四根线:SCLK、MOSI、MISO、CS,各司其职
- 一主多从,独立片选最可靠
- CPOL 和 CPHA 决定时序模式,主从必须一致
- 模式 0 和模式 3 最常用,但一定要以数据手册为准