SPI 通信中,从机发送模式的 “下溢(Underrun)” 是影响数据可靠性的关键问题 —— 当主机时钟已触发,但从机 TXFIFO 无可用数据时,会导致传输异常。STM32H7 系列通过灵活的 UDRDET 配置,支持三种下溢检测时刻,配合 dummy 数据、自定义下溢模式(underrun pattern)机制,可精准适配不同场景需求,核心价值在于 “提前规避异常、明确故障处理、保障传输完整性”。
1. SPI Underrun 是什么
1.1 下溢定义与发生场景
- Underrun(下溢) :SPI 从机工作在发送模式时,主机的 SCK 时钟信号已到来,但从机 TXFIFO 中无待发送数据,导致无法按时输出有效数据的异常状态。
- 核心影响 :未处理的下溢会导致传输数据错乱、主机接收错误,严重时引发通信中断。
- 解决逻辑 :通过硬件检测下溢事件,自动发送预设数据(dummy/pattern),同时触发中断提醒软件处理,避免异常扩散。
1.2 基础铺垫:SPI 时钟与传输逻辑
下溢检测与 SPI 时钟相位 / 极性直接相关,需先明确核心配置(不深入复杂时序,聚焦传输本质):
- CPOL(时钟极性) :SCK 空闲电平(0 = 低电平,1 = 高电平)。
- CPHA(时钟相位) :数据采样 / 移位的时钟边沿(0 = 奇数边沿采样、偶数边沿移位;1 = 奇数边沿移位、偶数边沿采样)。
- 传输关键 :从机需在主机指定的时钟边沿前,将数据写入 TXFIFO,否则触发下溢。
2. 核心配置:UDRDET 三种下溢检测模式详解
STM32H7 通过 SPI_CFG1 寄存器的UDRDET[1:0]字段,配置从机下溢检测时刻,三种模式行为差异显著,需结合场景选型:
| UDRDET 配置 |
检测时刻 |
下溢后行为 |
关键波形特征 |
适用场景 |
| 0(默认) |
新数据帧启动时(主机开始提供该帧时钟) |
1. 首帧下溢:发送 dummy 数据;2. 未清除 UDR 状态位:后续帧发送 underrun pattern |
第二帧触发下溢,第三帧起发 pattern(见图 2) |
对首帧容错要求高、允许短暂无数据的场景(如低速传感器数据传输) |
| 1 |
上一帧传输结束时(TXFIFO 变为空) |
1. 初始 TXFIFO 空(首帧):先送 dummy,后续发 pattern;2. 传输中 TXFIFO 空:直接发 pattern |
首帧空则首帧送 dummy,传输中为空则当前帧后发 pattern(见图 3/4) |
需快速响应下溢、避免多帧错误的场景(如高速数据流式传输) |
| 2 |
SS 信号激活时(SS引脚拉低) |
1. 激活时 TXFIFO 空:立即触发下溢;2. SS 未翻转:每帧后均触发下溢(即使 TXFIFO 非空) |
首帧及后续均发送 pattern(见图 5) |
严格要求无数据时不传输、需快速终止通信的场景(如安全敏感数据传输) |




关键补充:下溢后核心处理机制
- dummy 数据 :硬件默认生成的占位数据,无实际含义,仅用于维持 SPI 时序完整性。
- underrun pattern :可通过 SPI_CFG1 寄存器
UDRCFG字段配置(如全 0、全 1、交替码等),用于主机识别下溢异常。
- 硬件延迟注意 :下溢事件发生后,需经过几个 SPI 时钟周期 才能检测到(或触发中断),软件处理需预留缓冲时间。
3. 实战配置:从 CubeMX 到代码实现
3.1 CubeMX 配置步骤
- 启用 SPI 从机模式,配置 CPOL/CPHA(需与主机一致)。
- 进入 “Configuration”→“SPI Configuration”→“Advanced Parameters”。
- 找到 “Underrun Detection (UDRDET)”,选择对应模式(0/1/2)。
- (可选)配置 underrun pattern(“Underrun Pattern Configuration”)。
- 启用 UDR 中断(如需软件处理下溢):“NVIC Settings”→勾选 “SPIx global interrupt”。
3.2 寄存器配置代码示例(HAL 库)
#include "stm32h7xx_hal.h"
// SPI句柄(需提前初始化SPI从机模式)
extern SPI_HandleTypeDef hspi1;
// 配置UDRDET模式(以UDRDET=1为例)
void SPI_Config_Underrun(uint8_t udr_det_mode) {
// 1. 禁用SPI,避免配置过程中产生传输
__HAL_SPI_DISABLE(&hspi1);
// 2. 配置UDRDET字段(SPI_CFG1寄存器,位[17:16])
MODIFY_REG(hspi1.Instance->CFG1, SPI_CFG1_UDRDET, (udr_det_mode << SPI_CFG1_UDRDET_Pos));
// 3. (可选)配置underrun pattern(UDRCFG字段,位[19:18],示例:全0 pattern)
MODIFY_REG(hspi1.Instance->CFG1, SPI_CFG1_UDRCFG, (0x00 << SPI_CFG1_UDRCFG_Pos));
// 4. 清除之前的UDR状态位(SPI_SR寄存器)
__HAL_SPI_CLEAR_FLAG(&hspi1, SPI_FLAG_UDR);
// 5. 重新启用SPI
__HAL_SPI_ENABLE(&hspi1);
}
// UDR中断回调函数(处理下溢事件)
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI1 && __HAL_SPI_GET_FLAG(hspi, SPI_FLAG_UDR)) {
// 1. 处理下溢逻辑(如补充数据到TXFIFO、终止通信等)
printf("SPI Underrun detected!rn");
// 2. 清除UDR状态位(必须操作,否则后续一直发送pattern)
__HAL_SPI_CLEAR_FLAG(hspi, SPI_FLAG_UDR);
}
}
4. 选型建议与避坑指南
4.1 配置选型建议
- 低速、数据突发传输(如传感器周期性上报):选 UDRDET=0,允许首帧 dummy 数据,容错性更高。
- 高速、连续数据流(如 ADC 采样实时传输):选 UDRDET=1,快速检测传输中 TXFIFO 空,减少错误数据量。
- 高可靠性、无数据时禁止传输(如加密数据通信):选 UDRDET=2,SS 激活即检测,直接发 pattern 提示异常。
4.2 常见问题与规避
- 问题 1:下溢后一直发送 underrun pattern → 未清除 SPI_SR 寄存器的 UDR 状态位,需在中断或主循环中主动清除。
- 问题 2:检测不到下溢中断 → 未启用 SPI 全局中断,或未在 NVIC 中配置中断优先级。
- 问题 3:不同 UDRDET 模式混用导致异常 → 明确场景后固定配置,避免动态切换(如需切换需先禁用 SPI)。
STM32H7 SPI 的 Underrun 特性通过灵活的检测时刻配置和自动容错机制,从硬件层面提升了从机发送的可靠性。核心是根据通信场景选择合适的 UDRDET 模式,配合状态位清除、pattern 配置和中断处理,可彻底规避下溢导致的传输异常。 |