UART协议帧格式详解:起始位、数据位、校验位、停止位
UART通信,说白了就是一根线发、一根线收,双方约定好节奏,一比特一比特地往外送数据。这个“约定好的节奏”就是帧格式。我见过不少工程师,上来就调波特率,结果数据全是乱码,最后发现是帧格式配错了。嗯,咱们今天就把这个帧格式彻底掰开揉碎。
核心要点:UART帧是异步传输的基本单位,没有时钟线,全靠起始位同步、停止位对齐。帧格式一旦配错,通信必败。
2.1 帧结构总览
一个标准的UART数据帧,由4个部分组成:起始位、数据位、校验位(可选)、停止位。空闲时,TX线保持高电平。发送端拉低电平,接收端检测到下降沿,就知道“要发数据了”。
下面这张图,是我自己画的一个典型8N1帧结构(8数据位、无校验、1停止位),你一看就明白:
2.2 起始位:通信的“发令枪”
起始位只有1个比特宽度,固定为低电平。它的作用就是告诉接收端:“注意,我要开始发数据了!”
接收端怎么知道起始位来了?它一直在采样RX线。当检测到从高电平跳变到低电平时,就认为起始位到达。然后启动内部的波特率计数器,在起始位的中间位置再采样一次确认。如果还是低电平,好,确认是起始位,开始收后面的数据位。
我的经验:我曾经遇到过一批设备,偶尔会收到0x00字节。查了半天,发现是电源纹波太大,导致空闲时TX线出现毛刺下降沿,接收端误判为起始位。后来在TX线上加了一个100kΩ上拉电阻,问题解决。起始位的检测对边沿敏感,电源噪声是常见干扰源。
2.3 数据位:真正的信息载体
数据位是帧的核心,长度可以是5、6、7、8位,最常见的是8位。为什么?因为一个字节正好8位,方便和MCU内部数据总线对齐。
这里有个关键点:数据位是LSB First,也就是最低有效位先发。比如你要发送0x55(二进制01010101),先发的是最低位1,然后是0、1、0、1、0、1、0,最后发最高位0。接收端收到后,再按LSB First拼回来。
我见过有人用逻辑分析仪抓波形,手动解析数据时按MSB顺序读,结果怎么都对不上。你想想看,顺序反了,数据肯定不对。
| 数据位长度 | 常见场景 | 备注 |
|---|---|---|
| 5位 | 老式电传打字机、某些工业协议 | 现在很少用 |
| 6位 | 某些ASCII字符集 | 不常见 |
| 7位 | 标准ASCII(0-127) | 纯文本通信时用 |
| 8位 | 绝大多数嵌入式通信 | 最常用,推荐使用 |
注意:如果通信双方数据位长度不一致,比如发送端设8位,接收端设7位,接收端会把第8位当成校验位或停止位的一部分,结果就是数据错位、校验失败。我调试时第一件事就是检查双方的寄存器配置是否一致。
2.4 校验位:数据完整性的“小保镖”
校验位是可选的,用于检测传输过程中是否发生了单比特翻转。常见的有三种:无校验(N)、奇校验(O)、偶校验(E)。
校验位的计算范围是数据位(不包括起始位和停止位)。发送端根据数据位中1的个数,决定校验位是0还是1。
- 无校验(N):不发送校验位,帧长度最短,效率最高。适合噪声小的环境。
- 奇校验(O):数据位 + 校验位中1的总数为奇数。比如数据位有3个1,校验位就补0,使总数保持奇数。
- 偶校验(E):数据位 + 校验位中1的总数为偶数。逻辑与奇校验相反。
我个人习惯在RS485总线这种长距离、易受干扰的场景下,强制使用偶校验。为什么是偶校验?因为很多MCU的UART外设默认支持偶校验,配置起来顺手。而且偶校验在检测单比特错误时,和奇校验效果一样好。
避坑指南:我曾经在一个项目中,发送端设了偶校验,接收端设了无校验。结果接收端把校验位当成数据位的一部分,每个字节都多了一个比特,数据全乱。用逻辑分析仪一看,帧长度对不上,才找到问题。记住:校验位配置必须两端一致。
2.5 停止位:帧的“句号”
停止位固定为高电平,长度可以是1、1.5或2个比特时间。它的作用是给接收端一个缓冲,让它有时间处理刚收到的字节,准备接收下一帧。
为什么会有1.5和2个比特的选项?
- 1位停止位:最常用,效率最高。适合大多数场景。
- 1.5位停止位:很少见,某些老式UART芯片用。我做了十几年嵌入式,只用过一次。
- 2位停止位:给接收端更多处理时间。在低速MCU或高波特率场景下有用。比如用1MHz波特率时,MCU可能来不及处理中断,2位停止位能缓解压力。
你想想看,如果停止位太短,接收端还没处理完上一帧,下一帧的起始位就来了,就会发生帧错误(Framing Error)。逻辑分析仪上会显示“帧错误”标志,停止位采样为低电平。
2.6 常见帧格式组合
实际项目中,帧格式通常写成“数据位-校验位-停止位”的形式。比如8N1、8E1、8O2等。下面这个表,是我整理的常用组合:
| 格式 | 数据位 | 校验位 | 停止位 | 帧总长度(比特) | 典型应用 |
|---|---|---|---|---|---|
| 8N1 | 8 | 无 | 1 | 10 | 最通用,调试首选 |
| 8E1 | 8 | 偶校验 | 1 | 11 | RS485总线、工业通信 |
| 8O1 | 8 | 奇校验 | 1 | 11 | 某些PLC协议 |
| 7E1 | 7 | 偶校验 | 1 | 10 | ASCII文本通信 |
| 8N2 | 8 | 无 | 2 | 11 | 低速MCU、高波特率 |
我的建议:如果你不确定对方的帧格式,先用逻辑分析仪抓一帧波形。数一下从起始位下降沿到停止位上升沿之间有多少个比特时间,再减去起始位和停止位,就能反推出数据位和校验位的长度。这是最直接的排查方法。
2.7 用逻辑分析仪验证帧格式
理论说完了,咱们看看实际怎么抓。假设你配置了8N1格式,波特率115200。用逻辑分析仪抓TX引脚,你会看到这样的波形:
- 空闲高电平 → 下降沿(起始位开始)
- 低电平持续1个比特时间(约8.68μs @115200)
- 8个数据位,LSB先发,每个比特也是8.68μs
- 高电平持续1个比特时间(停止位)
- 回到空闲高电平,等待下一帧
逻辑分析仪会自动解析这些电平,显示出十六进制数据。如果解析出来的数据和发送的不一致,先检查帧格式配置。我遇到过最坑的一次,是某国产MCU的UART外设,数据位顺序默认是MSB First,和标准UART相反。不看数据手册根本发现不了。
重要提醒:逻辑分析仪采样率至少要是波特率的4倍以上,推荐8倍。比如115200波特率,采样率至少要460.8kHz,最好用1MHz以上。采样率不够,可能漏掉起始位的下降沿,导致帧同步失败。
好了,UART帧格式的四个部分就讲到这里。起始位是发令枪,数据位是正文,校验位是防错码,停止位是句号。记住这个比喻,以后调通信心里就有底了。