STM32L010F4 DMA接收UART数据首次正常后续顺序异常问题
STM32L010F4 UART DMA接收后续数据顺序错位问题解决
你的问题是首次接收6字节数据包(SOF+4字节数据+EOF)完全正常,但后续接收时数据顺序变成EOF在前,接着是SOF和4字节数据。结合代码和现象,核心原因是:
当你发送ACK的过程中,对方设备已经发送了下一包的部分字节(比如EOF),这些字节被UART的接收寄存器(DR)或FIFO缓存,但此时DMA未运行,无法将数据搬运到内存缓冲区。等ACK发送完成、重新启动DMA接收时,DMA会先读取缓存里的残留EOF,再接收后续的SOF和4字节,最终导致整个数据包顺序错位。
另外代码里还有两处潜在问题:主循环轮询启动DMA的方式不够严谨,且启动DMA前未清理UART接收路径的残留数据。
解决方案
1. 启动DMA前强制清理UART接收缓存
新增一个清理接收缓存的函数,在启动DMA前调用,确保接收路径没有残留数据:
// 清理UART接收缓存,丢弃残留字节并清除错误标志 void UART_Clear_RX_Buffer(UART_HandleTypeDef *huart) { uint8_t dummy; // 循环读取DR寄存器,直到RXNE标志位被清除 while(__HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE) != RESET) { dummy = huart->Instance->DR; } // 清除帧错误、噪声错误、溢出错误标志 __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE | UART_FLAG_NE | UART_FLAG_ORE); }
然后修改主循环里的DMA启动逻辑:
while (1) { if(RX_Data_Rdy != 1) { UART_Clear_RX_Buffer(&huart2); HAL_UART_Receive_DMA (&huart2, RX_Data, 6); // 可选:检查DMA启动是否成功 // if(HAL_UART_Receive_DMA (&huart2, RX_Data, 6) != HAL_OK) { /* 处理错误 */ } } if(TX_Data_Pending) { HAL_UART_Transmit_DMA(&hlpuart1, ACK_Data, 3); TX_Data_Pending = 0; // 启动发送后立即置0,避免重复调用 } }
2. 优化回调逻辑,在ACK发送完成后直接启动DMA
把DMA接收的启动从主循环轮询改到ACK发送完成的回调里,减少轮询带来的不确定性,同时加入缓存清理:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { // 确认是LPUART1的发送完成回调(即ACK发送完毕) if(huart->Instance == LPUART1) { TX_Data_Pending = 0; RX_Data_Rdy = 0; memset(RX_Data, 0, 6); // 清理缓存后直接启动下一次DMA接收 UART_Clear_RX_Buffer(&huart2); HAL_UART_Receive_DMA (&huart2, RX_Data, 6); } }
对应的主循环可以简化为:
while (1) { if(TX_Data_Pending) { HAL_UART_Transmit_DMA(&hlpuart1, ACK_Data, 3); TX_Data_Pending = 0; } }
额外注意事项
- 检查UART初始化配置,确保接收FIFO(如果芯片支持)的阈值设置合理,避免数据残留
- 尽量协调对方设备的发送时序,让其在收到ACK后再发送下一包,从源头减少时序冲突
- 调用DMA相关函数时建议检查返回值,避免重复启动或异常情况未被处理
内容的提问来源于stack exchange,提问作者nobby
相关产品推荐
相关产品推荐

