1. UART基础回顾:串行通信原理与帧结构详解
做嵌入式这么多年,UART 是我打交道最多的接口之一。说它简单吧,确实就两根线;说它坑多吧,我早期一个项目因为停止位配置不对,整批设备在高温下丢数据,排查了整整三天。所以这一章,咱们把 UART 的底子彻底捋一遍。
1.1 串行通信 vs 并行通信
先搞清楚一个基本概念:为什么叫“串行”?
并行通信是一次传多位,比如 8 位数据同时通过 8 根线传输。速度快,但线多、成本高、抗干扰差。串行通信则是一位一位地传,只用 1~2 根数据线。速度看似慢,但线少、距离远、成本低。
我个人的习惯是:板内近距离、对延迟敏感的场景用并行(比如 LCD 屏接口);板间通信、长距离、或者引脚紧张时,无脑选串行。UART 就是串行通信的典型代表。
| 对比项 | 串行通信 | 并行通信 |
|---|---|---|
| 数据线数量 | 1~2 根 | 8 根以上 |
| 传输距离 | 远(可达几十米) | 近(通常 < 1 米) |
| 抗干扰能力 | 强 | 弱 |
| 硬件成本 | 低 | 高 |
| 典型应用 | UART, SPI, I2C | 并口打印机, 旧式总线 |
1.2 UART 帧结构详解
UART 传输的基本单位叫“帧”。一帧数据包含以下几个部分:
- 起始位:1 位,固定为低电平(逻辑 0)。告诉接收端“准备接收”。
- 数据位:5~9 位,常见为 8 位。这是真正的数据内容。
- 校验位:0 或 1 位,可选。用于简单的错误检测。
- 停止位:1、1.5 或 2 位,固定为高电平(逻辑 1)。表示一帧结束。
你想想看,为什么要有起始位和停止位?因为 UART 是异步通信,没有单独的时钟线。接收端必须靠起始位的下降沿来同步时钟,再靠停止位来确认帧结束。没有它们,收发双方根本对不上节奏。
核心要点:UART 帧结构 = 起始位(1) + 数据位(5~9) + 校验位(0/1) + 停止位(1/1.5/2)
1.3 起始位详解
起始位是 UART 通信的“发令枪”。
空闲时,TX 线保持高电平。当发送端要发数据时,先把 TX 线拉低一个位时间。接收端检测到这个下降沿后,就知道“数据要来了”,开始按约定的波特率采样后续的位。
我曾经遇到过一个坑:某款 MCU 的 UART 模块对起始位的最小脉宽有要求,如果发送端的下降沿不够陡峭(比如线缆太长导致信号边沿变缓),接收端可能检测不到起始位,整帧数据就丢了。后来我在 TX 线上加了一个 10kΩ 上拉电阻,问题解决。
注意:起始位的下降沿是 UART 同步的关键。信号线过长、电容过大,会导致边沿变缓,接收端可能漏检起始位。建议 TX/RX 走线尽量短,必要时加施密特触发器整形。
1.4 数据位详解
数据位是帧的核心,承载实际信息。
常见配置是 8 位数据,因为一个字节正好 8 位,方便和内存、寄存器对接。但有些老协议或特殊场景会用 7 位(比如 ASCII 字符传输)或 9 位(比如多机通信中的地址/数据标志)。
数据位的传输顺序是 LSB 优先(最低有效位在前)。举个例子,要发送 0x55(二进制 01010101),实际线上先发最低位 1,再依次发 0、1、0、1、0、1,最后发最高位 0。
我建议新手在调试时,用逻辑分析仪抓一下波形,对照着看数据位的顺序,印象会非常深刻。
| 数据位长度 | 常见用途 | 备注 |
|---|---|---|
| 5 位 | 老式电传机 | 现在极少用 |
| 7 位 | ASCII 字符传输 | 部分 Modbus 协议用 |
| 8 位 | 通用数据通信 | 最常用,默认配置 |
| 9 位 | 多机通信地址标志 | RS-485 多机模式常见 |
1.5 校验位详解
校验位用于简单的错误检测。UART 支持三种校验模式:
- 无校验 (None):不发送校验位,帧长度最短。
- 奇校验 (Odd):数据位 + 校验位中“1”的个数为奇数。
- 偶校验 (Even):数据位 + 校验位中“1”的个数为偶数。
举个例子:数据位是 0x55(二进制 01010101,有 4 个 1)。
- 奇校验:校验位应为 1(使总 1 数为 5,奇数)。
- 偶校验:校验位应为 0(使总 1 数为 4,偶数)。
说实话,校验位只能检测奇数个位的翻转错误。如果两个位同时翻转,校验位就检测不出来了。所以它只适合低噪声环境。我在工业现场一般会用更可靠的 CRC 校验,而不是依赖 UART 的奇偶校验。
经验之谈:如果通信环境噪声较大,建议关闭 UART 的硬件校验,在应用层自己加 CRC 或累加和校验。硬件校验位反而会占用一个位的时间,降低有效数据吞吐量。
1.6 停止位详解
停止位标志一帧的结束,固定为高电平。
停止位可以是 1 位、1.5 位或 2 位。为什么会有 1.5 位这种奇怪的长度?因为早期有些 UART 硬件用 1.5 位来给接收端留出处理时间。现在大多数场景用 1 位停止位就够了。
什么时候用 2 位停止位?
- 波特率较高时(比如 115200 以上),给接收端更多时间处理。
- 使用机械继电器或光耦隔离时,信号延迟较大,需要更长的停止位来保证帧间隔。
我记得有一次调试一个 250kbps 的 UART 链路,接收端总是偶尔丢帧。查了半天,发现是停止位太短,接收端的 FIFO 没来得及把数据搬走,下一帧就来了。把停止位从 1 改成 2,问题消失。
1.7 UART 帧结构完整图示
下面我用一张 SVG 图把整个帧结构画出来,方便你对照理解。
1.8 常见帧格式配置
实际项目中,最常见的帧格式是 8N1(8 数据位、无校验、1 停止位)。但不同场景有不同选择:
| 配置名称 | 数据位 | 校验位 | 停止位 | 典型场景 |
|---|---|---|---|---|
| 8N1 | 8 | 无 | 1 | 通用串口通信,默认配置 |
| 8E1 | 8 | 偶校验 | 1 | Modbus RTU 部分实现 |
| 8O1 | 8 | 奇校验 | 1 | 老式终端设备 |
| 7E1 | 7 | 偶校验 | 1 | ASCII 字符传输 |
| 8N2 | 8 | 无 | 2 | 高波特率或长线传输 |
我的建议:除非有特殊要求,否则一律用 8N1。它兼容性最好,效率最高。如果通信环境有噪声,不要依赖硬件校验位,在应用层做 CRC 校验更可靠。
1.9 本章小结
UART 帧结构看似简单,但每个位都有它的设计目的。起始位负责同步,数据位承载信息,校验位提供基础检错,停止位保证帧间隔。理解这些基础,后面调波特率、优化性能时才能得心应手。