4. 串口通信协议详解:UART协议基础,波特率、数据位、停止位、校验位,硬件流控

串口通信,说白了就是两个设备之间,用一根或两根线,按约定好的节奏,一位一位地传数据。UART(Universal Asynchronous Receiver/Transmitter)是其中最经典的协议。我刚开始做嵌入式时,觉得串口太简单了,不就是TX、RX两根线嘛。直到有一次,一个产品在产线上批量烧录时,10台里有3台烧录失败……查了两天才发现,是波特率偏差和流控没处理好。从那以后,我对串口协议的每一个参数都格外较真。

这一章,我们就来把UART协议的底裤扒干净。你理解了这些基础,后面讲串口烧录和OTA升级时,才能知道为什么有时候会卡住、为什么会丢包。

4.1 UART协议的本质:异步、全双工、帧结构

UART是异步通信,意思是收发双方没有独立的时钟线。那它们怎么同步?靠的是双方事先约定好相同的波特率,然后通过起始位来对齐。

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

空闲状态(高电平) → 起始位(低电平) → 数据位(LSB先发) → 校验位(可选) → 停止位(高电平)

我习惯把UART帧比作一列火车:起始位是火车头,告诉接收方“注意,数据要来了”;数据位是车厢,装着你要传的信息;校验位是货物清单,用来检查车厢有没有被篡改;停止位是车尾,告诉接收方“这一趟结束了”。

核心要点:UART是异步的,所以收发双方的波特率必须一致。偏差超过2%-3%,就会出现误码。我在项目中见过有人用115200和1152000(少打了个0),结果数据全是乱码,查了半天才反应过来。

UART也是全双工的,TX和RX独立工作。这意味着你可以同时发送和接收数据。这在OTA升级中很重要——设备一边接收固件包,一边可以回复ACK。

4.2 波特率:通信的节拍器

波特率,就是每秒传输的符号数。对于UART来说,一个符号就是一个bit,所以波特率就等于比特率。单位是bps(bits per second)。

常见的波特率有:

波特率(bps) 每位时长(μs) 典型应用场景
9600 104.17 调试输出、低速传感器
19200 52.08 GPS模块、工业仪表
38400 26.04 蓝牙模块、短距离通信
57600 17.36 WiFi模块、中等速率
115200 8.68 串口烧录、OTA升级(最常用)
230400 4.34 高速烧录、内部调试
921600 1.09 高速烧录(需硬件支持)

你想想看,为什么115200最常用?因为它在速度和稳定性之间取得了很好的平衡。我做过测试,在普通MCU上,115200的误码率远低于230400,而速度比9600快了12倍。

我的经验:在串口烧录场景下,如果MCU的时钟精度不够(比如内部RC振荡器),建议不要用超过115200的波特率。我曾经在一个项目里用内部时钟跑230400,结果烧录成功率只有60%。换成外部晶振后,问题立刻解决。

4.3 数据位:一次传多少数据

数据位,就是每个帧里实际承载的数据比特数。常见的有5、6、7、8位。现在绝大多数场景都用8位数据位,因为一个字节正好8位,方便处理。

为什么会有7位?那是历史遗留问题。早期的ASCII码只需要7位,第8位可以用来做校验。但现在,除非你在跟古董设备通信,否则一律选8位。

数据位的发送顺序是LSB(最低有效位)先发。比如你要发送0x55(二进制01010101),先发最低位1,再发0,再发1……最后发最高位0。接收方收到后,再按LSB优先拼回去。

注意:有些设备支持9位数据位模式,用于多机通信或地址帧识别。但这不是标准UART,需要MCU的USART模块支持。我建议初学者先别碰,容易把自己绕晕。

4.4 停止位:帧与帧之间的间隔

停止位,就是每个数据帧结束后的高电平持续时间。常见的有1位、1.5位、2位。

  • 1位停止位:最常用,效率最高。每传8位数据,只需要1位开销。
  • 2位停止位:给接收方更多时间处理数据。适合低速设备或噪声环境。
  • 1.5位:很少见,一般只在某些特殊设备上出现。

我个人习惯,只要不是跟特别老的设备通信,一律用1位停止位。为什么?因为效率高。在OTA升级中,每帧多1位停止位,意味着总传输时间增加约10%。如果固件有1MB,那差距就很明显了。

但有一种情况我会用2位停止位——当通信线路很长(比如超过10米)或者电磁干扰严重时。多一位停止位,相当于给接收方多了一个采样窗口,能有效降低误码率。

4.5 校验位:数据完整性的第一道防线

校验位,用来检测数据在传输过程中是否出错。UART支持以下几种校验方式:

校验方式 原理 特点
无校验(None) 不发送校验位 效率最高,但无检错能力
奇校验(Odd) 数据位+校验位中1的个数为奇数 能检测奇数个bit错误
偶校验(Even) 数据位+校验位中1的个数为偶数 能检测奇数个bit错误
标记校验(Mark) 校验位固定为1 基本不用,无实际检错能力
空格校验(Space) 校验位固定为0 基本不用,无实际检错能力

在实际项目中,我推荐用无校验+上层CRC校验的组合。为什么?因为UART的奇偶校验只能检测奇数个bit错误,如果两个bit同时翻转,它检测不出来。而CRC(循环冗余校验)能检测出几乎所有错误。

避坑指南:我曾经在一个项目中,用了偶校验,结果数据偶尔出错,但校验位每次都通过。查了很久才发现,是线路上的噪声导致两个bit同时翻转。从那以后,我再也不依赖UART的硬件校验了。串口烧录和OTA升级,一定要在应用层做CRC校验。

4.6 硬件流控:RTS/CTS

硬件流控,就是通过额外的信号线,让收发双方互相告诉对方“我准备好了”或者“我忙,你先别发”。常用的信号线是RTS(请求发送)和CTS(清除发送)。

工作流程是这样的:

  1. 设备A想发送数据,先拉高RTS,告诉设备B“我要发了”。
  2. 设备B如果准备好了,就拉高CTS,告诉设备A“来吧,我准备好了”。
  3. 设备A看到CTS为高,开始发送数据。
  4. 如果设备B的接收缓冲区快满了,就拉低CTS,设备A看到后暂停发送。
  5. 设备B处理完数据后,再拉高CTS,设备A继续发送。

你可能会问:什么时候需要硬件流控?

  • 高速通信时:比如921600波特率,MCU处理不过来,需要流控来防止数据溢出。
  • 与PC通信时:PC端的USB转串口芯片通常有缓冲区,但缓冲区满了就会丢数据。硬件流控可以避免这个问题。
  • 长距离通信时:线路上的延迟可能导致数据重叠,流控可以协调节奏。

但说实话,在大多数MCU的串口烧录和OTA升级场景中,我很少用硬件流控。为什么?因为MCU的处理速度足够快,而且我们会在应用层做分包和应答机制。硬件流控反而多占用了两个GPIO,增加了布线复杂度。

我的建议:如果你用的是STM32这类主流MCU,串口烧录时基本不需要硬件流控。但如果你在做高速数据采集,或者用ESP32通过串口传输音频流,那硬件流控就很有必要了。

4.7 知识体系总览

下面这张图,把UART协议的核心参数和它们之间的关系梳理清楚了。你可以把它当作一个速查表。

UART协议知识体系 UART协议核心参数与关系 UART数据帧 波特率 数据位 停止位 校验位 硬件流控 (RTS/CTS) 关键说明: 1. 波特率决定通信速度,收发双方必须一致,偏差 < 3% 2. 数据位通常为8位,LSB先发 3. 停止位推荐1位,噪声环境用2位 4. 校验位建议用无校验+上层CRC,硬件流控按需使用

4.8 实战中的参数选择建议

说了这么多理论,最后给点实际建议。你在做串口烧录或OTA升级时,参数怎么配?

场景 波特率 数据位 停止位 校验位 硬件流控
调试输出 115200 8 1 无 关
串口烧录(内部晶振) 115200 8 1 无 关
串口烧录(外部晶振) 921600 8 1 无 开
OTA升级(WiFi模块) 115200 8 1 无 关
长距离通信(>10米) 9600 8 2 偶校验 开
高速数据采集 230400 8 1 无 开

嗯,这张表你可以直接拿去用。我自己的项目,90%的情况都是115200、8N1(8数据位、无校验、1停止位)。简单、稳定、够用。