一、UART 基础回顾:异步串行通信原理与帧格式

各位同学,咱们正式开始这门课之前,先把 UART 的老底儿翻出来晒晒。你可能会说:“UART 嘛,我用了好几年了,有啥好讲的?” 嗯,我当年也是这么想的,直到有一次在产线上被一个波特率偏差问题折腾了整整两天…… 从那以后,我养成了一个习惯:每次调 UART,先把基础概念在心里过一遍。

1.1 异步串行通信——没有时钟线,怎么对得上?

UART 的全称是 Universal Asynchronous Receiver/Transmitter,通用异步收发器。说白了,它就是一种不需要额外时钟线的串行通信方式。

你想想看,SPI 和 I²C 都有时钟线(SCLK、SCL),收发双方靠这根线同步。但 UART 只有两根数据线:TX(发送)和 RX(接收)。没有时钟线,两边怎么对齐节奏?

答案就藏在“异步”这两个字里。收发双方各自用自己的时钟,但必须提前约定好一个相同的“节拍”——这就是波特率。我习惯把波特率理解成“每秒敲多少下鼓点”,两边鼓点频率一致,才能踩在同一个拍子上。

核心要点:异步 ≠ 随意。没有时钟线,但双方必须通过波特率约定和帧格式约定来建立同步。

1.2 帧格式——一帧数据长什么样?

UART 的数据是一帧一帧传的。每一帧就像一个小信封,里面装着你要发的数据,外面包着“信封皮”。这个信封皮包括:起始位、数据位、校验位(可选)、停止位。

我画了一张帧格式的结构图,你先看一眼整体,我再拆开讲。

UART 帧格式结构图 起始位 1 bit 数据位 5~9 bit(常用 8 bit) 校验位 0/1 bit(可选) 停止位 1/1.5/2 bit 空闲状态:TX/RX 线保持高电平(逻辑 1) 传输方向:LSB(最低有效位)先发,MSB 后发 一帧完整数据 = 起始位 + 数据位 + 校验位(可选)+ 停止位

好,咱们一个一个拆开看。

起始位(Start Bit)

空闲时,TX/RX 线都是高电平。发送端要发数据了,先把线拉低一个 bit 的时间。这个高→低跳变就是起始位。接收端检测到这个跳变,就知道“要开始收数据了”。

我在项目中遇到过一个问题:接收端没检测到起始位,整包数据全丢了。后来发现是线太长,信号上升沿变缓了。嗯,起始位的检测对信号质量很敏感,后面讲故障定位时会细说。

数据位(Data Bits)

起始位之后,就是真正的数据了。常见配置是 8 位数据(1 字节),也有 5、6、7、9 位的。数据位按LSB 先发的顺序传输。比如你要发 0x55(二进制 01010101),先发最低位 1,再发 0、1、0、1、0、1,最后发最高位 0。

我的习惯:除非有特殊需求(比如和 7 位 ASCII 的老设备通信),一律用 8 位数据位。8 位是事实上的工业标准,兼容性最好。

校验位(Parity Bit)

校验位是可选的,用于简单的错误检测。分三种:

  • 无校验(None):不发送校验位,帧里没有这个字段。
  • 奇校验(Odd):数据位中 1 的个数 + 校验位 = 奇数。
  • 偶校验(Even):数据位中 1 的个数 + 校验位 = 偶数。

举个例子:数据位是 0x55(01010101),里面有 4 个 1(偶数)。偶校验时校验位为 0,奇校验时校验位为 1。

注意:校验位只能检测奇数个 bit 的错误。如果两个 bit 同时翻转,校验位是看不出来的。我曾经在一个工控项目里被这个坑过——偶发双 bit 翻转,校验位完全没反应。后来加了 CRC 才解决。

停止位(Stop Bit)

数据位(和校验位)发完后,发送端把线拉高至少一个 bit 时间。这个高电平就是停止位。停止位可以是 1 bit、1.5 bit 或 2 bit。为什么要有停止位?说白了就是给接收端一点时间“消化”刚收到的数据,准备接收下一帧。

避坑提醒:停止位太短(比如 1 bit)时,如果接收端处理速度慢,可能来不及把数据从移位寄存器搬到缓冲区,导致下一帧数据覆盖当前数据。我建议:低速 MCU 或高波特率场景,优先用 2 位停止位。

1.3 波特率——通信的“节拍器”

波特率(Baud Rate)就是每秒传输的符号数。在 UART 里,一个符号就是一个 bit,所以波特率 = 比特率(bps)。

常见波特率有:9600、19200、38400、115200、921600 等。选哪个?我一般遵循这个原则:

场景 推荐波特率 说明
调试输出、低速传感器 9600 / 19200 抗干扰强,线长 10 米以上也能稳定
一般数据采集、控制 38400 / 57600 速度和稳定性的折中
高速数据传输、固件升级 115200 / 921600 线长建议 1 米以内,注意信号完整性

波特率偏差是个大问题。收发双方的波特率必须一致,误差一般要求 ±2% 以内。为什么是 2%?因为 UART 在数据位中间采样,如果偏差太大,采样点会逐渐偏移,最后采到错误的 bit。

我算过一笔账:115200 bps,每个 bit 约 8.68 μs。如果收发双方时钟偏差 3%,累积到第 8 个数据位时,采样点偏移了约 2 μs——已经超过半个 bit 宽度了。结果就是:数据全错,但你还查不出原因。

实战建议:选 MCU 时注意 UART 时钟源精度。内部 RC 振荡器通常有 ±1%~±3% 的误差,高波特率下建议用外部晶振。我习惯用 16 MHz 或 11.0592 MHz 晶振,分频后能精确得到标准波特率。

1.4 本章小结

这一章咱们把 UART 的底子打牢了:

  • 异步串行通信:没有时钟线,靠波特率约定和帧格式同步。
  • 帧格式:起始位(1 bit)→ 数据位(5~9 bit)→ 校验位(可选)→ 停止位(1/1.5/2 bit)。
  • 波特率:双方必须一致,误差控制在 ±2% 以内。