第一讲:UART通信基础——同步与异步、数据帧格式
各位做嵌入式开发的同行,咱们今天聊聊UART。这个东西看似简单,但很多坑都藏在细节里。我个人习惯,每接手一个新板子,第一件事就是把UART调通——这是最基础的调试通道。调不通,后面什么printf、log都没戏。
UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。注意「异步」这两个字,这是理解整个通信机制的核心。
1.1 同步 vs 异步:就差一根时钟线
通信里最基础的分水岭,就是有没有独立的时钟线。同步通信,比如I²C、SPI,发送方和接收方共用一根时钟线,时钟边沿来了,数据就对上了。异步通信呢?没有这根时钟线。双方各自用自己的时钟来采样数据。
你想想看,没有公共时钟,两边靠什么对齐?靠的是约定的波特率(Baud Rate)。说白了,就是双方商量好——我每秒钟发送多少个bit,你就用同样的速度来读。
| 特点 | 同步通信 (如SPI) | 异步通信 (UART) |
|---|---|---|
| 时钟线 | 需要,独立一根 | 不需要,双方自定时 |
| 抗干扰能力 | 时钟同步,抗偏差强 | 对时钟精度敏感 |
| 硬件复杂度 | 多一根线,但收发器简单 | 少一根线,但需要波特率校准 |
| 典型场景 | 板内短距离高速传输 | 板间、设备间低速通信 |
关键认知:异步不等于乱来。波特率偏差一旦超过2%,通信就大概率会错位。我在项目中遇到过因为晶振精度不够,115200波特率下每隔几十个字节就丢一个byte的怪事——后来查出来是两边晶振误差叠加导致的。
1.2 UART数据帧格式——一个典型bit序列长什么样?
UART一帧数据,就像一列小火车。从空闲电平出发,经过起始位、数据位、校验位、停止位,最后回到空闲。
典型的帧格式(以8数据位、无校验、1停止位为例):
空闲(高电平) → 起始位(低) → bit0 → bit1 → … → bit7 → 停止位(高) → 空闲
注意:数据发送是LSB first(低位在前)。很多初学者会搞反,认为是MSB先发,结果抓波形怎么看都不对。
| 字段 | 位数 | 电平特征 | 说明 |
|---|---|---|---|
| 空闲 | — | 高电平 (逻辑1) | 没有数据传输时保持高 |
| 起始位 | 1位 | 低电平 (逻辑0) | 检测到下降沿表示一帧开始 |
| 数据位 | 5~9位(常用8) | LSB先发 | 实际传输的数据 |
| 校验位 | 0或1位 | 奇/偶/无 | 可选,用于简单检错 |
| 停止位 | 1、1.5或2位 | 高电平 (逻辑1) | 保证帧间隔,方便接收方重新同步 |
工程小技巧:我习惯在调试时用逻辑分析仪抓一帧完整的波形,对着数据手册逐bit核对。比如发0x55(二进制01010101),看是不是LSB先发,停止位电平对不对。这个习惯救了我很多次。
1.3 波特率——收发双方的时间节拍器
波特率定义的是「每秒传输的符号数」。对于UART,一个符号就是一个bit,所以波特率等于比特率(bps)。常用值:9600、19200、38400、115200、921600。
为什么会有这么多速率?因为受限于晶振频率、分频器精度以及信号完整性。你想想看,如果晶振是16MHz,要产生115200波特率,需要分频系数 = 16000000 / 115200 ≈ 138.8889。不是整数!MCU只能取整,比如139。实际波特率就成了16000000/139 ≈ 115107.9,偏差约 -0.08%。这个误差在接收端容忍范围内(一般±2%以内可以工作)。
注意:偏差是累积的。一帧10个bit(起始+8数据+停止),最后一位的累积偏移 = 偏差率 × 9个bit时间。假设系统偏差+2%,最后一位采样点偏移接近18%,很可能落到bit边界上。我之前在stm32上做2400波特率通信,用了内部HSI振荡器(精度约±1%),一帧下来没事,但连续多帧后积累的误差导致偶尔丢包——后来换了外部晶振才稳定。
1.4 接收方的采样策略——多数投票法
UART接收器怎么知道数据线在什么时候是有效的bit?没有时钟线,全靠过采样。
现代MCU的UART外设,一般以16倍(或8倍)波特率时钟对RX引脚进行采样。比如115200波特率,内部会用一个1.8432MHz的时钟去采样。每隔16个采样时钟,判定一次当前bit值。判定方法是:在中间位置连续采样3次,取多数值(majority voting)。
为什么这么做?因为起始位下降沿的检测点可能有±1个采样时钟的抖动,加上传输线上的毛刺干扰,3中取2能有效过滤。
| 过采样率 | 常用场景 | 对偏差容忍度 |
|---|---|---|
| 16× | 主流MCU(STM32、NXP等) | 高,可容忍约±2% |
| 8× | 部分低功耗或高速模式 | 中,容忍约±1% |
| 3× | 早期简单UART | 低,需要高精度时钟 |
1.5 避坑指南——我踩过的几个雷
- 晶振精度不对等。曾经用国产某MCU和PC串口通信,两边都是115200,但PC端用的是USB转串口芯片自带时钟(精度0.25%),MCU用内部RC(误差±1.5%),叠加起来偏差接近2%。结果就是:短包没问题,长包(超过256字节)末尾几字节开始错位。解决办法:换外部晶振,或者降低波特率到38400。
- 电平不匹配。UART只定义逻辑电平,没定义物理电压。3.3V设备直接接5V设备,电流倒灌会烧IO。我习惯用双向电平转换模块,或者选带fail-safe功能的RS-232芯片。
- 接地不可靠。两个设备的地电势差太大,data线上的共模噪声会让接收方误判电平。长距离通信最好用RS-485,或者至少用差分信号。
- 起始位毛刺误触发。有些传感器在上电瞬间RX线上会有短暂低脉冲,UART误认为是一帧开始,然后整个后续数据全乱。解决方案:在软件中加帧超时判断,或者硬件上拉电阻加强抗干扰。
1.6 本章小结
UART作为最基础的异步通信接口,核心就是「没有时钟线、全靠约定速率」。理解它的数据帧格式——起始位、数据位(LSB first)、校验位(可选)、停止位——是后面分析波特率偏差的前提。记住:接收方靠过采样+多数判决来恢复数据,而波特率偏差直接决定了采样点是否落在bit窗口内。偏差太大,一切免谈。
下一章,我们来深入解剖波特率偏差在接收端究竟是如何一步步导致误码的。我会结合波形图,把理论公式和实际测试数据对照着讲。
(正文结束)