2. UART协议帧结构:起始位、数据位、校验位、停止位与空闲位

UART通信说白了就是一根线上一比特一比特地串着走。没有时钟线,全靠收发双方约定好速率,然后从帧结构里把数据“抠”出来。我早年刚接触嵌入式时,总觉得这玩意儿简单,结果调试时波形对不上,折腾了一整天才发现是停止位配错了。嗯,咱们今天就把这个帧结构掰开揉碎了讲清楚。

2.1 一帧数据长什么样?

UART的帧结构是固定的套路:空闲位 → 起始位 → 数据位 → 校验位(可选) → 停止位。平时线上没有数据传输时,信号线保持高电平,这就是空闲状态。一旦要发数据,先把电平拉低一个位的时间,告诉对方“注意,我要开始了”——这就是起始位。然后跟着5到8个数据位,低位在前高位在后。接着可以带一个校验位做简单的错误检测。最后拉高电平保持1位、1.5位或2位的时间,告诉对方“这帧结束了”,这就是停止位。

核心要点: 起始位永远是低电平,停止位永远是高电平。这是UART协议的基石,任何设备都必须遵守。

2.2 起始位:通信的“发令枪”

起始位的作用就是让接收端知道数据要来了。它把空闲时的高电平拉低,持续一个位时间。接收端检测到这个下降沿后,就开始按约定的波特率采样后续的位。

我在项目中遇到过一个问题:两个设备波特率差了2%,结果起始位之后的采样点逐渐偏移,到最后一个数据位时已经采到边沿上了,数据全是错的。所以起始位不仅是“发令枪”,也是接收端做时钟同步的起点。

调试技巧: 用示波器抓UART波形时,先找下降沿。从下降沿开始数位时间,就能把整帧数据解出来。我习惯把波特率对应的位宽算好,比如9600波特率下一位约104μs,直接在示波器上设好光标,一眼就能看出问题。

2.3 数据位:真正的“货物”

数据位是帧里真正要传的信息,通常是5到8位。最常见的是8位数据,对应一个字节。发送时低位在前(LSB first),这是UART的铁律。

举个例子,要发送0x55(二进制01010101),实际线上先发最低位1,然后0、1、0、1、0、1,最后发最高位0。为什么这样设计?我猜早期串口设备处理低位更方便,反正现在大家都这么用,你记住就行。

数据位配置 常见场景 说明
8位 最常用,传输ASCII字符或二进制数据 几乎所有的MCU默认配置
7位 纯ASCII文本通信(如老式终端) 节省1位,但现代系统很少用
5位或6位 特殊工业协议或老设备 遇到时注意核对数据手册
注意: 收发双方的数据位长度必须一致。我曾经调试一个GPS模块,发端设了8位数据,收端误配成7位,结果每个字节都丢了最高位,解析出来的经纬度完全不对。这种问题示波器看不出来,得用逻辑分析仪逐位核对。

2.4 校验位:简单的“防错机制”

校验位是可选的,用来检测传输过程中有没有发生单比特错误。它有两种模式:

  • 奇校验(Odd Parity): 数据位中1的个数加上校验位,总共有奇数个1。
  • 偶校验(Even Parity): 数据位中1的个数加上校验位,总共有偶数个1。

举个例子,数据位是0x55(二进制01010101),里面有4个1(偶数)。如果配偶校验,校验位就是0,保持总1数为偶数;如果配奇校验,校验位就是1,让总1数变成奇数。

说实话,校验位只能检测奇数个比特错误,如果两个比特同时翻转,校验位就发现不了。但胜在实现简单,硬件自动计算,不占CPU。我个人的习惯是:短距离板内通信不用校验位,长距离或工业现场环境一定开奇校验。

2.5 停止位:帧的“结束符”

停止位是发送端拉高电平,持续1位、1.5位或2位的时间。它给接收端留出处理上一帧数据的时间,也为下一帧的起始位下降沿做准备。

为什么会有1.5位这种奇怪的配置?这主要是早期UART芯片的遗留问题,现在基本只用1位或2位。我建议:默认用1位停止位,如果通信距离远或干扰大,可以改成2位,牺牲一点速度换取可靠性。

经验之谈: 停止位配置不匹配时,接收端往往能收到数据但会多一个帧错误标志。我在调试一个蓝牙模块时就遇到过,发端设了1位停止位,收端设了2位,结果每帧都报帧错误,但数据本身是对的。这种坑最隐蔽,排查时记得先核对停止位配置。

2.6 空闲位:线上的“沉默期”

空闲位不是帧的一部分,但它很重要。两帧之间,信号线保持高电平,这就是空闲位。它的作用有两个:

  1. 区分帧边界: 接收端通过检测高电平到低电平的跳变来识别起始位,空闲位保证了起始位之前一定是高电平。
  2. 给设备喘息时间: 有些低速MCU处理完一帧数据需要时间,空闲位就是天然的缓冲期。

你想想看,如果没有空闲位,两帧背靠背发送,接收端怎么知道前一帧结束、后一帧开始?全靠停止位的高电平和起始位的低电平之间的跳变来区分。所以空闲位虽然不传数据,但它是协议能正常工作的前提。

2.7 完整帧结构图解

下面我用一张SVG图把整个帧结构画出来,你一看就明白了。

UART帧结构示意图 UART帧结构(8位数据 + 无校验 + 1位停止位) 空闲位 高电平 起始位 低电平 D0 LSB D1 D2 D3 D4 D5 D6 D7 MSB 停止位 高电平 空闲位 高电平 下降沿 上升沿 说明: • 空闲位和停止位都是高电平,但停止位属于帧的一部分,空闲位在帧之间 • 数据位从D0(最低位)开始发送,D7(最高位)最后发送 • 如果开启校验位,它位于数据位之后、停止位之前

2.8 常见配置速查表

实际项目中,UART配置通常用一个字符串表示,比如“9600 8N1”。我来解释一下这个格式:

配置字符串 波特率 数据位 校验 停止位 典型应用
9600 8N1 9600 8 无 1 最通用,MCU默认配置
115200 8N1 115200 8 无 1 高速调试,蓝牙模块
19200 8E1 19200 8 偶校验 1 工业Modbus协议
4800 7O2 4800 7 奇校验 2 老式终端设备

“8N1”里的8是数据位,N表示无校验(None),1是停止位位数。同理“8E1”就是偶校验(Even),“7O2”就是7位数据、奇校验(Odd)、2位停止位。这个命名法很直观,我建议你记牢,以后看设备手册一眼就能读懂。

个人习惯: 新项目我默认用115200 8N1,速度快且兼容性好。如果通信不稳定,先降波特率到9600试试,再考虑加校验位或改2位停止位。别一上来就搞复杂配置,简单的最可靠。

2.9 避坑指南

最后分享几个我踩过的坑,你遇到了能少走弯路:

  • 起始位误判: 我曾经把噪声的毛刺当成起始位,导致接收端不断触发中断。后来在GPIO上加了硬件滤波,问题解决。
  • 校验位与数据位混淆: 有次调试一个传感器,手册写的是“8位数据+偶校验”,我配成了“8位数据+无校验”,结果校验位被当成数据位处理,每个字节都多了一位,解析全乱。用逻辑分析仪逐位看才找到问题。
  • 停止位不足: 在高速率下(如1Mbps以上),如果接收端处理速度跟不上,1位停止位可能不够。我建议高速通信时用2位停止位,或者降低波特率。
  • 空闲位电平反转: 有些老设备用RS232电平(±12V),空闲位是负电压,起始位是正电压。和TTL电平(3.3V/5V)正好相反。接转换芯片时一定要注意电平极性。