串口通信协议(UART):基本原理、数据帧格式、波特率、奇偶校验、硬件流控
串口通信,也就是UART,是嵌入式开发里最基础、最常用的通信方式之一。我个人觉得,搞通信协议的人要是连UART都搞不明白,那基本等于厨师不会切菜。今天咱们就把UART的底裤扒干净,从原理到实战,一步到位。
一、UART的基本原理
UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。说白了,它就是一根线发、一根线收,不需要时钟线,全靠双方约定好节奏。
为什么叫异步?因为没有独立的时钟信号。发送方和接收方各自用自己的时钟,只要波特率一致,就能对上话。我在项目中遇到过一个问题:两块板子波特率都设成了115200,但一个用16MHz晶振,一个用12MHz晶振,结果数据全是乱码。后来一查,晶振误差累积导致采样点偏移了。嗯,这里要注意:晶振精度很重要,尤其是高速率下。
UART的物理层很简单:
- TXD:发送数据线
- RXD:接收数据线
- GND:共地,这个不能省
你想想看,没有GND,信号电平就没有参考点,通信肯定不稳定。我曾经见过有人偷懒不接地线,结果数据偶尔对偶尔错,排查了两天才发现是地线没接。
二、数据帧格式
UART的数据帧格式,说白了就是一串高低电平的组合。标准格式如下:
起始位(1bit) + 数据位(5~8bit) + 校验位(0/1bit) + 停止位(1/1.5/2bit)
举个例子,8N1格式:1个起始位 + 8个数据位 + 无校验 + 1个停止位。这是最常用的配置,占了嵌入式项目里的八成以上。
我习惯把数据帧画成时序图,这样更直观。下面是我用SVG画的一个标准UART数据帧结构:
注意看,数据位是LSB先发,也就是最低位在前。这个细节很多人容易搞反,我刚开始做的时候也踩过这个坑。
三、波特率
波特率就是每秒传输的比特数,单位是bps。常见的波特率有:9600、19200、38400、115200、921600等。
为什么是这些数字?因为它们都是2的整数次幂的倍数,方便从系统时钟分频得到。举个例子,系统时钟16MHz,要得到115200波特率,分频系数就是16MHz / 115200 ≈ 138.89,取整后实际波特率会有微小误差。
波特率误差公式:
误差 = |实际波特率 - 目标波特率| / 目标波特率 × 100%
一般要求误差 < 2%,否则通信可能不稳定。
我建议你选波特率时留点余量。比如系统时钟精度一般,就别硬上921600,老老实实用115200。我曾经在一个项目里为了追求速度用了1M波特率,结果晶振误差3%,数据丢包率高达20%,最后只能降速。
| 波特率 | 每比特时间 | 传输1字节耗时(8N1) | 适用场景 |
|---|---|---|---|
| 9600 | 104.17 μs | 约1.04 ms | 调试、低速传感器 |
| 115200 | 8.68 μs | 约86.8 μs | 通用通信、模块交互 |
| 921600 | 1.09 μs | 约10.9 μs | 高速数据传输 |
四、奇偶校验
奇偶校验是UART里最简单的错误检测手段。它分两种:
- 奇校验:数据位中1的个数 + 校验位 = 奇数
- 偶校验:数据位中1的个数 + 校验位 = 偶数
举个例子,数据位是0b01010101,里面有4个1(偶数)。如果是偶校验,校验位就是0;如果是奇校验,校验位就是1。
说实话,奇偶校验只能检测奇数个比特的错误,如果两个比特同时翻转,它就检测不出来了。所以它只适合对可靠性要求不高的场景。
我的经验:调试阶段建议开启奇偶校验,能帮你快速发现硬件连接问题。量产时如果通信环境好,可以关掉校验位,省一个比特的时间,提高吞吐量。
五、硬件流控
硬件流控,也叫RTS/CTS流控。它用两根额外的信号线来控制数据流:
- RTS(Request To Send):请求发送,由接收方控制
- CTS(Clear To Send):允许发送,由发送方检测
工作原理很简单:接收方缓冲区快满时,拉低RTS,告诉发送方「先别发了,我消化一下」。发送方检测到CTS为低,就暂停发送。等接收方处理完,再拉高RTS,发送方继续发。
我建议你在以下场景使用硬件流控:
- 高速率通信(如921600以上)
- 接收方处理速度跟不上发送方
- 长距离传输,信号容易受干扰
注意:硬件流控需要双方都支持,并且接线要正确。我曾经在一个项目里只接了TXD和RXD,没接RTS/CTS,结果高速通信时数据疯狂丢失。后来加上流控线,问题立刻解决。
如果不使用硬件流控,那就得靠软件协议来保证不丢数据。比如每发一包数据,等对方回复ACK再发下一包。这种方式叫XON/XOFF软件流控,但效率比硬件流控低不少。
六、实战避坑指南
最后,我把自己这些年踩过的坑总结一下,希望能帮你少走弯路:
- 共地问题:两个设备之间一定要接GND,否则电平参考点不一致,数据必乱。
- 波特率匹配:不光数值要一样,晶振精度也要考虑。误差超过2%就可能丢数据。
- 电平转换:3.3V和5V设备直接连,可能会烧IO口。用电平转换芯片,别省这个钱。
- 中断处理:接收中断里别做耗时操作,否则会丢数据。我习惯用环形缓冲区,中断里只存数据,主循环里慢慢处理。
- 调试工具:逻辑分析仪是调试UART的神器,能看到每个比特的电平变化,比串口助手靠谱多了。
我曾经在一个项目里,设备偶尔死机,排查了三天没找到原因。最后用逻辑分析仪抓波形,发现是干扰导致停止位被误判为起始位,整个帧错位了。加上硬件流控和更严格的校验后,问题再没出现过。
UART看起来简单,但真正用好它,需要理解每一个细节。希望今天的分享对你有帮助。
公众号:蓝海资料掘金营,微信deep3321