1. UART 基础回顾:协议帧格式、波特率计算、奇偶校验、硬件流控
UART,说白了就是串口。搞嵌入式的人,没人离得开它。我最早接触 UART 是在大学做单片机实验,那时候觉得这东西太简单了——就两根线,一收一发,能有多复杂?后来真正做产品了才发现,UART 的坑一个接一个。波特率对不上、数据丢帧、校验搞错,这些问题我几乎都踩过。
这一节,咱们把 UART 的基础重新捋一遍。不是为了凑篇幅,而是后面讲 DMA 传输和性能调优时,这些基础概念会反复出现。你如果对帧格式和波特率计算不熟,后面会很难跟上。
1.1 UART 协议帧格式
UART 是异步串行通信,没有时钟线。收发双方靠约定的波特率来同步。一帧数据长什么样?我习惯用下面这张图来理解:
UART 数据帧结构(8-N-1 为例)
起始位(1bit) + 数据位(5~8bit) + 校验位(0/1bit) + 停止位(1/1.5/2bit)
这里有几个关键点,我特别想强调一下:
- 起始位:一定是低电平。接收端检测到电平从高跳变到低,就知道一帧数据要开始了。
- 数据位:先发 LSB(最低有效位),再发 MSB。这个顺序容易搞反,我见过有人把数据位顺序弄错,调试了一整天。
- 校验位:可选。奇校验、偶校验、或者不用。
- 停止位:一定是高电平。至少 1 位,有些老设备用 1.5 位或 2 位。
我的习惯: 新项目默认用 8-N-1(8 数据位、无校验、1 停止位)。这是最通用的配置,兼容性最好。除非对方设备明确要求其他格式,否则别乱改。
1.2 波特率计算
波特率就是每秒传输的符号数。对于 UART 来说,一个符号就是 1 位。所以波特率 = 每秒传输的位数(bps)。
为什么波特率会算错?我遇到过好几次,问题都出在时钟源上。微控制器的 UART 外设通常从系统时钟分频得到波特率时钟。分频公式各家芯片略有不同,但核心逻辑是一样的:
// 常见 UART 波特率计算公式(以 STM32 为例)
// 假设系统时钟 72 MHz,目标波特率 115200
// USARTDIV = 72,000,000 / (16 × 115200) = 39.0625
// 整数部分 = 39,小数部分 = 0.0625 × 16 = 1
// BRR 寄存器值 = (39 << 4) | 1 = 625
你看,算出来是个小数。如果小数部分不能精确表示,实际波特率和目标波特率之间就有误差。这个误差不能太大,一般要求 < 2%。
| 目标波特率 | 系统时钟 72 MHz | 系统时钟 16 MHz | 误差(72 MHz) |
|---|---|---|---|
| 9600 | 精确 | 精确 | 0% |
| 115200 | 近似 | 精确 | 0.16% |
| 921600 | 近似 | 不支持 | 0.16% |
| 2M | 近似 | 不支持 | 1.73% |
避坑指南: 我曾经在一个项目里用了 2M 波特率,系统时钟 72 MHz,算下来误差 1.73%。单板测试没问题,但接上长线缆(超过 1 米)就开始丢包。后来降到 921600,问题解决。高波特率对时钟精度和线缆质量都很敏感,别只看理论值。
1.3 奇偶校验
奇偶校验是最简单的错误检测手段。原理不复杂:
- 偶校验:数据位中 1 的个数 + 校验位 = 偶数
- 奇校验:数据位中 1 的个数 + 校验位 = 奇数
举个例子,发送 0x55(二进制 01010101),数据位里有 4 个 1:
- 偶校验:校验位 = 0(4 个 1 已经是偶数)
- 奇校验:校验位 = 1(需要凑成奇数个 1)
但说实话,奇偶校验只能检测奇数个位的翻转。如果两个位同时翻转,校验位检测不出来。我一般只在低速率、短距离的场景下用校验。高速通信或者噪声大的环境,建议用 CRC 或者更高级的协议层校验。
一句话总结: 奇偶校验是「防君子不防小人」的。它能挡住偶发单比特错误,但对付不了突发噪声。
1.4 硬件流控
硬件流控,就是 RTS/CTS 那两根线。RTS(Request To Send)和 CTS(Clear To Send)用来协调收发双方的节奏。
工作逻辑其实很简单:
- 发送方想发数据前,先拉高 RTS,问接收方:「我能发吗?」
- 接收方如果准备好了,就拉高 CTS 回应:「来吧,我准备好了。」
- 如果接收方忙不过来,就拉低 CTS,发送方看到 CTS 低了,就得停下来等。
我什么时候会用硬件流控?两个场景:
- 高速通信:比如 1M 以上波特率,软件流控(XON/XOFF)来不及处理,硬件流控更可靠。
- 多设备共享总线:有些 RS-485 或者多机通信场景,RTS 可以用来控制收发切换。
我的建议: 如果只是两个设备点对点通信,波特率不超过 115200,而且双方都有足够的缓冲区,硬件流控不是必须的。省掉那两根线,还能少点故障点。但如果你在做 DMA 传输,硬件流控配合 DMA 的请求信号,能实现非常高效的数据搬运——这个后面章节会详细讲。
1.5 本章小结
UART 基础就这些内容。帧格式、波特率、校验、流控,每个点都不难,但组合起来就容易出问题。我见过太多人栽在波特率误差上,也见过因为校验位配置不一致导致通信失败的案例。
记住一句话:UART 通信的可靠性,90% 取决于初始化配置是否正确。 后面讲 DMA 传输时,你会发现所有性能调优都建立在这些基础参数之上。基础不牢,地动山摇。