STM32-SPI
一、SPI 是什么
SPI 的全称是 Serial Peripheral Interface,即串行外设接口。
它是一种:
-
高速通信总线
-
全双工通信总线
-
同步通信总线
-
主从式通信总线
SPI 常用于连接这些外设:
-
存储芯片,例如 NOR Flash
-
AD/DA 转换器
-
LCD 显示屏
-
传感器
-
射频模块
-
触摸控制器
与 IIC 相比,SPI 通常速度更快,通信时序也更直接;代价是引脚占用更多,而且一般需要为每个从机准备独立片选信号。
二、SPI 相关引脚
SPI 常见四根信号线如下:
| 引脚 | 全称 | 方向 | 作用 |
|---|---|---|---|
| MOSI | Master Out Slave In | 主机输出,从机输入 | 主机向从机发送数据 |
| MISO | Master In Slave Out | 主机输入,从机输出 | 从机向主机发送数据 |
| SCK | Serial Clock | 主机输出 | SPI 时钟信号 |
| NSS / CS | Slave Select / Chip Select | 主机输出 | 选择目标从机 |

SPI 通信中,主机负责产生时钟 SCK。没有 SCK 时钟,从机不会主动完成数据移位。
NSS/CS 用于选择从机。通常:
NSS/CS 拉低 -> 选中从机
NSS/CS 拉高 -> 释放从机
SPI 没有像 IIC 那样统一的设备地址机制,常见做法是通过片选线区分不同设备。
三、SPI 的全双工特性
SPI 的 MOSI 和 MISO 是两根独立数据线,因此可以同时发送和接收数据。
每来一个 SCK 时钟:
主机通过 MOSI 移出 1 bit
从机通过 MISO 移出 1 bit
主机和从机同时完成 1 bit 数据交换
这也是为什么 HAL_SPI_TransmitReceive() 很常用:SPI 的发送和接收天然是绑定在时钟移位过程中的。
即使主机只是想读从机数据,也通常需要发送空字节来提供时钟,例如发送 0xFF,从机再在 MISO 上返回有效数据。
四、SPI 结构框图理解
SPI 的数据发送和接收主要和以下模块有关:
-
数据寄存器
-
发送缓冲区
-
接收缓冲区
-
移位寄存器
-
MOSI/MISO/SCK/NSS 引脚
1. 发送过程
发送数据时,CPU 或 HAL 函数先把数据写入 SPI 数据寄存器。
数据会进入发送缓冲区,再通过移位寄存器按位从 MOSI 输出。
简化流程:
CPU/HAL 写 SPI_DR
-> 发送缓冲区
-> 移位寄存器
-> MOSI 引脚输出
2. 接收过程
接收数据时,MISO 上的数据会随着 SCK 时钟逐位进入移位寄存器,然后进入接收缓冲区。
CPU 或 HAL 函数再从 SPI 数据寄存器读取结果。
简化流程:
MISO 输入
-> 移位寄存器
-> 接收缓冲区
-> CPU/HAL 读取 SPI_DR
3. SPI 时钟信号
SPI 时钟由主机产生。
在 STM32 中,SPI 时钟通常通过 SPI_CR1 中的波特率控制位配置。
主机通过调整 SPI 分频系数控制 SCK 频率。从机能否正常通信,取决于它是否支持当前 SCK 频率。
五、SPI 主控制逻辑
SPI 的主控制逻辑主要涉及:
| 寄存器 | 作用 |
|---|---|
SPI_CR1 |
配置 SPI 主要工作参数 |
SPI_CR2 |
配置中断、DMA 等扩展控制 |
SPI_SR |
查看 SPI 当前状态 |
SPI_DR |
存放待发送数据或接收数据 |
在 STM32F1 中,常见有 3 个 SPI 外设;在部分 F4/F7/H7 系列中,SPI 外设数量更多。实际使用时要以具体芯片手册为准。
六、SPI 工作模式:CPOL 和 CPHA
SPI 的工作模式由两个参数决定:
-
CPOL:时钟极性
-
CPHA:时钟相位
1. CPOL 时钟极性
CPOL 决定没有数据传输时,SCK 时钟线的空闲电平。
| CPOL | SCK 空闲状态 |
|---|---|
| 0 | 低电平 |
| 1 | 高电平 |
2. CPHA 时钟相位
CPHA 决定在第几个时钟边沿采样数据。
| CPHA | 采样边沿 |
|---|---|
| 0 | 第 1 个边沿采样 |
| 1 | 第 2 个边沿采样 |
这里的“第 1 个边沿”和“第 2 个边沿”要结合 CPOL 理解。
例如:
-
CPOL = 0 时,空闲为低电平,第 1 个边沿是上升沿。
-
CPOL = 1 时,空闲为高电平,第 1 个边沿是下降沿。
3. 四种 SPI 模式
SPI 常见四种模式:
| 模式 | CPOL | CPHA |
|---|---|---|
| Mode 0 | 0 | 0 |
| Mode 1 | 0 | 1 |
| Mode 2 | 1 | 0 |
| Mode 3 | 1 | 1 |
主机和从机必须使用相同的 SPI 模式,否则会出现数据错位、读写异常等问题。
很多 SPI Flash 支持 Mode 0 和 Mode 3。使用前要查器件手册,确认它支持哪种模式。
七、SPI 数据帧与传输顺序
SPI 数据帧长度可以配置,常见为 8 位。
数据传输顺序也可以配置:
| 传输顺序 | 含义 |
|---|---|
| MSB first | 先发送最高位 |
| LSB first | 先发送最低位 |
大多数外设使用 MSB first,也就是先发送高位。
例如发送 0xA5:
0xA5 = 1010 0101
MSB first: 1 -> 0 -> 1 -> 0 -> 0 -> 1 -> 0 -> 1
主从双方的数据帧长度和传输顺序必须一致。
八、SPI_CR1 控制寄存器
SPI_CR1 是 SPI 的核心控制寄存器,用于配置主要工作参数。
常见配置项包括:
| 配置项 | 说明 |
|---|---|
| SPI 模式 | 主机模式或从机模式 |
| 通信方向 | 全双工、单线、只收等 |
| NSS 管理 | 硬件 NSS 或软件 NSS |
| 数据帧格式 | 8 位或 16 位等 |
| CPOL | 时钟极性 |
| CPHA | 时钟相位 |
| 波特率分频 | 控制 SCK 时钟频率 |
| 帧格式 | MSB first 或 LSB first |
在 HAL 库中,这些配置通常通过 SPI_InitTypeDef 结构体完成。
九、SPI_SR 状态寄存器
SPI_SR 用于查询 SPI 当前传输状态。
常见状态位包括:
| 标志位 | 含义 |
|---|---|
| TXE | 发送缓冲区空 |
| RXNE | 接收缓冲区非空 |
1. TXE
TXE = 1 表示发送缓冲区为空,可以继续写入新的待发送数据。
轮询发送时,常见逻辑是:
等待 TXE = 1
写入 SPI_DR
2. RXNE
RXNE = 1 表示接收缓冲区中已有接收数据,可以读取。
轮询接收时,常见逻辑是:
等待 RXNE = 1
读取 SPI_DR
注意,有些资料或代码中会误写成 RXE,实际常用标志位名称是 RXNE。
十、SPI_DR 数据寄存器
SPI_DR 用于存放待发送或接收的数据。
从软件角度看,它像是一个数据入口和出口:
写 SPI_DR -> 启动发送
读 SPI_DR -> 获取接收数据
但硬件内部通常包含发送缓冲区和接收缓冲区,因此读写 SPI_DR 实际会影响不同的数据路径。
使用 SPI 时要记住:发送和接收是移位同步发生的。发送一个字节的同时,硬件也可能接收到一个字节。
十一、H7 系列 SPI 寄存器差异
在 H7 系列中,SPI 寄存器组织和 F1/F4/F7 有明显差异。
常见寄存器包括:
| 寄存器 | 作用 |
|---|---|
SPI_CFG1 |
配置 SPI 传输速率和帧格式 |
SPI_CFG2 |
配置 SPI 工作模式、NSS 引脚、传输顺序 |
RCC_D2CCIP1R |
SPI 时钟源选择 |
SPI_CR1 |
使能 SPI 等控制 |
SPI_I2SCFGR |
选择 SPI/I2S 模式 |
SPI_SR |
查看传输状态 |
SPI_TXDR |
发送数据寄存器,与 TxFIFO 连接 |
SPI_RXDR |
接收数据寄存器,与 RxFIFO 连接 |
如果使用 H7,不要直接照搬 F1 的寄存器理解。HAL 库能屏蔽一部分差异,但底层排查问题时仍然要看对应系列参考手册。
十二、SPI 相关 HAL 函数
1. 使能 SPI 时钟
使用 SPI 前,需要先使能对应外设时钟:
__HAL_RCC_SPIx_CLK_ENABLE();
例如使用 SPI2,就要使能 SPI2 时钟。
2. 初始化 SPI
调用:
HAL_SPI_Init();
该函数根据 SPI_HandleTypeDef 中的初始化参数配置 SPI。
3. MSP 初始化回调
调用或重写:
HAL_SPI_MspInit();
MSP 初始化通常用于配置:
-
SPI 外设时钟
-
SCK/MOSI/MISO/NSS 对应 GPIO
-
GPIO 复用推挽输出
-
中断或 DMA 相关资源
4. SPI 发送
阻塞发送函数:
HAL_SPI_Transmit();
适合只需要向从机发送数据的场景,例如发送命令、地址或配置字。
5. SPI 接收
阻塞接收函数:
HAL_SPI_Receive();
SPI 接收时通常仍然需要主机产生时钟,因此底层可能会发送空数据来换取接收数据。
6. SPI 发送接收
全双工收发函数:
HAL_SPI_TransmitReceive();
它适合同时发送和接收,例如主机发送命令字节,同时读取从机返回值。
7. 使能和失能 SPI
常用宏:
__HAL_SPI_ENABLE();
__HAL_SPI_DISABLE();
它们用于使能或关闭 SPI 外设。
十三、SPI 相关结构体
HAL 库中常见 SPI 结构体包括:
| 结构体 | 作用 |
|---|---|
SPI_HandleTypeDef |
SPI 句柄,保存 SPI 实例、初始化参数和状态 |
SPI_InitTypeDef |
SPI 初始化结构体,与 SPI 主要配置参数相关 |
SPI_InitTypeDef 中常见字段包括:
-
工作模式:主机或从机
-
通信方向
-
数据帧大小
-
CPOL
-
CPHA
-
NSS 管理方式
-
波特率分频
-
传输顺序
配置 SPI 时,最容易出错的是 CPOL、CPHA、NSS 和分频系数。遇到通信异常时,优先检查这几项。
十四、SPI 基本配置流程
使用 HAL 库配置 SPI,可以按以下步骤理解。
1. 配置 SPI 工作参数
先确定:
-
主机还是从机
-
全双工还是只收/只发
-
数据帧长度
-
CPOL / CPHA
-
NSS 管理方式
-
波特率分频
-
MSB first 还是 LSB first
然后调用:
HAL_SPI_Init();
2. 使能 SPI 时钟并初始化 GPIO
在 HAL_SPI_MspInit() 中完成:
-
使能 SPI 外设时钟
-
使能 GPIO 时钟
-
配置 SCK、MOSI、MISO、NSS 引脚
SPI 引脚常见配置为复用推挽输出模式。
3. 使能 SPI 外设
调用:
__HAL_SPI_ENABLE();
4. SPI 数据传输
根据需求选择:
HAL_SPI_Transmit();
HAL_SPI_Receive();
HAL_SPI_TransmitReceive();
5. 设置 SPI 传输速度
SPI 传输速度由波特率分频控制。
在 STM32 中通常通过 SPI_CR1 中的波特率控制位配置,HAL 中则通过初始化结构体里的分频字段设置。
速度设置要满足两个条件:
-
不超过从机最大支持频率。
-
不超过板级连线和信号质量能稳定支持的频率。
更多推荐




所有评论(0)