UART高级应用:多机通信、流控制、环形缓冲区与FIFO管理一、从一次产线“死机”说起去年做一款工业数据采集器,三块STM32通过UART组网,主节点轮询两个从节点。产线跑了两周,突然出现主节点每隔几小时就卡死——不是看门狗复位,是串口中断再也进不来了。用逻辑分析仪抓波形,发现从节点A在某个时刻连续发了0x00字节,把主节点的接收缓冲区撑爆了,后续数据全部丢失,中断标志位被卡死在RXNE=1但DR寄存器读不到新数据。这个坑让我意识到:UART不是两根线焊上就能用的。多机通信的地址过滤、流控制的硬件握手、环形缓冲区的边界处理、FIFO的深度配置——任何一个环节出问题,产线就得停。二、多机通信:别让从节点“抢话筒”2.1 硬件层面的地址过滤多机通信最朴素的做法是软件过滤:每个从节点收到所有数据,判断地址字节是否匹配。但这样做有两个问题:一是从节点CPU被频繁打断,二是总线冲突——两个从节点同时应答时,TX引脚会短路。正确的做法是利用UART的静默模式(Silent Mode)。以STM32为例,配置USART_CR1的M位为9位数据模式,第9位作为地址/数据标志位。当从节点收到地址帧(第9位=1)且地址不匹配时,硬件自动进入静默状态,后续数据帧(第9位=0)不会触发中断。只有匹配地址的从节点才会唤醒。// 配置从节点地址为0x05,开启地址匹配中断USART1-CR1|=USART_CR1_M;// 9位数据模式,别漏了这步USART1-CR2|=(0x050);// 设置节点地址USART1-CR1|=USART_CR1_WAKE;// 地址匹配唤醒USART1-CR1|=USART_CR1_RXNEIE;// 接收中断使能// 这里踩过坑:WAKE位必须和M位同时设置,否则地址帧识别会错位主节点发送时,地址帧的第9位置1,数据帧的第9位置0。硬件自动完成过滤,从节点CPU只在被寻址时响应。2.2 总线仲裁的“土办法”如果多个从节点需要主动上报(比如报警信号),必须引入总线仲裁机制。我见过最蠢的做法是让从节点检测总线空闲后直接发数据——两个从节点同时检测到空闲,同时拉低TX,结果就是总线电平被拉成0.5V,谁的数据都发不出去。推荐用主从轮询+事件标志位:从节点有数据要上报时,拉高一个GPIO作为请求线,主节点在轮询周期内检测到请求后,再通过地址帧询问具体数据。这样虽然增加了一个引脚,但彻底避免了总线冲突。三、流控制:硬件RTS/CTS比你想的更重要3.1 为什么软件流控不靠谱很多工程师觉得XON/XOFF软件流控够用了,直到遇到高速率+大数据量。某次调试115200波特率,主节点每100ms发一次1KB数据包,从节点处理不过来,软件流控的XOFF字符(0x13)发出去时,主节点已经又发了200字节——缓冲区溢出。硬件流控(RTS/CTS)才是硬道理。CTS引脚告诉对方“我还能收”,RTS引脚告诉对方“你还能发”。STM32的USART支持硬件流控,配置USART_CR3的CTSE和RTSE位即可。// 开启硬件流控,注意CTS引脚必须配置为输入模式USART3-CR3|=USART_CR3_CTSE|USART_CR3_RTSE;// 别这样写:只开CTSE不开RTSE,会导致RTS引脚一直输出低电平,对方以为你永远能收3.2 流控的“死锁”陷阱硬件流控有个经典死锁场景:接收方缓冲区满,拉低RTS;发送方检测到CTS为低,停止发送。但如果接收方在处理完数据后,忘记拉高RTS,发送方就会一直等待——死锁。解决方案:在接收中断中,每次读取数据后检查缓冲区剩余空间,当空间大于阈值(比如FIFO深度的一半)时,再拉高RTS。不要等到缓冲区完全空才拉高,留出余量。voidUSART_IRQHandler(