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、1.5 或 2 个 bit。1.5 位比较少见,主要在早期某些 UART 芯片中使用。2 位停止位常用于低速设备或噪声环境,给接收端更多处理时间。
| 停止位长度 | 适用场景 | 备注 |
|---|---|---|
| 1 位 | 标准通信,波特率较高 | 最常用 |
| 1.5 位 | 老式 UART 芯片 | 现在很少用 |
| 2 位 | 低速、噪声环境 | 增加帧间隔,提高可靠性 |
注意:停止位配置错误是最常见的 UART 通信故障之一。如果接收端检测不到停止位的高电平,它会报「帧错误」(Frame Error)。我调试时第一件事就是看 UART 的状态寄存器——如果帧错误标志置位,十有八九是停止位不匹配。
2.6 完整帧格式图解
下面我用一张 SVG 图把整个帧结构画出来。你可以看到每个 bit 在时间轴上的位置和电平状态。
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 帧格式时,有几点我特别想强调:
- 先确认对方规格:拿到一个设备,第一件事是看它的 UART 参数——波特率、数据位、校验位、停止位。少一个都不行。
- 校验位不是万能的:奇偶校验只能检测奇数个 bit 翻转。如果两个 bit 同时翻转,校验会误判为正确。对可靠性要求高的场景,建议在应用层加 CRC 校验。
- 停止位不是越长越好:2 位停止位虽然可靠,但会降低有效数据传输率。在 115200 波特率下,8N1 每秒约传 11520 字节,8N2 只有约 10472 字节,差了近 10%。
- 帧格式错误的表现:如果配置不对,通常表现为数据全乱、偶尔丢字节、或者 UART 状态寄存器报帧错误/校验错误。我调试时习惯先看状态寄存器,再抓波形。
重要提醒:有些 MCU 的 UART 外设支持自动检测帧格式(Auto Baud Rate Detection),但我不建议在生产环境中依赖这个功能。它只在特定条件下可靠,而且会增加启动时间。老老实实把帧格式写死在代码里,比什么都靠谱。
好了,帧格式的结构就讲到这里。你理解了起始位、数据位、校验位、停止位各自的作用和配置要点,后面写驱动时就知道每个寄存器该怎么配了。