串口通信协议详解:UART协议原理、波特率设置、数据帧格式、流控机制

串口,说白了就是嵌入式世界里最基础的“电话线”。你想想看,一个MCU要跟PC通信,或者两个MCU之间要聊天,最直接、最省引脚的方式就是UART。我做Bootloader这么多年,串口烧录始终是最后一道防线——哪怕芯片里程序跑飞了,只要Bootloader还在,串口就能把固件灌进去。

这一章我们就把UART的底裤扒干净。从协议原理到波特率怎么算,从数据帧里每个bit的含义到流控要不要加,我都会结合项目里的实际坑点来讲。

UART协议原理:异步,但并不同步

UART全称是Universal Asynchronous Receiver/Transmitter。注意“Asynchronous”这个词——异步。什么意思?就是收发双方没有独立的时钟线。不像SPI有SCLK,I2C有SCL,UART就靠两根线:TX和RX。

那没有时钟怎么对齐数据?靠的是双方约定好的波特率,以及起始位和停止位。我打个比方:两个人约好每秒钟说一个字,说之前先咳嗽一声(起始位),说完之后点个头(停止位)。这样哪怕没有节拍器,也能听懂对方在说什么。

核心要点:UART是异步串行通信,数据以帧为单位传输,每帧由起始位、数据位、可选的校验位和停止位组成。收发双方必须使用相同的波特率、数据位长度、校验方式和停止位位数。

我在一个工业项目里遇到过这样的问题:两块板子用UART通信,一块是5V的STM32,另一块是3.3V的ESP32。直接连TX和RX,结果ESP32的IO口烧了。嗯,这里要注意——电平不匹配是串口通信最常见的硬件坑之一。

波特率设置:快与稳的博弈

波特率(Baud Rate)就是每秒传输的符号数。对于UART来说,一个符号就是一个bit,所以波特率就等于比特率。常见的波特率有:9600、19200、38400、115200、921600等。

怎么选?我个人习惯遵循一个原则:在满足时间要求的前提下,选最低的稳定波特率。为什么?因为波特率越高,每个bit的宽度越窄,对时钟精度和信号质量的要求就越高。

波特率 bit宽度 典型应用场景 注意事项
9600 104.17 µs 调试日志、低速率传感器 几乎任何MCU都能稳定跑
115200 8.68 µs Bootloader烧录、文件传输 需要晶振误差<2%
921600 1.09 µs 高速固件升级 线长建议<10cm,需硬件流控

波特率误差怎么算?以STM32为例,UART时钟来自APB总线,通过USARTDIV分频得到目标波特率。公式是:

BaudRate = fCK / (16 * USARTDIV)

如果fCK=72MHz,想要115200,算出来USARTDIV=39.0625。但分频寄存器只能取整数或半整数,实际只能设39或39.5。对应的实际波特率分别是115384和113924。误差分别是0.16%和-1.1%。

避坑指南:我曾经在一个项目里用了内部RC振荡器(精度约±1%),波特率设到460800,结果通信时好时坏。后来换成外部晶振,问题立刻消失。记住:波特率越高,对时钟源精度要求越苛刻。Bootloader场景下,我建议115200封顶,除非你确认晶振误差在±0.5%以内。

数据帧格式:每个bit都有它的使命

一个完整的UART数据帧长这样:

起始位(1bit) + 数据位(5~9bit) + 校验位(0/1bit) + 停止位(1/1.5/2bit)

我们一条一条拆开看。

起始位

TX线从高电平拉低,持续1个bit时间。接收端检测到这个下降沿,就知道“要发数据了”。这是帧同步的关键。

数据位

通常是8位,LSB(最低有效位)在前。为什么LSB在前?因为早期UART硬件设计上,先发低位可以更快地开始移位操作。这个习惯一直保留到现在。

校验位

可选。奇校验:数据位+校验位中1的个数为奇数。偶校验:1的个数为偶数。我个人在Bootloader场景下很少用校验位——因为数据包层面已经有CRC校验了,再加一个bit的奇偶校验意义不大,反而浪费带宽。

停止位

TX线拉高,持续1/1.5/2个bit时间。这是给接收端处理数据的时间窗口。停止位越长,抗干扰能力越强,但传输效率越低。

经验之谈:我习惯用8N1——8位数据、无校验、1位停止位。这是UART通信的“普通话”,几乎所有设备都支持。如果你不确定对方的配置,先试8N1,大概率能通。

流控机制:硬件流控 vs 软件流控

流控(Flow Control)解决的是“发送方太快,接收方来不及处理”的问题。UART的接收缓冲区通常只有1个字节(FIFO深的除外),如果CPU没来得及读,下一个字节就来了,直接覆盖——这就是溢出错误。

两种流控方式:

硬件流控(RTS/CTS)

需要额外两根线:RTS(请求发送)和CTS(允许发送)。接收方准备好接收时,拉高RTS;发送方检测到CTS为高,才发送数据。这是最可靠的流控方式。

我记得在一个批量烧录项目中,用115200波特率通过串口给1000块板子烧固件。没有硬件流控时,大约有3%的板子烧录失败。加上RTS/CTS后,失败率降到0。为什么?因为每块板子的Flash擦写时间不一样,硬件流控能自动适配最慢的那块。

软件流控(XON/XOFF)

不需要额外引脚,通过发送特殊字符来控制:XOFF(0x13)让发送方暂停,XON(0x11)让发送方继续。问题在于:如果传输的是二进制数据,0x13和0x11可能出现在数据中,造成误判。

流控方式 优点 缺点 适用场景
无流控 最简单,只需TX/RX两根线 高速传输易丢数据 调试日志、低速通信
硬件流控 可靠,不影响数据内容 需要额外两根线 Bootloader烧录、文件传输
软件流控 不需要额外引脚 二进制数据可能误触发 纯文本通信

避坑指南:我曾经在一个产品里用了软件流控传输固件包,结果固件里恰好有一个字节是0x13,接收端以为是XOFF,直接卡死。从那以后,只要传输二进制数据,我坚决不用软件流控。要么不加流控,要么加硬件流控,没有中间选项。

UART通信知识体系

下面这张图把UART的核心知识点串起来了。从物理层到协议层,再到实际应用中的注意事项,一目了然。

UART串口通信知识体系 物理层 TX / RX 两根信号线 电平标准:TTL/RS232/RS485 共地要求:GND必须连接 协议层 数据帧格式 起始位(1) + 数据位(5~9) + 校验位(0/1) + 停止位(1/2) LSB在前,MSB在后 常用配置:8N1 时序层 波特率 = 1 / bit宽度 时钟源:外部晶振优先 误差要求:< 2% 流控机制 硬件流控:RTS/CTS 软件流控:XON/XOFF 无流控:简单但不可靠 实战要点 • 电平匹配:3.3V vs 5V • 波特率选115200封顶 • 二进制传输不用软件流控 • 长线传输降低波特率 • 加TVS管防静电

写在最后

UART看起来简单,但真正用好它,需要理解每一层的工作原理。从物理层的电平匹配,到协议层的帧格式,再到时序层的波特率误差,最后到流控机制的选择——任何一个环节出问题,通信就会失败。