单片机串口为何丢字?缓冲水位怎么留?
串口链路少几个字节时,线缆和干扰常常先背锅,但真正原因可能藏在时钟误差和缓冲余量里。单片机串口若只按平均吞吐设计,突发报文一来就会暴露尾部丢字。
波特率误差不是只要能打开终端就算合格。异步串口靠起始位重新对齐,但一个字符内部仍要靠本地采样时钟判断位中心;发送端和接收端误差方向相反时,采样点会逐位偏移。数据位越长、停止位越紧、波特率越高,容错余量越小。片内 RC 振荡器在温度、电压变化下漂移较大,低温启动或电池电压下陷时,原本勉强可用的配置就可能越界。表现常常不是整帧都错,而是在长报文、连续 0 或连续 1 较多时偶发帧错误。
因此串口时钟要按最坏误差合成,而不是只看一端标称。若系统没有外部晶体,就要确认内部校准在全温范围内仍满足对端容差;若使用倍频和分频器生成波特率,还要计算实际分频误差。对关键链路,增加停止位、降低波特率或在协议层加入帧校验,比事后盯着接收代码查 bug 更直接。单片机端若还同时承担 PWM 或低功耗切钟,切换时钟源后必须重新确认串口分频,否则调试口正常并不代表业务口稳定。
缓冲水位解释了另一类丢字:硬件接收到了,软件没来得及搬走。片上 UART FIFO 往往只有几级,接收中断若被更高优先级任务拖住,FIFO 很快溢出;即便用环形缓冲,若高水位报警设得太晚,应用线程还没消费,下一包数据已经进来。很多系统把解析、校验和回调都放进接收中断里,短包看不出问题,连续报文一来,中断时间反而挤占了后续接收。若接收同时承担调试日志转发,日志峰值还会和业务帧抢同一段内存带宽,让看似足够的缓冲在少数场景下突然见底。
水位应按最坏阻塞时间反推。先估算最高波特率下一字节间隔,再把最长关中断、最长高优先级 ISR、DMA 搬运延迟和应用线程唤醒时间都算进去,得到在停止处理期间还会涌入多少字节。高水位必须早于真正满,低水位也要能避免频繁抖动。若有 RTS/CTS,拉起停止请求后对端 FIFO 和移位寄存器仍会吐出尾字节,这部分也要留在余量里。否则看似启用了流控,实际仍在最后几字节溢出。
验证时不要只发固定短字符串。应连续发送最大长度帧,同时制造日志高峰、Flash 写入或其他中断压力,并记录 UART 错误标志、FIFO 水位、环形缓冲写指针和应用消费时间。还要测试帧间隔从很短到很长的变化,因为一些协议用空闲时间判帧,间隔抖动会让粘包和拆包错误被误认为丢字。若采用 DMA 接收,还要看半满中断和空闲中断是否会遗漏尾段或重复提交。若错误只在高温或低压时出现,优先查波特率误差;若总在系统忙时出现,缓冲预算通常更可疑。
所以,串口稳定性既是时钟问题,也是时间预算问题。把采样误差压进容差,再把缓冲水位留到最坏阻塞之后,链路才不会靠运气收包。





