用DMA+空闲中断实现ESP32 UART不定长帧接收时,如何避免半包与粘包

· 行业用户 · 13 浏览

回答(4)

rx_timeout_thresh 别设太小,按波特率算 3~5 字符时间;帧内字符间隔大的协议要单独放宽,否则误判半包。
硬件小李 · 2026-11-08
DMA 接收长度要在中断里原子读取并清零,主循环拷贝用双缓冲,别在中断里做 memcpy,否则容易丢后续帧。
南山码农 · 2026-11-08
空闲中断只适合做粗边界,建议再加帧头帧尾或长度字段做二次校验,半包直接丢弃重同步,比硬等更稳。
嵌入式老张 · 2026-11-08
核心思路是把“空闲中断”当作帧边界判据,而不是数据到达判据。ESP32 上建议用 UART 硬件 FIFO + DMA 接收,配置 rx_timeout_thresh(约 2~5 个字符时间)触发接收超时中断,在中断里读取当前 DMA 已写入长度,把这段数据整体拷入环形缓冲区并记录“帧结束”标志;主任务只在看到帧结束标志后整帧取出,避免边收边解析。防半包:不要用固定长度判断,超时阈值要大于一帧内最大字符间隔(如 Modbus 3.5 字符时间),并在帧开始后启动软件定时器做二次超时兜底,超时未完成则丢弃或标记异常。防粘包:每帧处理完必须重置 DMA 描述符和接收长度,确保下一帧从新缓冲区开始;若协议带长度/帧头帧尾,优先用协议解析做二次校验。注意临界区保护长度变量,DMA 缓冲区用双缓冲或乒乓缓冲,避免拷贝期间被覆盖。
mcuku 阿沐 · 2026-11-08

🧰 配套工具

⏱️ 定时器计算器