UART 接收丢数据?别只查中断,看看 ORE 与 IDLE
摘要:UART 接收数据时,莫名其妙整帧丢包或后半段乱码?不是波特率错了,而是 ORE(Overrun Error)或 DMA 覆盖导致的。本文解析两个最容易被忽略的标志位。
一、问题描述(现象)
**MCU 接收一帧 100 字节的数据,只收到前 80 个;
或者 DMA 接收缓冲区里,后半段全是 0xFF。**
很多工程师的排查方向是:
-
波特率不匹配?
-
中断里代码太多?
-
发送端发送太快?
二、原理分析
1. 物理模型
UART 接收是一个被动过程。
发送端 -> TX -> RX -> [Shift Register] -> [RDR] -> DMA/CPU
2. 核心参数
UART 有两个“沉默杀手”:
|
标志位 |
全称 |
触发条件 |
|---|---|---|
|
ORE |
Overrun Error |
上一字节还没读走,下一字节又来了 |
|
IDLE |
Idle Line |
总线空闲(常用于判断一帧结束) |
3. 反直觉真相
DMA 开着,不代表数据不会丢。
当 ORE 发生时:
-
UART 硬件会丢弃新来的数据。
-
但 DMA 并不知道,它还在按节奏搬运。
-
结果:Buffer 里出现空洞或错位。
三、工程级解决方案
方案 1:必须清 ORE 标志(最容易被忘)
在中断里,不仅要读数据,还要清错误标志。
void USART_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart, UART_FLAG_ORE)) {
__HAL_UART_CLEAR_OREFLAG(&huart); // 必须清!
uint8_t dummy = huart.Instance->RDR; // 读一下寄存器
}
if (__HAL_UART_GET_FLAG(&huart, UART_FLAG_RXNE)) {
uint8_t data = huart.Instance->RDR;
// 处理数据
}
}
方案 2:DMA + IDLE 中断(推荐)
不要用 DMA 接收固定长度,用 IDLE 中断 接收不定长帧。
配置步骤:
-
开启 UART 全局中断。
-
开启 IDLE 中断。
-
DMA 设置为 Circular 模式。
// 伪代码
HAL_UART_Receive_DMA(&huart, rx_buf, BUF_SIZE);
__HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE);
中断处理:
void USART_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart);
// 计算收到的数据长度
// 处理 rx_buf
// 重启 DMA
}
}
方案 3:降低发送端的压力
如果发送端是 PC 或 Linux:
-
不要连续狂发。
-
每发一帧,等待 ACK。
-
或者降低波特率(115200 比 921600 稳得多)。
四、选型避坑建议
-
DMA 缓冲区:至少是最大帧长的 2 倍。
-
ORE 标志:只要用了 DMA,就必须处理 ORE。
-
波特率误差:> 3% 必出问题,检查晶振(HSI 慎用)。
五、总结 Checklist
-
[ ] 是否开启了 ORE 错误检测?
-
[ ] 是否清除了 ORE 标志位?
-
[ ] 是否使用了 IDLE 中断接收不定长数据?
-
[ ] DMA 缓冲区是否足够大?
-
[ ] 波特率误差是否 < 2%?
💡 注意区分:本文讨论的是 接收端丢包与硬件错误。
若你的问题是 RS485 通讯失败,请检查收发切换延时,详见《RS485 通讯总崩?别只怪线》。
References
-
STM32 RM0008 – USART functional description
-
STM32 Application Note AN3109 – Communication peripheral FIFO emulation
如果你曾被“UART 明明收到数据,程序却没反应”折磨过,欢迎在评论区交流。
原创文章,转载请注明出处。
更多推荐


所有评论(0)