3、UART协议详解:起始位/数据位/校验位/停止位、波特率误差容限、常见UART配置

UART,说白了就是串口通信的物理层规矩。很多工程师觉得它简单,不就是TX、RX两根线嘛。但我在项目中见过太多因为UART配置不对导致的“幽灵故障”——数据偶尔错一位,或者两台设备就是聊不到一块去。今天咱们把UART协议从头到尾捋一遍,把那些容易踩的坑都填上。

3.1 UART帧结构:一个字节是怎么传出去的?

UART是异步通信,没有时钟线。收发双方靠约定的“帧格式”来同步。一个完整的UART帧长这样:

UART帧结构(8-N-1为例):
空闲(高电平)→ 起始位(低电平)→ 数据位(LSB先发)→ 校验位(可选)→ 停止位(高电平)→ 空闲

咱们拆开来看每个部分:

字段 长度 电平 说明
空闲位 任意 高电平(逻辑1) 总线空闲状态,TX保持高
起始位 1 bit 低电平(逻辑0) 通知接收方“我要发数据了”
数据位 5~9 bit LSB先发 实际传输的数据内容
校验位 0/1 bit 奇/偶/无 简单检错,不是纠错
停止位 1/1.5/2 bit 高电平(逻辑1) 帧结束标志,给接收方处理时间

起始位是UART通信的“发令枪”。总线空闲时是高电平,一旦TX拉低,接收方就知道要开始采样了。我调试过一个设备,示波器上看起始位总是毛刺——后来发现是GPIO初始化时没先拉高,导致上电瞬间误触发。

数据位通常用8位,因为一个字节正好8位。但有些老设备用7位数据位+1位校验,或者9位数据位做多机地址识别。嗯,RS485多机通信里9位模式很常见,后面章节会细讲。

校验位分奇校验和偶校验。奇校验:数据位+校验位里1的个数为奇数。偶校验:1的个数为偶数。说白了就是凑数。但注意——校验位只能检错,不能纠错。发现错了怎么办?扔掉重传。

停止位至少1位,给接收方留时间处理刚收到的字节。有些低速设备用2位停止位,我见过一个老式PLC就这么配,当时还纳闷为什么通信这么慢。

3.2 波特率误差容限:为什么9600比115200更稳?

波特率就是每秒传多少位。9600波特率就是每秒传9600个bit。但问题是——收发双方的时钟不可能完全一样。

你想想看,两个单片机各自用晶振分频产生波特率时钟。晶振有误差,分频有舍入,两边加起来可能差个百分之几。这个误差积累到一帧的末尾,就可能采错位。

波特率误差容限经验值:
  • 总误差(发送方+接收方)应 < 2%
  • 单边误差应 < 1%
  • 帧越长(数据位+停止位多),容限越紧
  • 115200以上建议用有源晶振或内部PLL校准

为什么9600比115200稳?因为波特率越低,每个bit的时间越长,同样的时钟误差造成的采样点偏移绝对值越小。举个例子:

波特率 每bit时间 1%误差偏移 8位数据后累计偏移
9600 104.17 μs 1.04 μs 8.33 μs(约8% bit宽度)
115200 8.68 μs 0.087 μs 0.69 μs(约8% bit宽度)
921600 1.085 μs 0.011 μs 0.087 μs(约8% bit宽度)

看到没?虽然百分比一样,但高速率下绝对时间窗口更小,对采样点位置更敏感。我曾经在一个项目里用内部RC振荡器跑115200,结果温度一变化就丢数据——换成外部晶振后问题消失。

我的习惯: 能用外部晶振就别用内部RC。如果必须用内部RC,波特率别超过38400。RS485组网时,所有节点的波特率误差方向最好一致——都用同一批晶振。

3.3 常见UART配置:8-N-1为什么是默认选项?

你打开任何一个MCU的UART初始化代码,大概率看到这样的配置:

// STM32 UART配置示例 - 8-N-1
UART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;   // 8位数据
huart1.Init.StopBits = UART_STOPBITS_1;         // 1位停止
huart1.Init.Parity = UART_PARITY_NONE;          // 无校验
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart1);

8-N-1就是:8位数据、无校验、1位停止。为什么它成了事实标准?

  • 8位数据:一个字节正好8位,不用拼凑
  • 无校验:省掉1位,帧效率更高(8/10=80% vs 8/11=72.7%)
  • 1位停止:够用,不浪费带宽

但有些场景需要改配置:

场景 推荐配置 原因
普通调试/数据采集 8-N-1 效率高,兼容性好
噪声环境(工业现场) 8-E-1 或 8-O-1 校验位能检出单bit翻转
低速老旧设备 7-E-1 或 7-O-1 兼容ASCII 7位编码
RS485多机通信 9-N-1(9位模式) 第9位做地址/数据标志
高可靠长距离 8-N-2 2位停止给接收方更多处理时间

我个人习惯是:调试阶段用8-N-1,简单。正式产品如果走RS485,会考虑9位模式或者加校验。但注意——收发双方配置必须完全一致,包括停止位位数。我曾经被坑过一次:设备A配了1位停止,设备B配了2位停止,结果能收到数据但偶尔丢帧,查了两天才发现。

3.4 UART配置速查表

下面这张表是我自己整理的项目常用配置,直接拿去用:

参数 常用值 注意事项
波特率 9600 / 19200 / 38400 / 115200 RS485建议≤115200,长距离降速
数据位 8(默认)/ 7 / 9 9位用于多机地址识别
校验位 None / Even / Odd Even更常用,因为0的个数稳定
停止位 1(默认)/ 2 2位用于低速或高噪声环境
流控制 None / RTS/CTS RS485不用硬件流控
避坑指南: 我曾经在调试一个多机RS485网络时,所有设备都配了115200 8-N-1,但就是通信不稳定。后来用示波器抓波形发现——主机的实际波特率是115200,但从机因为晶振偏差实际跑了114800,误差0.35%。单看没问题,但一帧10位累计误差3.5%,刚好踩在停止位采样边缘。换成9600后一切正常。

3.5 UART帧结构知识图谱

下面这张图把UART帧的完整结构和配置选择逻辑串起来了:

UART帧结构 & 配置选择逻辑 UART 一帧数据(以8-N-1为例,共10位) 起始位 数据位(8位,LSB先发) 校验位 停止位 空闲(高电平) UART 配置选择逻辑 波特率选择 低速(9600~38400)更稳定 高速(115200+)需外部晶振 数据位选择 8位:通用标准 9位:RS485多机地址 校验 & 停止位 无校验+1停止:效率最高 偶校验+2停止:高可靠 ⚠ 关键原则 • 收发双方配置必须完全一致(波特率、数据位、校验、停止位) • 总波特率误差应 < 2%,单边 < 1% • 噪声环境优先选偶校验 + 2位停止位 • RS485多机通信推荐9位模式或增加帧头地址

UART协议看着简单,但每个参数背后都有取舍。波特率选高了速度快但容错差,校验位加了可靠但效率降,停止位多了稳但带宽浪费。没有绝对正确的配置,只有最适合你项目的配置。

我的建议: 新项目起步时先用9600 8-N-1打通通信,确认硬件没问题后再逐步提速。别一上来就115200,万一通信不通,你分不清是配置问题还是硬件问题。调试RS485网络时尤其如此——先低速打通,再优化性能。