文章摘要
SPI(Serial Peripheral Interface)是嵌入式开发中最常用的高速通信总线之一,也是大厂面试的高频考点。本文从通信时序、电气特性、驱动开发三个层面,系统梳理 SPI 的核心原理与工程实践,涵盖 CPOL/CPHA 四种模式、边发边收机制、MISO 三态问题、信号完整性设计,以及硬件/软件 SPI 的驱动实现与参数配置等关键内容,帮助你在面试与日常开发中真正做到"知其然,更知其所以然"。
文章目录
- 文章摘要
- 本栏系列文章
- 其他专栏精品文章
- 前言
- 一、SPI 基础概念与时序
- 1.1 SPI 是什么
- 1.2 边发边收
- 1.3 SPI 时序:CPOL 与 CPHA
- 1.4 边沿读取与 M/LSB
- 1.5 建立时间与保持时间
- 二、电气属性与硬件设计
- 2.1 电气模型
- 2.2 拓扑结构
- 2.3 硬件设计核心注意事项
- (1)走线长度(最重要)
- (2)信号完整性(Signal Integrity)
- (3)CS(片选)设计
- (4)MISO 三态问题(非常关键)
- (5)上拉
- (6)地线问题
- (7)电平匹配(非常重要)
- 三、驱动开发与调试
- 3.1 硬件 SPI 与软件 SPI
- 3.2 硬件 SPI 初始化
- 3.3 软件 SPI 初始化
- 3.4 附:MCU 外设 API 设计思路
- 总结
本栏系列文章
学透CANFD看这个就行(四): 错误帧、位填充、采样点与通信负荷
其他专栏精品文章
CSDN高赞项目:工程训练竞赛之垃圾分类垃圾桶
前言
SPI 和 IIC 几乎是一定会被问到的两门总线协议,而 SPI 又因为"模式多、速率高、坑更多",考察的深度往往比 IIC 更上一层。
SPI 看起来"只是四根线",但真正能讲清楚的人并不多:
- 你知道 CPOL 和 CPHA 到底分别决定什么吗?四种模式能否张口就画出来?
- 为什么读数据时也要发一个"空字节"?
- 多从机共线时 MISO 为什么要三态?信号反射了怎么处理?
这篇文章结合我自己的工程经验,把 SPI 的知识体系完整梳理出来,重点覆盖面试中容易被追问的几个方向:
- 时序层面:CPOL/CPHA 四种模式、边发边收、M/LSB、建立/保持时间;
- 电气层面:推挽驱动、走线长度、信号完整性、MISO 三态、电平匹配;
- 驱动层面:硬件 SPI 与软件 SPI 的初始化、参数结构体配置、API 设计思路。
无论你是正在备战嵌入式面试的求职者,还是希望系统巩固 SPI 基础的同学,相信这篇文章都能帮你建立起完整的知识框架。
一、SPI 基础概念与时序
1.1 SPI 是什么
SPI(Serial Peripheral Interface)是 MCU 和外设之间的一种同步串行通信方式。它的核心逻辑是:
主机发时钟,从机跟着时钟一位一位收发数据。
与 IIC 不同的是,SPI 通信不是靠"地址"找设备,而是靠 CS 片选线找设备。
SPI 由四根信号线组成:
| 信号线 | 含义 | 方向 |
|---|---|---|
| SCLK | 时钟线 | 主机输出 |
| MOSI | 主机发送、从机接收 | 主 → 从 |
| MISO | 从机发送、主机接收 | 从 → 主 |
| CS | 片选线 | 主机输出 |
1.2 边发边收
SPI 的一个显著特点是边发边收:哪怕你只是想读数据,通常也要发送一个空字节(比如0xFF或0x00),用来产生时钟。
在驱动库封装时,可以把CS 操作单独封装,尽量不与字节交换耦合,这样在多从机场景下复用性更好。
例如,MCU 要给外设发送0x9F,一次完整的通信流程是:
CS 拉低 -> MOSI 发送 0x9F -> SCLK 输出 8 个时钟 -> MISO 返回 1 个字节 -> CS 拉高1.3 SPI 时序:CPOL 与 CPHA
SPI 的时序本质只控制两件事:
-时钟空闲时是高还是低(CPOL);
-在第几个边沿采样数据(CPHA)。
其中:
-CPOL(Clock Polarity,时钟极性)决定"时钟初始状态";
-CPHA(Clock Phase,时钟相位)决定"在上升沿还是下降沿读数据"。
主机同样也是在边沿进行数据发送的。
每个时钟都有两个边沿(上升沿、下降沿),SPI 时钟只能在边沿读数据。当 CPOL = 0 时,第一个边沿为上升沿。
CPOL 取值:
| CPOL | 时钟空闲状态 |
|---|---|
| 0 | 空闲时 SCLK = 0(低电平) |
| 1 | 空闲时 SCLK = 1(高电平) |
CPHA 取值:
| CPHA | 数据采样时刻 |
|---|---|
| 0 | 第一个边沿采样 |
| 1 | 第二个边沿采样 |
由 CPOL 与 CPHA 组合出四种工作模式:
| 模式 | CPOL | CPHA | 时钟空闲电平 | 采样边沿 | 数据变化边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低 | 上升沿(第 1 边沿) | 下降沿 |
| Mode 1 | 0 | 1 | 低 | 下降沿(第 2 边沿) | 上升沿 |
| Mode 2 | 1 | 0 | 高 | 下降沿(第 1 边沿) | 上升沿 |
| Mode 3 | 1 | 1 | 高 | 上升沿(第 2 边沿) | 下降沿 |
记忆口诀:CPOL 看空闲电平,CPHA 看采样在第几个边沿。Mode 0(CPOL=0, CPHA=0)最常用,也是相对最稳定的一种配置。
1.4 边沿读取与 M/LSB
SPI 不是一次把整个字节发出去,而是按 bit 发送与读取。SPI 更像一个"移位寄存器接口",按照 CLK 进行移入移出数据,因此提供 M/LSB 配置。
-MSB(Most Significant Bit first):高位在前;
-LSB(Least Significant Bit first):低位在前。
通常使用MSB 模式,且需要注意:
- 如果是主从 MCU 通信,M/LSB 必须设置一致;
- 如果从机是传感器,数据手册会标注它属于 M/LSB 哪一种。
对比 I2C:I2C 协议本身就规定 MSB 模式(没有 M/LSB 配置选项),而 SPI 提供可配置选项。
关于采样边沿的深入理解:
CPHA 决定在第几个边沿进行采样,这个"采样"不仅代表主机对从机数据的采样,也包含从机对主机数据的采样。因此在另一个边沿开始时,主机与从机将对 MISO 与 MOSI 进行数据更新;理想情况下,MOSI 与 MISO 的数据会从"准备数据的边沿"一直维持到"下一个准备数据的边沿"。
1.5 建立时间与保持时间
- 建立时间(Setup Time):采样之前,数据必须提前稳定的最短时间。在 SPI 里的意义是——上升沿之前的一段时间,MOSI 必须已经稳定;
- 保持时间(Hold Time):采样之后,数据还必须继续保持稳定的最短时间。即采样之后的一小段时间内,数据必须保持不变。
SPI 数据正确的本质:发送端在一个边沿改变数据,使数据在另一个边沿采样时满足 setup/hold 时间,从而保证采样点处的数据是稳定的逻辑电平。
二、电气属性与硬件设计
2.1 电气模型
SPI 的本质是推挽高速数字信号 + 同步时钟总线,即 MOSI / SCLK 是推挽驱动(区别于 IIC 的开漏 + 上拉)。
由于是推挽驱动,SPI 可以跑得很快:
| 速率 | 说明 |
|---|---|
| 几 MHz ~ 20 MHz | 常规场景 |
| 50 MHz | 较高速度 |
| 100+ MHz | 短线等严格条件下可达 |
但因为 SPI 是单端对地信号,抗干扰能力一般。
2.2 拓扑结构
除主从机一对一外,SPI 常见的拓扑是总线(CLK、MOSI、MISO)被多从机共享,CS 用于独立控制各个从机。
例如出现 8 路一样的 ADC 时,如果采样率允许,可以使用软件 SPI,甚至可以共用 MOSI、CLK、CS,然后各个设备回传的 ADC 数据分别走各自的 MISO。
关键点:如果存在多个从机共用 MISO 的情况,MISO 必须处于三态(高阻)。
2.3 硬件设计核心注意事项
(1)走线长度(最重要)
| 速率 | 走线要求 |
|---|---|
| 低速(<1 MHz) | 可以几十 cm,板上随便走 |
| 中速(5~20 MHz) | 走线尽量短、不要绕远、避免跨分割地 |
| 高速(>50 MHz) | 控制阻抗、走线等长(可选)、加地参考层 |
(2)信号完整性(Signal Integrity)
边沿太快会出现信号反射,导致波形振铃、过冲、下冲。解决方法:
- 串联电阻:
MCU ---[22Ω~47Ω]--- SPI 线,降低上升沿速度、抑制反射; - 缩短走线:SPI 的第一优化永远是"缩短距离";
- 增加地平面:SPI 参考地必须完整,GND plane 必须连续,避免跨分割。
(3)CS(片选)设计
- CS 必须使用独立 GPIO;
- CS 拉低后必须稳定一段时间,不能乱跳。
(4)MISO 三态问题(非常关键)
多从机状态下,只有一个从机能驱动 MISO,其他从机都必须是高阻态。
(5)上拉
- MOSI / SCLK不需要上拉(推挽驱动);
- CS需要上拉(防止上电误触发)。
(6)地线问题
SPI 是高速单端信号,必须保证:
- MCU 和从机 GND共地;
- 地线低阻抗;
- 不要"长回流路径"。
(7)电平匹配(非常重要)
MCU 通常是 3.3V,而从机可能是 1.8V 或 5V,需要:
- 使用电平转换芯片;
- 或确认 IO 是tolerant IO(容忍电平)。
三、驱动开发与调试
3.1 硬件 SPI 与软件 SPI
SPI 驱动也可以划分成两类:
- 硬件 SPI:借助 MCU 片上的 SPI 外设实现;
- 软件 SPI:通过控制 GPIO 实现。
无论硬件 SPI 还是软件 SPI,驱动部分核心都可划分成:初始化部分、写字节、读字节。
3.2 硬件 SPI 初始化
基本流程为:
- 使能 SPI,初始化 GPIO,打开 AF 时钟,打开 SPI 时钟;
- 处理重映射相关 GPIO(封装时考虑用弱函数处理这部分);
- 复位 SPI,初始化 SPI 参数,使能 SPI。
芯片固件中的 SPI 初始化一般基于**「结构体 + 初始化函数」**,通常需要配置以下参数:
① 传输模式:用于 MCU 进一步裁剪 SPI 通信模式,常见模式如下:
| 模式 | 说明 |
|---|---|
| 全双工 | 同时收发(最常用) |
| 全双工只接收 | 仍需初始化 MOSI,只是硬件上不再发出数据 |
| 半双工双向只接收 / 只发送 | 数据线只有一根 SDI 或 SDO,需将 MOSI/MISO 中一个引脚配置为数据线,另一个引脚释放(设为 GPIO 或悬空),具体取决于芯片手册 |
② 主从模式:确认通信为主机还是从机。
③ 帧宽度:确定一帧的宽度,通常设置为 8bit;对于某些大数据量场景(如读取外置 Flash),可配置为 16bit。
④ 时钟极性与时钟相位(CPOL/CPHA):确定空闲时时钟线的电平,并决定在第几个边沿采样。
⑤ 时钟分频系数:与 IIC 直接配置速率不同,SPI 通过时钟分频来确定 clk 频率,且分频系数最小为 2(因为数据传输时其他状态机需要时间吞吐数据)。
⑥ 片选类型:通常有软件片选与硬件片选。作为主机收发时通常使用软件片选,作为从机接收时使用硬件片选。
⑦ M/LSB:决定字节发送时 bit 序号,一般用 MSB,且主从机需要一致。
3.3 软件 SPI 初始化
软件 SPI 的初始化更简洁,只需初始化对应的 GPIO:
- CS、MOSI、SCK:初始化成推挽输出,其中 CS 需要先输出为高;
- MISO:配置为浮空输入。
3.4 附:MCU 外设 API 设计思路
MCU 外设的 API 设计基本只看 2 个维度:
- 配置复杂度(参数多不多);
- 是否"运行态状态机"。
基于这两个维度,常见外设的 API 风格推荐如下:
| 外设类型 | 推荐 API 风格 | 原因 |
|---|---|---|
| GPIO | 函数式 | 配置极少 |
| UART | 函数 / 结构体混合 | 中等复杂 |
| SPI | 结构体 | 参数组合多 |
| I2C | 函数式 | 状态机驱动 |
| ADC | 结构体 | 多通道 + 采样配置复杂 |
| DAC | 函数式 | 配置简单 |
| TIMER | 结构体 | 模式复杂(PWM / 输入捕获) |
| DMA | 结构体 | 描述符复杂 |
| CAN | 结构体 | 报文 + 过滤器复杂 |
| USB | 高度结构体 | 协议栈复杂 |
| WDT | 函数式 | 极简单 |
| EXTI | 函数式 | 配置少 |
总结
本文从三个维度系统地梳理了 SPI 总线:
- 通信时序:理解 SPI「主机发时钟、CS 选设备、边发边收」的核心逻辑,掌握 CPOL/CPHA 四种工作模式、M/LSB 配置,以及建立时间与保持时间对采样稳定性的影响;
- 电气特性:SPI 是推挽高速单端信号,重点关注走线长度、信号完整性、MISO 三态、CS 上拉、共地与电平匹配等硬件设计要点;
- 驱动开发:掌握硬件 SPI 的初始化流程与参数结构体配置(传输模式、帧宽度、分频系数、片选类型等)、软件 SPI 的 GPIO 初始化,以及外设 API 设计的一般思路。
掌握以上内容,基本可以应对绝大多数 SPI 相关的开发与调试场景,也能在面试中从容应对各种追问。希望本文对你有帮助,欢迎交流讨论!