2. UART 数据帧结构:起始位、数据位、校验位、停止位,帧格式详解

UART 通信,说白了就是两个设备之间通过一根 TX 和一根 RX 线,按位串行地聊天。但聊天也得有规矩——什么时候开始说、说几个字、说完怎么确认,这些规矩就定义在数据帧里。

我刚开始做嵌入式时,总觉得帧格式就是配几个寄存器的事。直到有一次调试一个工业传感器,数据总是偶尔错一个字节,查了两天才发现是停止位配置和对方不匹配。嗯,从那以后,我对帧结构的每个 bit 都不敢马虎了。

2.1 数据帧的完整结构

一个标准的 UART 数据帧,由四个部分组成:

  • 起始位(Start Bit)—— 1 位,固定为低电平
  • 数据位(Data Bits)—— 5~9 位,通常为 8 位
  • 校验位(Parity Bit)—— 0 或 1 位,可选
  • 停止位(Stop Bit)—— 1、1.5 或 2 位,固定为高电平

你想想看,这就像一封信:起始位是信封上的「收件人地址」,数据位是信的内容,校验位是信封上的「检查封条」,停止位是「此致 敬礼」。

关键认知:UART 是异步通信,没有时钟线。收发双方靠约定的波特率(bit 速率)和帧格式来同步。起始位和停止位就是替代时钟线的「握手信号」。

2.2 起始位 —— 通信的「起跑枪」

空闲时,TX 线保持高电平。当发送端要发数据时,先把 TX 线拉低一个 bit 时间。这个从高到低的跳变,就是起始位。

接收端一直在采样 RX 线。它检测到这个下降沿后,就知道「要开始收数据了」。然后根据约定的波特率,在后续每个 bit 的中间位置采样数据位。

我个人习惯在逻辑分析仪上看起始位——那个下降沿特别明显。如果波形上一直高电平,说明总线空闲;如果看到连续的低电平,那可能是起始位被误判了。

实战技巧:我曾经遇到过一个问题:两个设备波特率都是 115200,但一个用 16 倍过采样,另一个用 8 倍过采样。结果起始位的下降沿检测时机差了半个 bit,导致整个帧都错位。所以,起始位检测的精度,直接决定了通信的可靠性。

2.3 数据位 —— 真正的「信息载荷」

起始位之后,就是数据位。通常我们配置为 8 位数据,但实际支持 5~9 位。

数据位的发送顺序是 LSB(最低有效位)在前,MSB(最高有效位)在后。比如你要发 0x55(二进制 01010101),实际线上先发最低位 1,再发 0、1、0、1、0、1,最后发最高位 0。

为什么这样设计?我猜是为了早期硬件实现方便——移位寄存器从低位开始移出,电路更简单。

数据位长度 常见场景 说明
5 位 老式电传打字机 现在基本不用
7 位 ASCII 字符传输 早期 Modbus 等协议用
8 位 最常用 一个字节,通用性最强
9 位 多机通信地址帧 如 RS-485 多机协议

注意:收发双方的数据位长度必须一致。我曾经见过一个项目,发送端配了 8 位数据,接收端误配成 7 位。结果每个字节都少收 1 位,后面的数据全部错位,而且 CRC 校验还查不出来——因为错位后数据看起来「合理」但完全不对。

2.4 校验位 —— 数据的「体检医生」

校验位是可选的。它用来检测数据位在传输过程中是否发生了单 bit 翻转。

常见的校验方式有三种:

  • 无校验(None):不发送校验位,帧长度最短
  • 奇校验(Odd):数据位中 1 的个数 + 校验位 = 奇数
  • 偶校验(Even):数据位中 1 的个数 + 校验位 = 偶数

举个例子:数据位是 0x55(二进制 01010101),其中 1 的个数是 4(偶数)。

  • 如果配偶校验,校验位为 0,保持总 1 数为偶数
  • 如果配奇校验,校验位为 1,使总 1 数变为奇数

我个人习惯在噪声环境(比如电机附近、长线传输)中开启奇校验。为什么选奇校验?因为偶校验如果所有位都变成 0,1 的个数还是偶数,校验会误判为正确。奇校验能避免这种全 0 的极端情况。

避坑指南:我曾经在一个项目中,发送端配了偶校验,接收端配了无校验。结果接收端把校验位当成数据位的第 9 位来读,每个字节都多了一个 bit,数据全乱。所以,校验位的配置必须严格一致——要么都开、要么都关、要么校验方式相同。

2.5 停止位 —— 帧的「结束符」

数据位(和校验位)发完后,发送端把 TX 线拉高,保持至少 1 个 bit 时间。这个高电平就是停止位。

停止位的作用有两个:

  1. 帧间分隔:告诉接收端这一帧结束了
  2. 时钟同步:给接收端一点时间处理刚收到的字节

停止位长度可以是 1、1.5 或 2 个 bit。1.5 位比较少见,主要在早期某些 UART 芯片中使用。2 位停止位常用于低速设备或噪声环境,给接收端更多处理时间。

停止位长度 适用场景 备注
1 位 标准通信,波特率较高 最常用
1.5 位 老式 UART 芯片 现在很少用
2 位 低速、噪声环境 增加帧间隔,提高可靠性

注意:停止位配置错误是最常见的 UART 通信故障之一。如果接收端检测不到停止位的高电平,它会报「帧错误」(Frame Error)。我调试时第一件事就是看 UART 的状态寄存器——如果帧错误标志置位,十有八九是停止位不匹配。

2.6 完整帧格式图解

下面我用一张 SVG 图把整个帧结构画出来。你可以看到每个 bit 在时间轴上的位置和电平状态。

UART 数据帧结构(8N1 格式示例) 空闲 起始位 数据位(8 位:LSB → MSB) 校验位 停止位 空闲 对应波形(高电平 = 1,低电平 = 0) 下降沿 触发接收 每个 bit 在中间采样 高电平 帧结束 说明:8N1 格式 = 8 位数据 + 无校验 + 1 位停止位。这是最常用的 UART 帧格式。

2.7 常见帧格式速查

实际项目中,你可能会遇到各种帧格式组合。我整理了一个速查表,方便你配置时参考:

格式名称 数据位 校验位 停止位 帧总长度 典型应用
8N1 8 无 1 10 bit 最通用,PC 串口默认
8E1 8 偶校验 1 11 bit Modbus RTU
8O1 8 奇校验 1 11 bit 工业传感器
7E1 7 偶校验 1 10 bit ASCII 文本传输
8N2 8 无 2 11 bit 噪声环境、低速设备
9N1 9 无 1 11 bit 多机通信地址帧

我的建议:如果你不确定对方用什么格式,先试 8N1。这是最通用的配置,大部分 UART 设备默认就是 8N1。如果通信不稳定,再根据对方手册调整校验位和停止位。

2.8 帧格式配置实战要点

配置 UART 帧格式时,有几点我特别想强调:

  1. 先确认对方规格:拿到一个设备,第一件事是看它的 UART 参数——波特率、数据位、校验位、停止位。少一个都不行。
  2. 校验位不是万能的:奇偶校验只能检测奇数个 bit 翻转。如果两个 bit 同时翻转,校验会误判为正确。对可靠性要求高的场景,建议在应用层加 CRC 校验。
  3. 停止位不是越长越好:2 位停止位虽然可靠,但会降低有效数据传输率。在 115200 波特率下,8N1 每秒约传 11520 字节,8N2 只有约 10472 字节,差了近 10%。
  4. 帧格式错误的表现:如果配置不对,通常表现为数据全乱、偶尔丢字节、或者 UART 状态寄存器报帧错误/校验错误。我调试时习惯先看状态寄存器,再抓波形。

重要提醒:有些 MCU 的 UART 外设支持自动检测帧格式(Auto Baud Rate Detection),但我不建议在生产环境中依赖这个功能。它只在特定条件下可靠,而且会增加启动时间。老老实实把帧格式写死在代码里,比什么都靠谱。

好了,帧格式的结构就讲到这里。你理解了起始位、数据位、校验位、停止位各自的作用和配置要点,后面写驱动时就知道每个寄存器该怎么配了。