3. UART协议详解:起始位、数据位、校验位、停止位、波特率计算

UART,说白了就是串口通信的物理层规矩。很多工程师一上来就调库函数,遇到通信乱码就抓瞎。我个人习惯是先把协议帧掰开揉碎了看,这样定位问题才快。

UART 的帧结构其实很简单,就 5 个部分:空闲位、起始位、数据位、校验位、停止位。咱们一个一个说。

3.1 帧结构拆解

先看一张完整的 UART 数据帧结构图,心里有个全局印象。

UART 标准数据帧结构 空闲 高电平 起始 低电平 D0 LSB D1 D2 D3 D4 D5 D6 D7 MSB 校验 可选 停止 高电平 下一帧 起始位 说明: • 数据位传输顺序:LSB(最低有效位)先发,MSB(最高有效位)后发 • 校验位和停止位是可配置的,空闲位和起始位是固定的

3.2 起始位与停止位

起始位:一个位宽的低电平。它告诉接收方「注意,数据要来了」。空闲时总线是高电平,突然拉低,这就是起始信号。

停止位:一个或两个位宽的高电平。它告诉接收方「这帧数据结束了」。停止位之后总线恢复高电平,等待下一帧。

我的经验:停止位长度可以配成 1、1.5 或 2 位。大多数场景用 1 位停止位就够了。如果通信距离远、干扰大,我建议用 2 位停止位,给接收方多留点处理时间。

3.3 数据位

数据位可以是 5、6、7、8 位,最常见的是 8 位。传输顺序是 LSB 先发,MSB 后发。

举个例子,你要发 0x55(二进制 01010101):

  • LSB 是 1(最低位)
  • MSB 是 0(最高位)
  • 实际线上顺序:1 → 0 → 1 → 0 → 1 → 0 → 1 → 0

你想想看,如果接收方按 MSB 先收,那 0x55 就变成 0xAA 了,全乱套。所以协议规定必须 LSB first,这是硬规矩。

3.4 校验位

校验位是可选的,用来做简单的错误检测。常见三种模式:

校验模式 逻辑 适用场景
无校验 (None) 不发送校验位 近距离、干扰小、追求速度
奇校验 (Odd) 数据位中 1 的个数 + 校验位 = 奇数 对数据完整性有一定要求
偶校验 (Even) 数据位中 1 的个数 + 校验位 = 偶数 与奇校验对称,看系统习惯

注意:校验位只能检测奇数个比特错误。如果两个比特同时翻转,校验位是看不出来的。我曾经在一个工业项目里遇到过这种情况,后来加了 CRC 才搞定。

3.5 波特率计算

波特率就是每秒传输的比特数,单位是 bps(bits per second)。

一帧的总位数 = 1(起始位)+ 数据位位数 + 校验位(0 或 1)+ 停止位位数

举个例子,8 数据位 + 无校验 + 1 停止位:

总位数 = 1 + 8 + 0 + 1 = 10 位

波特率 9600 bps 时:
每帧时间 = 10 / 9600 ≈ 1.042 ms
每秒传输帧数 = 9600 / 10 = 960 帧/秒

常用波特率有:9600、19200、38400、115200、921600。我个人习惯是:

  • 调试阶段:用 115200,速度快,省时间
  • 量产产品:用 9600 或 19200,抗干扰能力强

避坑指南:我曾经在一个项目里,主控和从机的波特率都配成 115200,但主控的晶振是 16MHz,从机是 12MHz,结果两边算出来的实际波特率差了 2.3%。通信偶尔丢字节,查了两天才发现是波特率误差超标了。记住,波特率误差必须控制在 ±2% 以内,最好在 ±1% 以内。

3.6 波特率发生器配置

以 STM32 为例,波特率计算公式:

波特率 = fCK / (16 × USARTDIV)

其中:
fCK = 外设时钟频率
USARTDIV = 一个 16 位寄存器值,包含整数和小数部分

实际配置时,我们一般用 CubeMX 或直接算好 USARTDIV 值写寄存器。但理解原理很重要——波特率误差主要来自时钟分频的取整误差。

目标波特率 时钟频率 实际波特率 误差
115200 16 MHz 115200 0%
115200 8 MHz 114943 -0.22%
9600 16 MHz 9615 +0.16%

小技巧:选时钟频率时,尽量选能被目标波特率 × 16 整除的值。比如 115200 × 16 = 1.8432 MHz,很多 UART 芯片专门用这个频率做时钟源,误差为 0。

3.7 常见配置速查表

下面是我整理的最常用配置组合,直接拿去用:

场景 数据位 校验位 停止位 波特率
调试输出 8 无 1 115200
传感器通信 8 无 1 9600
工业现场总线 8 偶校验 1 19200
长距离传输 7 奇校验 2 4800