一、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 中则通过初始化结构体里的分频字段设置。

速度设置要满足两个条件:

  1. 不超过从机最大支持频率。

  2. 不超过板级连线和信号质量能稳定支持的频率。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐