1. UART 基础回顾:协议原理与帧结构
UART,全称是 Universal Asynchronous Receiver/Transmitter。说白了,就是让两个设备之间,只用两根线就能互相聊天的一种方式。一根发(TX),一根收(RX),没有时钟线。
我刚开始做嵌入式那会儿,总觉得 UART 太简单了,不就是串口嘛。后来在项目里被它坑过几次,才发现——越是基础的东西,越容易在细节上翻车。咱们这一章,就把 UART 的底裤扒干净。
1.1 为什么叫“异步”?
同步通信,比如 SPI、I2C,都带一根时钟线。发送方和接收方看着同一个时钟干活。UART 没有这根线,那两边怎么对齐节奏?
答案是:双方事先约定好一个“说话速度”。这个速度就是波特率。你发得快,我收得也快;你发得慢,我也跟着慢。两边各自用自己的时钟去采样,只要误差在容忍范围内,就能正常通信。
核心要点:UART 的“异步”指的是没有独立的时钟信号线,而不是说收发双方可以随意乱发。波特率必须一致,否则数据全是乱的。
1.2 帧结构:一帧数据长什么样?
UART 的数据是一帧一帧发的。每一帧包含以下几个部分:
- 起始位(Start Bit):1 位,逻辑低电平。告诉接收方“注意,我要开始发数据了”。
- 数据位(Data Bits):5~9 位,最常见的是 8 位。这就是你要传的字节。
- 校验位(Parity Bit):可选,0 或 1 位。用于简单的错误检测。
- 停止位(Stop Bit):1、1.5 或 2 位,逻辑高电平。表示这一帧结束。
我习惯把 UART 帧想象成一列火车:起始位是火车头鸣笛,数据位是车厢,校验位是安检员,停止位是站台刹车。缺了哪个,这趟车都跑不顺。
个人经验:我在调试一个 GPS 模块时,发现数据偶尔会错位。抓了波形才发现,接收端的停止位配置成了 2 位,而发送端只发了 1 位。接收方一直在等第二个停止位,结果把下一帧的起始位给吞了。从那以后,我每次配 UART 都会先确认停止位配置。
1.3 波特率:说话的速度
波特率(Baud Rate)定义的是每秒传输多少个符号(Symbol)。在 UART 里,一个符号就是一个 bit,所以波特率就等于比特率(bps)。
常见的波特率有:9600、19200、38400、115200、921600 等。选哪个?看你的需求。
| 波特率 (bps) | 传输 1 字节耗时 (8N1) | 典型应用场景 |
|---|---|---|
| 9600 | 约 1.04 ms | 调试日志、低速传感器 |
| 115200 | 约 86.8 μs | 常见嵌入式调试、蓝牙模块 |
| 921600 | 约 10.9 μs | 高速数据传输、固件升级 |
波特率越高,单位时间传的数据越多,但对时钟精度的要求也越高。为什么?
假设你设了 115200 bps,每个 bit 的时长是 1/115200 ≈ 8.68 μs。接收方在每个 bit 的中间位置采样。如果双方的时钟有 3% 的误差,累积到第 10 个 bit(一帧数据),采样点可能已经偏了半个 bit 的位置——数据就错了。
避坑指南:我曾经在一个项目里,MCU 用内部 RC 振荡器,精度只有 ±2%。波特率设到 921600 时,通信时好时坏。换成外部晶振后,问题消失。所以,高波特率一定要用高精度时钟源,内部 RC 振荡器通常只适合 115200 及以下。
1.4 帧格式的常见组合
UART 的帧格式通常写成类似 8N1 这样的缩写。意思是:8 位数据位、无校验(None)、1 位停止位。
- 8N1:8 数据位 + 无校验 + 1 停止位。最常用,总共 10 bits 传输一个字节。
- 8E1:8 数据位 + 偶校验 + 1 停止位。
- 8O1:8 数据位 + 奇校验 + 1 停止位。
- 7E1:7 数据位 + 偶校验 + 1 停止位。老式设备常见。
- 9N1:9 数据位 + 无校验 + 1 停止位。用于传输 9 位地址/数据。
我个人建议,除非有特殊需求,一律用 8N1。兼容性最好,效率也最高。校验位能检测单 bit 错误,但不能纠错,而且会降低吞吐量。现在很多协议都在上层做 CRC 校验,UART 层的奇偶校验反而用得少了。
1.5 波特率计算公式
在 MCU 里配置 UART 时,波特率通常由一个分频器产生。公式大致是:
波特率 = 时钟频率 / (16 × 分频系数)
或者在某些芯片里:
波特率 = 时钟频率 / (8 × (2 - 过采样模式) × 分频系数)
举个例子,STM32 的 USART,时钟 72 MHz,目标波特率 115200:
分频系数 = 72000000 / (16 × 115200) = 39.0625
取整为 39,实际波特率 = 72000000 / (16 × 39) = 115384
误差 = (115384 - 115200) / 115200 ≈ 0.16%
误差在 ±1.5% 以内通常没问题。但如果你用内部 RC 振荡器,本身就有 ±2% 的误差,再加上分频取整的误差,就可能超过 3%。这时候通信就不稳定了。
调试技巧:我习惯在代码里把 UART 的时钟配置打印出来,用逻辑分析仪抓一下 TX 脚的实际波形,测量一个 bit 的宽度,反算实际波特率。这样能直接验证配置是否正确,比看寄存器省心多了。
1.6 本章知识结构图
下面这张图把 UART 的核心概念串起来了。你可以把它当作本章的“地图”。
这张图把 UART 的四个核心概念串在了一起:异步原理决定了通信方式,帧结构定义了数据格式,波特率控制了传输速度,帧格式组合则是实际配置时的参数选择。搞懂这四个点,UART 的基础就算打牢了。
1.7 小结
这一章我们聊了 UART 最核心的几个概念:
- 异步通信靠的是波特率约定,而不是时钟线。
- 一帧数据包含起始位、数据位、校验位、停止位。
- 波特率是双方说话的速度约定,误差必须在容忍范围内。
- 帧格式组合(如 8N1)决定了每一帧的具体结构。