摘要:UART 接收数据时,莫名其妙整帧丢包或后半段乱码?不是波特率错了,而是 ORE(Overrun Error)或 DMA 覆盖导致的。本文解析两个最容易被忽略的标志位。


一、问题描述(现象)

**MCU 接收一帧 100 字节的数据,只收到前 80 个;

或者 DMA 接收缓冲区里,后半段全是 0xFF。**

很多工程师的排查方向是:

  1. 波特率不匹配?

  2. 中断里代码太多?

  3. 发送端发送太快?


二、原理分析

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 中断​ 接收不定长帧。

配置步骤:

  1. 开启 UART 全局中断。

  2. 开启 IDLE 中断。

  3. 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 稳得多)。


四、选型避坑建议

  1. DMA 缓冲区:至少是最大帧长的 2 倍。

  2. ORE 标志:只要用了 DMA,就必须处理 ORE。

  3. 波特率误差:> 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 明明收到数据,程序却没反应”折磨过,欢迎在评论区交流。

原创文章,转载请注明出处。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐