嵌入式串口为何丢包?流控怎么配平?
扫描二维码
随时随地手机看文章
串口丢包常被归咎于线材或干扰,但很多系统在实验室里安静放着也会少字节。嵌入式通信一旦让缓冲水位和硬件流控彼此脱节,接收链路就会出现一种很危险的错觉:两端都认为自己已经提醒过对方减速,可数据还是继续冲过来。
环形缓冲首先不是越大越保险,因为问题常出在消费节拍和告警时机。接收中断或 DMA 只负责把字节搬进 RAM,真正解析协议、落日志、做转发的线程却可能被别的任务延后。若高水位只在“快满了”才触发,而上层一次又要处理较长报文,缓冲里剩余空间可能还没等到应用释放就被下一串数据吃光。更隐蔽的是日志串口和业务串口共用同一调度资源时,后台打印一多,业务数据虽然按时进缓冲,解析线程却来不及清空,现场看起来像外设突然抽风,实则是消费链比输入链慢了半拍。
更稳妥的做法,是按最坏报文长度和最长调度阻塞时间反推水位阈值,而不是只看平均吞吐。高水位不是一个装饰性报警,它必须留出足够时间给对端减速、给本端完成一次协议处理,必要时还要预留 DMA 尾块尚未搬运完的空间。一旦协议解析还要等待上层确认,滞留时间会再叠一层。嵌入式链路若没有把“还能再进多少字节”和“自己多久才能腾出空间”一起算进去,缓冲大小再翻倍也只是把故障推迟几毫秒。
RTS/CTS 传播延迟则解释了为什么已经拉高 RTS 仍会溢出。对端看到停止请求需要经过线路传播、串口硬件状态机、生效到发送器这一整条路径;若对端内部还有 FIFO,它往往会把已经进入移位器和 FIFO 的字节继续发完。结果是本端刚觉得安全,实际上还要再吞下十几个甚至几十个字节。嵌入式板上若 CTS 信号还绕了电平转换、隔离器或长线缆,这个尾巴会更长,必须被算进缓冲余量,而不能假设握手信号会立刻刹车。因此握手线的提前量本质上是一笔尾流预算,而不是开关量判断。
真正配平流控,要把硬件和软件看成同一条制动链。协议解析若存在长临界区,RTS 应更早触发;若 DMA 采用半满中断,阈值要和 CTS 尾字节预算一起选;若对端不可控,还要加应用层分块确认,避免单靠电气流控承受全部背压。对突发链路,阈值也应按最大突发而不是平均波特率来定。只有让每一级都知道最坏情况下还会再涌入多少数据,停止请求才不是一纸通知,而是可兑现的余量管理。
排障时不要只盯丢失位置,最好同时记录缓冲占用、RTS 翻转时刻、DMA 写指针和解析线程唤醒时间。很多问题会表现成“总在长报文尾部少几个字节”,那往往不是校验算法错,而是 RTS 拉起后仍有尾流;若总在系统打印高峰期丢包,则更可能是消费路径被日志抢走了时隙。把这些时间点拉成一条线,原因会比看十份串口抓包更直白。
因此,串口可靠性并不只靠波特率保守。把缓冲水位做成真正的时间预算,再把流控尾巴算进最坏情况,链路才会稳。





