串口通信中,实现串口接收函数时,避免数据丢失或被覆盖的方法
一. 简介
本文来学习一下,串口通信中,在实现串口接收函数时,如何避免数据丢失或被覆盖。可以解决的几种方法。
二. 串口通信中,实现串口接收函数时,避免数据覆盖或丢失的方法
在编写串口接收函数时,避免数据丢失或被覆盖的核心在于 合理设计缓冲区、优化接收逻辑和处理异常情况。
在串口通信中,数据覆盖或丢失通常源于接收速度与处理速度不匹配、缓冲区管理不当或中断处理不及时。以下是避免这些问题的核心策略和实现方法:
1. 使用环形缓冲区(Ring Buffer)
环形缓冲区(Ring Buffer)类似如下:
原理
环形缓冲区通过头尾指针循环写入/读取数据,避免线性缓冲区的溢出问题。
写入指针(head)由中断服务程序(ISR)更新,读取指针(tail)由主程序控制。
优点
无数据覆盖:缓冲区满时丢弃新数据或报错。
高效解耦:中断仅负责写入,主程序异步读取。
2. 流控(Flow Control)
硬件流控(RTS/CTS)
通过RTS(Request to Send)和CTS(Clear to Send)引脚控制数据流。
软件流控(XON/XOFF)
发送特殊字符(XOFF=0x13暂停,XON=0x11继续)。
适用场景:无硬件流控引脚时。
3. 进行超时和错误处理
关键措施
(1)数据包超时检测:
// 检查自上次接收到串口数据以来的时间是否超过了预设的超时阈值(单位:毫秒)
// HAL_GetTick() 返回系统启动后的总毫秒数(由 SysTick 定时器每 1ms 更新一次)
// last_rx_time 记录了最后一次成功接收到数据的时刻(也是用 HAL_GetTick() 获取的)
// TIMEOUT_MS 是一个宏或常量,定义了等待一帧数据完成的最大空闲时间(如 5、10 或 20ms)
if (HAL_GetTick() - last_rx_time > TIMEOUT_MS) {// 如果超时,说明很可能一包完整的数据已经接收完毕(即使没有收到结束符)// 此时调用处理函数,提交当前缓冲区中已接收到的部分或完整数据进行解析// 即使数据不完整,也尝试处理,避免数据长期滞留导致系统无响应process_incomplete_packet();// 清空接收缓冲区,重置接收状态// 防止旧数据残留影响下一包数据的接收和解析// 通常会将缓冲区索引(如 head/tail)重置为初始状态reset_buffer();
}
(2)溢出检测:
if (USART1->ISR & USART_ISR_ORE) { // 检查溢出标志USART1->ICR |= USART_ICR_ORECF; // 清除标志handle_overflow_error(); // 错误处理
}
4. 启用硬件FIFO(如果支持)
原理
许多MCU的UART模块内置硬件FIFO(如STM32的16级FIFO),可缓存多个字节再触发中断,减少中断频率。
配置FIFO阈值(如半满触发中断)。
优点
降低CPU负载:减少中断次数,适合高波特率场景。
5. DMA传输(大数据量场景)
原理
使用DMA自动将串口数据搬运到内存,无需CPU干预。
循环模式(Circular Mode):DMA持续接收数据,覆盖旧数据(需及时处理)。
优点
零CPU占用:适合高速通信(如1Mbps)。
抗突发数据:DMA自动处理连续数据流。
6. 双缓冲区策略(Ping-Pong Buffer)
原理
使用两个缓冲区交替工作:DMA写入缓冲区A时,主程序处理缓冲区B,反之亦然。
避免处理延迟导致的数据覆盖。
三. 总结
方案选型建议
下一篇文章举例来说明这几种方法的使用(可能使用单片机STM32进行举例)。