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,偶数)
3.4 停止位:帧结束的标志
数据位和校验位发完后,发送端把电平拉高并保持至少一个 bit 时间,这就是停止位。停止位可以是 1、1.5 或 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 的完整波形:
3.7 用逻辑分析仪看帧结构
抓波形时,我习惯先看这几个关键点:
- 看起始位—— 下降沿是否干净?有没有毛刺?
- 数数据位—— 是不是 8 位?顺序对不对?
- 看停止位—— 高电平持续时间够不够?有没有被拉低?
- 看帧间隔—— 两帧之间有没有足够的高电平空闲时间?
你想想看,如果连帧结构都看不懂,后面怎么定位问题?所以这一章的内容,是后续所有调试工作的基础。建议你拿逻辑分析仪抓一段实际波形,对照着上面的 SVG 图,一个一个 bit 地数一遍。嗯,动手做一次,比看十遍书都管用。