4、串口DMA接收原理:RXNE标志与DMA请求的关系,接收溢出与处理策略
在串口DMA接收系统中,理解硬件层面的标志位与DMA请求之间的协同关系,是设计可靠双缓冲机制的前提。本章深入剖析RXNE(Receive Not Empty)标志如何触发DMA请求、数据如何从移位寄存器搬运到内存,以及当数据到达速度超过DMA处理能力时,如何检测并恢复溢出错误。
4.1 RXNE标志与DMA请求的硬件联动
USART外设内部包含一个接收移位寄存器(RDR)和一个接收数据寄存器(DR)。当一帧数据(通常为8位或9位)在移位寄存器中完成同步采样后,硬件自动将数据并行加载到DR中,同时置位RXNE标志。
RXNE标志的置位会触发两种行为:
- 中断请求:若USART_CR1寄存器中的RXNEIE位被使能,则产生USART中断,CPU需在中断服务程序中读取DR以清除RXNE。
- DMA请求:若USART_CR3寄存器中的DMAR(DMA接收使能)位被置1,则RXNE置位会向DMA控制器发送一个单次传输请求。
关键概念:RXNE标志是USART外设向DMA控制器发出的“数据已就绪”信号。DMA控制器在响应此请求后,执行一次从USART_DR寄存器到内存地址的搬运,搬运完成后DMA自动清除RXNE标志(无需软件干预)。
下图展示了从串行数据到达至DMA搬运完成的完整硬件流水线:
4.2 DMA请求的触发条件与传输粒度
DMA请求并非持续有效,而是遵循严格的触发-响应-清除周期:
| 阶段 | USART状态 | DMA请求线状态 | 说明 |
|---|---|---|---|
| 空闲 | RXNE = 0,DR为空 | 无效(低电平) | 无数据到达,DMA不动作 |
| 数据到达 | RXNE = 1,DR有数据 | 有效(高电平) | DMA控制器检测到请求,准备传输 |
| DMA响应 | DMA读取DR,RXNE自动清零 | 变为无效 | 单次传输完成,等待下一帧 |
| 溢出风险 | RXNE仍为1,新数据到达 | 请求持续有效但数据被覆盖 | ORE标志置位,需软件处理 |
工程经验:DMA传输的粒度是单字节。每收到一个字节,DMA就搬运一个字节。这与DMA的“块传输”或“突发传输”模式不同——串口DMA接收天然是单次请求-单次响应模式,直到DMA传输计数器(CNDTR)递减至0时才会触发传输完成中断。
4.3 接收溢出(ORE)的产生机制
溢出错误(OverRun Error,ORE)是串口DMA接收中最常见且最隐蔽的错误之一。其产生条件为:
- RXNE = 1:DR中已有数据等待DMA搬运。
- 新帧到达:移位寄存器完成下一帧数据的采样。
- 硬件冲突:新数据无法加载到DR(因为DR尚未被读取),导致移位寄存器中的数据被覆盖。
此时,USART_SR寄存器中的ORE标志位被硬件置位。如果ORE中断被使能(USART_CR3中的EIE位),则会触发中断;否则,错误仅体现在标志位上。
注意:ORE标志置位后,USART外设会停止接收后续数据,直到软件清除ORE标志。这意味着如果溢出处理不及时,后续所有数据都将丢失。在DMA模式下,DMA请求也会因RXNE无法被清除而停止。
下图展示了溢出发生的时序:
4.4 溢出处理策略
针对DMA接收中的ORE错误,嵌入式系统通常采用以下三种策略:
| 策略 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 中断轮询清除 | 使能USART错误中断(EIE),在中断中读取SR和DR清除ORE | 响应快,错误恢复及时 | 增加中断开销,可能嵌套 |
| DMA循环模式+软件检测 | DMA配置为循环模式,软件通过比较缓冲区写入位置检测溢出 | 无需中断,CPU负载低 | 无法检测单次溢出,仅能发现连续丢失 |
| 双缓冲乒乓切换 | 两个缓冲区交替使用,DMA传输完成中断中切换目标地址 | 从根本上避免溢出(缓冲区始终有空位) | 需要合理配置缓冲区大小和切换频率 |
推荐做法:在乒乓操作设计中,建议同时使能USART的错误中断(EIE)作为安全网。即使双缓冲机制因突发大数据量而出现短暂的DMA响应延迟,ORE中断也能及时介入,清除错误并重新同步,避免数据流永久中断。
4.5 ORE清除的软件流程
当检测到ORE标志时,正确的清除序列为:
- 读取USART_SR寄存器:获取当前状态,确认ORE位为1。
- 读取USART_DR寄存器:读取DR中的数据(即使已无效),此操作会硬件清除ORE标志。
- 重新使能DMA:如果DMA通道因溢出而暂停,需重新配置DMA的CNDTR寄存器并重新使能通道。
- 重置缓冲区指针:根据设计,可能需要丢弃当前缓冲区中的部分数据,重置写入索引。
// HAL库中ORE中断处理示例
void USART1_IRQHandler(void)
{
uint32_t isrflags = READ_REG(huart1.Instance->SR);
// 检查溢出错误
if ((isrflags & USART_SR_ORE) != RESET)
{
// 步骤1:读取SR(已读取)
// 步骤2:读取DR以清除ORE
volatile uint32_t tmp = READ_REG(huart1.Instance->DR);
(void)tmp; // 防止编译器警告
// 步骤3:重新启动DMA接收
HAL_UART_AbortReceive(&huart1);
HAL_UART_Receive_DMA(&huart1, g_dma_rx_buf, BUF_SIZE);
// 步骤4:记录错误计数
g_overflow_count++;
// 可选:通知上层协议栈
// protocol_notify_overflow();
}
// 其他中断处理...
}
优化技巧:在高速通信(如1Mbps以上)中,建议将ORE中断优先级设置为高于DMA传输完成中断。这样即使DMA正在处理传输完成回调,ORE中断也能立即抢占并清除错误,防止连续溢出导致数据雪崩。
4.6 乒乓操作中的溢出防护设计
在双缓冲乒乓架构中,溢出防护的核心思想是:确保DMA始终有一个空的缓冲区可供写入。具体措施包括:
- 缓冲区大小匹配:单个缓冲区容量应大于最大预期帧长,通常设置为最大帧长的2倍。
- 切换时机优化:在DMA传输完成一半(HT)和完全完成(TC)时均触发中断,实现更细粒度的切换控制。
- 看门狗计数器:在DMA传输完成中断中检查时间戳,若两次中断间隔超过阈值,则认为可能发生溢出,主动复位DMA通道。
下图展示了乒乓操作中防止溢出的缓冲区切换逻辑:
常见陷阱:不要依赖DMA的“自动循环模式”来防止溢出。循环模式虽然会自动重置地址,但如果应用程序处理速度跟不上DMA写入速度,旧数据会被新数据覆盖,且没有任何错误标志。乒乓操作配合显式的缓冲区切换和状态检查,才是可靠的溢出防护方案。