3. SPI 硬件控制器:寄存器映射与核心寄存器详解

在嵌入式系统中,SPI 外设的硬件控制完全通过一组内存映射寄存器实现。以典型的 STM32 MCU 为例,其 SPI 外设被映射到特定的地址空间,CPU 通过读写这些寄存器来配置模式、检查状态和收发数据。理解寄存器映射和每个控制/状态位的含义,是编写高效 SPI 驱动的基础。

3.1 SPI 外设寄存器映射概览

STM32 的 SPI 外设(如 SPI1、SPI2)挂载在高级高性能总线(AHB)或外设总线(APB)上,每个外设占用 1KB 的地址空间。以下为典型的 SPI 寄存器映射表(以 SPI1 为例,基地址 0x40013000):

偏移地址 寄存器名称 缩写 复位值 功能描述
0x00 控制寄存器 1 CR1 0x0000 SPI 使能、波特率、时钟极性和相位、主从模式等
0x04 控制寄存器 2 CR2 0x0000 中断使能、DMA 使能、帧格式、SS 输出使能等
0x08 状态寄存器 SR 0x0002 忙标志、发送缓冲区空、接收缓冲区非空、模式错误等
0x0C 数据寄存器 DR 0x0000 发送/接收数据缓冲区(双缓冲)
0x10 CRC 多项式寄存器 CRCPR 0x0007 CRC 计算多项式系数
0x14 RX CRC 寄存器 RXCRCR 0x0000 接收 CRC 值
0x18 TX CRC 寄存器 TXCRCR 0x0000 发送 CRC 值
0x1C I2S 配置寄存器 I2SCFGR 0x0000 I2S 模式配置(仅 SPI/I2S 复用外设)
0x20 I2S 预分频寄存器 I2SPR 0x0002 I2S 时钟分频
关键提示: 在驱动开发中,CR1、CR2、SR 和 DR 是最常操作的四个寄存器。CRC 相关寄存器通常在需要高可靠性通信(如 Flash 编程)时使用。I2S 寄存器仅在支持音频的外设中有效。

3.2 控制寄存器 1 (CR1) 详解

CR1 是 SPI 外设的核心配置寄存器,决定了通信的基本参数。其位定义如下:

位段 名称 类型 描述
15 BIDIMODE RW 双向数据模式使能:0=双线单向,1=单线双向
14 BIDIOE RW 双向模式下输出使能:0=接收,1=发送
13 CRCEN RW 硬件 CRC 校验使能
12 CRCNEXT RW 下一个数据为 CRC 值
11 DFF RW 数据帧格式:0=8 位,1=16 位
10 RXONLY RW 只接收模式:0=全双工,1=只接收
9 SSM RW 软件从设备管理使能:0=硬件 NSS,1=软件 NSS
8 SSI RW 内部从设备选择位(配合 SSM 使用)
7 LSBFIRST RW 帧格式:0=MSB 先行,1=LSB 先行
6 SPE RW SPI 外设使能:1=使能,0=禁止
5:3 BR[2:0] RW 波特率控制:000=fPCLK/2,001=fPCLK/4,...,111=fPCLK/256
2 MSTR RW 主从选择:1=主设备,0=从设备
1 CPOL RW 时钟极性:0=空闲低电平,1=空闲高电平
0 CPHA RW 时钟相位:0=第一个边沿采样,1=第二个边沿采样
配置示例: 设置 SPI1 为主设备、8 位数据、CPOL=0、CPHA=0、波特率 fPCLK/8:
SPI1->CR1 = (0x2 << 3) | (1 << 2) | (0 << 1) | (0 << 0); // BR=010, MSTR=1, CPOL=0, CPHA=0
SPI1->CR1 |= (1 << 6); // 使能 SPE

3.3 控制寄存器 2 (CR2) 详解

CR2 主要负责中断控制、DMA 传输和帧格式扩展:

位段 名称 类型 描述
15 RXDMAEN RW 接收缓冲区 DMA 使能:1=使能 DMA 请求
14 TXDMAEN RW 发送缓冲区 DMA 使能:1=使能 DMA 请求
13 SSOE RW SS 输出使能:主模式下自动控制 NSS 引脚
12 FRF RW 帧格式:0=Motorola SPI,1=TI 同步串行
11 ERRIE RW 错误中断使能(CRC、模式、溢出、帧错误)
10 RXNEIE RW 接收缓冲区非空中断使能
9 TXEIE RW 发送缓冲区空中断使能
8 DS[3:0] RW 数据大小(STM32F4/F7 系列):0000=保留,0001=4 位,...,1111=16 位
7:0 保留 - 保留位,必须保持复位值
避坑指南: 在配置 DMA 传输时,必须同时设置 CR2 中的 RXDMAEN 和 TXDMAEN。如果只使能其中一个,会导致 DMA 传输卡死或数据错位。另外,DS 位段(数据大小)在较新系列中用于支持 4-16 位可变帧长,旧系列使用 CR1 的 DFF 位仅支持 8/16 位。

3.4 状态寄存器 (SR) 详解

SR 是驱动轮询或中断查询的关键寄存器,反映外设当前工作状态:

位段 名称 类型 描述
7 BSY R 忙标志:1=正在通信或发送缓冲区非空,0=空闲
6 OVR R 溢出错误:1=接收溢出,需软件清零
5 MODF R 模式错误:1=多主冲突,需软件清零
4 CRCERR R CRC 错误:1=接收 CRC 不匹配,需软件清零
3 UDR R 欠载错误(I2S 模式)
2 CHSIDE R 声道侧(I2S 模式)
1 TXE R 发送缓冲区空:1=可以写入新数据,0=数据未发送完
0 RXNE R 接收缓冲区非空:1=有数据可读,0=无数据
轮询发送/接收流程:
// 发送一个字节
while (!(SPI1->SR & SPI_SR_TXE)); // 等待 TXE=1
SPI1->DR = data;

// 接收一个字节
while (!(SPI1->SR & SPI_SR_RXNE)); // 等待 RXNE=1
received = SPI1->DR;

3.5 数据寄存器 (DR) 与双缓冲机制

DR 是 SPI 外设与 CPU 或 DMA 交换数据的窗口。STM32 的 SPI 采用双缓冲结构:

  • 发送路径: CPU/DMA 写入 DR → 发送缓冲区(TX Buffer)→ 移位寄存器 → MOSI 引脚逐位输出。
  • 接收路径: MISO 引脚逐位输入 → 移位寄存器 → 接收缓冲区(RX Buffer)→ CPU/DMA 读取 DR。

双缓冲的优势在于:当移位寄存器正在处理当前数据时,CPU 可以提前写入下一个数据或读取上一个已接收的数据,从而减少等待时间。

性能要点: 在高速 SPI 通信中,DR 的访问速度直接影响吞吐量。使用 DMA 传输时,DMA 控制器直接读写 DR,无需 CPU 干预,可以最大化利用双缓冲的并行能力。DR 的位宽(8 位或 16 位)由 CR1 的 DFF 或 CR2 的 DS 位段决定。

3.6 SPI 寄存器操作流程 SVG 图

下图展示了 SPI 外设寄存器之间的数据流和控制关系:

SPI 外设寄存器操作与数据流图 控制路径 CR1 (控制寄存器1) CR2 (控制寄存器2) 配置模式/中断/DMA 状态路径 SR (状态寄存器) TXE / RXNE / BSY 轮询/中断触发条件 数据路径 DR (数据寄存器) TX Buffer / RX Buffer CPU/DMA 读写接口 配置影响 状态控制 DMA 使能控制 SPI 移位寄存器与引脚 (MOSI/MISO/SCK/NSS) 数据串行化 外部 SPI 从设备 物理连接 操作流程说明 ① 配置 CR1/CR2 设定模式与中断/DMA → ② 查询 SR 判断 TXE/RXNE → ③ 读写 DR 完成数据交换 → ④ 移位寄存器自动完成串行收发

3.7 寄存器操作时序与注意事项

在实际驱动开发中,寄存器操作需要遵循特定的时序要求:

  • 使能顺序: 必须先配置 CR1 的所有参数(MSTR、CPOL、CPHA、BR 等),最后再置位 SPE 使能外设。中途修改关键参数时,建议先清零 SPE。
  • DR 访问时序: 写入 DR 前必须确认 TXE=1;读取 DR 前必须确认 RXNE=1。连续写入时,需等待 TXE 再次置位。
  • BSY 标志: 在关闭 SPI 或修改配置前,必须等待 BSY=0,确保当前传输完成。BSY 在双线全双工模式下,从最后一个数据位采样完成后自动清零。
  • 错误处理: OVR、MODF、CRCERR 等错误标志必须由软件通过读取 SR 后写入 DR 或执行特定序列清零,否则外设可能卡死。
常见错误: 初学者容易在未检查 TXE 的情况下连续写入 DR,导致数据覆盖。在高速 SPI 中,建议使用 DMA 或 FIFO(如果硬件支持)来避免此类问题。另外,在从模式下,如果 NSS 信号不稳定,可能导致 MODF 错误,需确保 NSS 引脚电平正确。

3.8 寄存器映射在内存中的布局

在 STM32 的 CMSIS 头文件中,SPI 寄存器被定义为一个结构体,方便通过指针访问:

typedef struct {
  __IO uint16_t CR1;      // 控制寄存器 1,偏移 0x00
  __IO uint16_t CR2;      // 控制寄存器 2,偏移 0x04
  __IO uint16_t SR;       // 状态寄存器,偏移 0x08
  __IO uint16_t DR;       // 数据寄存器,偏移 0x0C
  __IO uint16_t CRCPR;    // CRC 多项式,偏移 0x10
  __IO uint16_t RXCRCR;   // RX CRC,偏移 0x14
  __IO uint16_t TXCRCR;   // TX CRC,偏移 0x18
  __IO uint16_t I2SCFGR;  // I2S 配置,偏移 0x1C
  __IO uint16_t I2SPR;    // I2S 预分频,偏移 0x20
} SPI_TypeDef;

#define SPI1_BASE    0x40013000
#define SPI1         ((SPI_TypeDef *) SPI1_BASE)

这种结构体映射方式使得寄存器访问如同普通变量操作,编译器会自动生成合适的 LDR/STR 指令。在性能调优时,可以进一步使用 __IO 限定词确保每次访问都直接操作外设寄存器,避免编译器优化导致的数据不一致。

内存映射优势: 通过结构体指针访问寄存器,代码可读性高且易于移植。在 DMA 传输中,DMA 控制器直接使用外设地址(如 &SPI1->DR)作为源或目标地址,无需 CPU 介入地址计算。