3、UART协议详解:起始位/数据位/校验位/停止位、波特率误差容限、常见UART配置
UART,说白了就是串口通信的物理层规矩。很多工程师觉得它简单,不就是TX、RX两根线嘛。但我在项目中见过太多因为UART配置不对导致的“幽灵故障”——数据偶尔错一位,或者两台设备就是聊不到一块去。今天咱们把UART协议从头到尾捋一遍,把那些容易踩的坑都填上。
3.1 UART帧结构:一个字节是怎么传出去的?
UART是异步通信,没有时钟线。收发双方靠约定的“帧格式”来同步。一个完整的UART帧长这样:
空闲(高电平)→ 起始位(低电平)→ 数据位(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,结果温度一变化就丢数据——换成外部晶振后问题消失。
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不用硬件流控 |
3.5 UART帧结构知识图谱
下面这张图把UART帧的完整结构和配置选择逻辑串起来了:
UART协议看着简单,但每个参数背后都有取舍。波特率选高了速度快但容错差,校验位加了可靠但效率降,停止位多了稳但带宽浪费。没有绝对正确的配置,只有最适合你项目的配置。