3. UART 数据帧结构详解:起始位、数据位、校验位、停止位

UART 通信说白了就是「按约定好的格式,一个 bit 一个 bit 地串行发送」。这个约定好的格式,就是我们常说的数据帧结构。我见过不少新手工程师,上来就调代码,波形抓出来却看不懂,就是因为对帧结构理解不够深。

一个完整的 UART 数据帧,通常包含以下几个部分:

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

下面我们逐个拆解,看看每个部分到底在干什么。

3.1 起始位:通信开始的信号

空闲时,UART 总线保持高电平。当发送端要开始传输时,先把电平拉低并保持一个 bit 时间,这就是起始位。

为什么是低电平?因为这样接收端可以通过下降沿检测到「有人要发数据了」。我在项目中遇到过一个问题:某次设备间歇性丢包,抓波形发现起始位被噪声干扰成了毛刺,接收端误判了起始时刻。后来在硬件上加了个 RC 滤波,问题就解决了。

小提示:起始位的下降沿是接收端同步时钟的关键。如果这个沿不干净,后面的数据大概率全错。

3.2 数据位:真正要传的信息

起始位之后,就是数据位。常见的是 8 位数据,但也可以配置为 5、6、7 或 9 位。

数据位的发送顺序是低位在前(LSB first)。举个例子,你要发送 0x55(二进制 01010101),实际在线上先发最低位 1,再依次发 0、1、0、1、0、1、0。

我个人习惯用逻辑分析仪抓包时,先看数据位的顺序对不对。有一次调试一个 GPS 模块,输出数据全是乱码,抓波形发现数据位顺序被配置成了 MSB first,跟接收端对不上。改过来就好了。

数据位长度 常见用途 备注
5 位 老式电传机、某些工业协议 现在很少用
7 位 ASCII 字符传输 早期 Modbus 常用
8 位 二进制数据、标准 UART 最常用,没有之一
9 位 多机通信地址帧 某些 RS-485 协议用

3.3 校验位:简单的错误检测

校验位是可选的,用来检测数据在传输过程中有没有被干扰。常见的有三种模式:

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

举个例子,数据位是 0x55(二进制 01010101,有 4 个 1):

  • 奇校验:校验位为 1(让总 1 数变成 5,奇数)
  • 偶校验:校验位为 0(总 1 数保持 4,偶数)
注意:校验位只能检测奇数个 bit 错误。如果两个 bit 同时翻转,校验位是查不出来的。我曾经在一个强干扰环境中吃过这个亏,后来改用了带 CRC 的协议。

3.4 停止位:帧结束的标志

数据位和校验位发完后,发送端把电平拉高并保持至少一个 bit 时间,这就是停止位。停止位可以是 1、1.5 或 2 位。

为什么要有停止位?两个作用:

  1. 告诉接收端「这一帧结束了」
  2. 给接收端留出处理时间,准备接收下一帧

我建议在高速通信(如 921600 bps)时使用 2 位停止位,能有效降低接收端处理不过来导致的丢帧。低速场景(9600 bps 以下)用 1 位就够了。

3.5 常见帧格式组合

实际项目中,帧格式通常用「数据位-校验位-停止位」来描述。下面是几种最常见的组合:

简称 数据位 校验位 停止位 典型应用
8N1 8 无 1 最通用,Arduino、PC 串口默认
8E1 8 偶校验 1 Modbus RTU、某些工业设备
8O1 8 奇校验 1 某些老式终端设备
7E1 7 偶校验 1 ASCII 字符传输
8N2 8 无 2 高速通信、抗干扰场景

我个人最常用的是 8N1,简单可靠。如果项目对数据完整性有要求,我会换成 8E1。

3.6 完整帧结构图解

下面这张 SVG 图展示了 8N1 格式下,发送 0x55 的完整波形:

8N1 帧结构示例:发送 0x55(01010101) 空闲 高电平 起始 低电平 D0 1 D1 0 D2 1 D3 0 D4 1 D5 0 D6 1 D7 0 停止 高电平 关键说明 • 发送顺序:LSB first,D0(最低位)最先发送 • 0x55 = 01010101(二进制),D0=1, D1=0, D2=1, D3=0, D4=1, D5=0, D6=1, D7=0 • 8N1 格式:8 数据位 + 无校验 + 1 停止位,共 10 bits 一帧

3.7 用逻辑分析仪看帧结构

抓波形时,我习惯先看这几个关键点:

  1. 看起始位—— 下降沿是否干净?有没有毛刺?
  2. 数数据位—— 是不是 8 位?顺序对不对?
  3. 看停止位—— 高电平持续时间够不够?有没有被拉低?
  4. 看帧间隔—— 两帧之间有没有足够的高电平空闲时间?
实战经验:我曾经调试一个蓝牙模块,抓波形发现停止位被拉低了。查了半天,发现是发送端波特率偏了 3%,导致停止位采样点落在了数据位上。换了个精度更高的晶振就解决了。

你想想看,如果连帧结构都看不懂,后面怎么定位问题?所以这一章的内容,是后续所有调试工作的基础。建议你拿逻辑分析仪抓一段实际波形,对照着上面的 SVG 图,一个一个 bit 地数一遍。嗯,动手做一次,比看十遍书都管用。