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 bit) 数据位 (5~9 bit) 校验位 (0/1 bit) 停止位 (1/1.5/2 bit) 常见值:9600~921600 时钟精度要求 8N1 (最常用) 8E1 / 8O1 7E1 (老设备) 9N1 (地址帧) 波特率越高,对时钟精度要求越高 内部 RC 振荡器建议 ≤115200

这张图把 UART 的四个核心概念串在了一起:异步原理决定了通信方式,帧结构定义了数据格式,波特率控制了传输速度,帧格式组合则是实际配置时的参数选择。搞懂这四个点,UART 的基础就算打牢了。

1.7 小结

这一章我们聊了 UART 最核心的几个概念:

  • 异步通信靠的是波特率约定,而不是时钟线。
  • 一帧数据包含起始位、数据位、校验位、停止位。
  • 波特率是双方说话的速度约定,误差必须在容忍范围内。
  • 帧格式组合(如 8N1)决定了每一帧的具体结构。