4、UART协议深度解析:帧格式、波特率误差、校验与流控
UART,说白了就是串口。搞嵌入式的人,没人敢说自己没碰过它。但说实话,很多人用了好几年,遇到干扰时照样抓瞎。今天我就把这层窗户纸捅破,从帧格式讲到流控,把那些容易踩坑的地方都拎出来说说。
4.1 UART帧格式:一个字节怎么飞过去的?
UART是异步通信,没有时钟线。那收发双方怎么对齐?靠的是帧格式里的起始位和停止位。
一个标准的UART帧长这样:
空闲(高电平) | 起始位(低) | 数据位(LSB→MSB) | 奇偶校验位(可选) | 停止位(高) | 空闲(高)
我习惯把帧拆成几个关键部分:
- 起始位:1位,固定为低电平。告诉接收方“我要发数据了”。
- 数据位:5~9位,常用8位。注意发送顺序是LSB在前,MSB在后。
- 奇偶校验位:0或1位,可选。后面细讲。
- 停止位:1、1.5或2位,固定为高电平。给接收方时间处理刚收到的字节。
关键点:起始位的下降沿是双方的时间基准。接收方从这个下降沿开始,按约定的波特率采样后续数据位。如果这个沿被干扰了,整帧就废了。
我在项目中遇到过一件事:某款传感器在强电磁环境下频繁丢字节。后来用示波器一看,起始位前面多了个毛刺,接收方误判了下降沿位置。解决办法是在软件里加了个去毛刺滤波,采样点避开边沿附近。
4.2 波特率误差分析:差多少会翻车?
波特率就是每秒传输的符号数。UART没有时钟线,全靠双方约定速率。但晶振有误差,分频有舍入,两边不可能完全一样。
那误差容忍度是多少?
| 数据位长度 | 理论最大误差(%) | 工程建议上限(%) |
|---|---|---|
| 8位 + 无校验 | ±4.5 | ±2.0 |
| 8位 + 奇偶校验 | ±4.0 | ±1.5 |
| 9位 + 奇偶校验 | ±3.5 | ±1.0 |
为什么会这样?因为误差是累积的。起始位对齐后,每采一位,误差就多一分。采到最后一位停止位时,误差最大。如果采样点漂出了位中间区域,就会采错。
我的经验:设计时尽量选整数分频的波特率。比如用11.0592MHz晶振配9600bps,误差几乎为零。用12MHz晶振配9600bps,误差就有0.16%,虽然能用,但心里不踏实。
我曾经在一个项目里,两边晶振分别是16MHz和8MHz,都配115200bps。结果16MHz那边误差0.16%,8MHz那边误差0.32%,加起来0.48%,还在容忍范围内。但如果两边都用12MHz配115200,误差直接飙到0.8%,高温下就翻车了。
4.3 奇偶校验:能防住什么?防不住什么?
奇偶校验分两种:
- 奇校验:数据位+校验位里,1的个数为奇数。
- 偶校验:数据位+校验位里,1的个数为偶数。
接收方收到数据后,重新计算1的个数,跟校验位比对。不一致就报错。
但这里有个坑:奇偶校验只能检测奇数个比特翻转。如果两个比特同时翻转,校验结果不变,错误就漏过去了。
注意:在强干扰环境下,比如电机驱动附近,经常出现连续多位翻转。这时候奇偶校验形同虚设。我建议配合校验和或CRC使用,别单靠奇偶校验保命。
我记得有一次调试一个工业控制器,UART通信偶尔出现乱码。打开奇偶校验后,错误帧能被检测到了,但重传率高达15%。后来发现是电源纹波太大,导致接收器采样抖动。加了滤波电容后,重传率降到0.1%以下。
4.4 流控制(RTS/CTS):什么时候必须用?
流控制解决的是“发送方太快,接收方来不及处理”的问题。UART的流控制分两种:
- 硬件流控(RTS/CTS):用两根专用引脚。接收方准备好时,拉高CTS;缓冲区快满时,拉低CTS让发送方暂停。
- 软件流控(XON/XOFF):在数据线上发特殊字符。接收方发0x13(XOFF)让发送方暂停,发0x11(XON)让发送方继续。
我个人强烈推荐硬件流控,原因有三:
- 软件流控的XON/XOFF字符不能出现在数据里,否则会误触发。虽然可以用转义,但麻烦。
- 软件流控在干扰环境下,控制字符本身可能被篡改,导致死锁。
- 硬件流控的响应速度更快,适合高速通信。
什么时候必须用流控? 当发送方是连续数据流(比如GPS模块每秒输出10条NMEA语句),而接收方需要处理中断、写日志、刷屏幕时。没有流控,缓冲区一满,数据就丢了。
我曾经在一个项目里,用STM32的UART接收激光雷达数据,波特率921600。接收方每帧还要做坐标变换,处理时间不稳定。不加流控时,每10秒丢一帧。加了RTS/CTS后,一帧都不丢。
4.5 UART帧结构与抗干扰要点
下面这张图把UART帧的完整结构和抗干扰的关键点串起来了:
4.6 避坑指南:我踩过的几个坑
最后分享几个实战教训:
- 坑一:波特率误差在常温下没问题,高温下就翻车。晶振的频率温度系数是ppm/°C,高温下误差会变大。我建议留足余量,别卡着理论极限。
- 坑二:RTS/CTS接线接反。RTS是发送方请求发送,CTS是接收方允许发送。接反了流控完全失效。我习惯用万用表量一下电平再确认。
- 坑三:奇偶校验位算错了位置。有些MCU的UART外设,校验位是自动插入的,但数据位长度设置不对时,校验位位置会偏移。我建议用示波器抓一帧,数一下位宽确认。
- 坑四:停止位用1.5位时,接收方不支持。1.5位停止位在低速场景下有用,但不是所有UART外设都支持。用之前查一下数据手册。
我的习惯:新板子调UART时,先发0x55(二进制01010101),用示波器看波形。每个数据位宽度应该一致,起始位下降沿干净,停止位高电平稳定。波形没问题了,再调软件。
UART看似简单,但干扰环境下处处是坑。把帧结构吃透,波特率算准,校验和流控配好,物理层选对,才能做出靠谱的通信系统。