UART通信故障定位信号捕获与协议解码调试实战 - 第1节

一、UART 的前世今生

UART,全称 Universal Asynchronous Receiver/Transmitter,通用异步收发器。这东西的历史,比我们大多数工程师的工龄都长。

上世纪 60 年代,计算机和终端之间需要通信。那时候的接口五花八门,各搞各的。直到 70 年代,Motorola 和 Intel 开始推专用的 UART 芯片,比如经典的 8250、16550。我刚开始做嵌入式那会儿,还在用 16550 的 PC 串口调试,一个芯片搞定收发,省心得很。

说白了,UART 就是把并行数据转成串行,一根线发、一根线收。不需要时钟线,两边约定好速度就行。这个「不需要时钟线」的特点,让它在很多场合比 SPI、I2C 更灵活。

核心特点: 全双工、异步、串行、无时钟线。

二、全双工异步通信原理

2.1 什么叫全双工?

全双工就是同时能发能收。UART 用两根独立的数据线:TX(发送)和 RX(接收)。发的不影响收的,收的也不影响发的。你想想看,就像两个人打电话,可以同时说话,也能同时听。

半双工呢?对讲机那种,按着键说,松手才能听。全双工显然效率更高。我在调试一个多传感器采集系统时,就吃过半双工的亏——主机轮询太慢,后来全换成 UART 全双工,数据吞吐量直接翻倍。

2.2 异步怎么理解?

异步就是没有独立的时钟线。收发双方各自用自己的时钟,靠约定的波特率和数据帧格式来同步。

那怎么保证两边节奏一致?靠的是起始位。接收端检测到起始位的下降沿,就开始按约定的波特率采样数据位。这就是所谓的「起始位同步」。

我的经验: 异步通信的容错能力其实很强。只要波特率误差在 ±2% 以内,一般都能正常工作。我见过不少板子,晶振偏差 1% 照样跑得稳稳的。

三、数据帧格式详解

UART 的数据帧,就像一列火车。每一帧由以下几个车厢组成:

字段 长度 说明
起始位 1 bit 固定为低电平(逻辑 0),标志一帧开始
数据位 5~9 bit 常用 8 bit,低位在前(LSB first)
校验位 0 或 1 bit 可选:无校验、奇校验、偶校验
停止位 1、1.5 或 2 bit 固定为高电平(逻辑 1),标志一帧结束

3.1 起始位

起始位就是一根下降沿。空闲时 TX/RX 都是高电平。当 TX 拉低,接收端就知道:要开始传数据了。

我曾经遇到一个坑:某款 MCU 的 UART 在初始化后,TX 默认是低电平。结果接收端一直以为有起始位,疯狂收乱码。查了两天才发现是 GPIO 初始电平没配好。

3.2 数据位

数据位是真正的信息载体。最常用的是 8 位数据,正好一个字节。也有用 7 位的,比如早期的 ASCII 传输。9 位数据在工业现场总线里偶尔见到,比如 9 位多机通信协议。

注意:数据位是低位先发。比如你要发 0x55(二进制 01010101),线上先看到的是最低位 1,然后是 0、1、0、1、0、1、0。这个顺序别搞反了,否则解码全错。

3.3 校验位

校验位用来做简单的错误检测。有三种模式:

  • 无校验(None): 不发送校验位,帧长最短。
  • 奇校验(Odd): 数据位 + 校验位中 1 的个数为奇数。
  • 偶校验(Even): 数据位 + 校验位中 1 的个数为偶数。

举个例子:数据位是 0x41(二进制 01000001),里面有 2 个 1。如果用偶校验,校验位就是 0,因为 2 已经是偶数。如果用奇校验,校验位就是 1,凑成 3 个 1。

注意: 校验位只能检测奇数个错误。如果两个 bit 同时翻转,校验位是看不出来的。所以对可靠性要求高的场合,建议用更高级的 CRC 校验。

3.4 停止位

停止位是高电平,持续 1、1.5 或 2 个 bit 时间。它给接收端一个缓冲,处理完当前帧,准备接收下一帧。

为什么会有 1.5 和 2 个停止位?老式设备处理速度慢,需要更多时间。现代 MCU 基本都用 1 个停止位。我只有在调试古董级设备时,才见过用 2 个停止位的。

四、波特率的概念与计算

4.1 波特率是什么?

波特率就是每秒传输的符号数。在 UART 里,一个符号就是一个 bit,所以波特率 = 比特率。单位是 bps(bits per second)。

常见的波特率有:9600、19200、38400、115200、921600。115200 是最常用的,速度适中,稳定性好。

4.2 波特率怎么算?

波特率由 UART 外设的时钟频率和分频系数决定。公式很简单:

波特率 = 时钟频率 / (16 × 分频系数)

举个例子:MCU 主频 16 MHz,想得到 115200 的波特率:

分频系数 = 16,000,000 / (16 × 115200) ≈ 8.68
取整为 9,实际波特率 = 16,000,000 / (16 × 9) ≈ 111,111 bps
误差 = (111,111 - 115,200) / 115,200 ≈ -3.55%

这个误差太大了,通信会不稳定。怎么办?换一个能整除的时钟频率,或者用带小数分频的 UART 模块。

避坑指南: 我曾经用 STM32F103 的 72 MHz 主频配 115200 波特率,分频系数算出来是 39.0625,取整后误差 0.16%,完全没问题。但如果用 8 MHz 的晶振配 115200,误差就大了。选晶振时,最好算一下常用波特率的误差。

4.3 波特率误差容忍度

接收端能容忍多大的波特率误差?一般来说:

  • ±2% 以内:稳定通信
  • ±2%~±3%:可能偶尔出错
  • 超过 ±3%:基本不可靠

这个容忍度跟数据帧长度有关。帧越长,累积误差越大。比如 8N1(8 数据位、无校验、1 停止位)总共 10 bit,误差容忍度就比 5N1 要小。

五、知识体系总览

下面这张图,把 UART 的核心知识点串起来了。你可以把它当作本章的思维导图:

UART 基础知识体系 UART 通信基础 发展历史 全双工异步原理 数据帧格式 波特率与计算 起始位 数据位 校验位 停止位 8250/16550 现代 MCU TX / RX 独立 时钟分频计算 核心要点 UART 是异步串行通信的基石,理解帧格式和波特率是调试一切串口问题的前提。 后续章节将围绕信号捕获、协议解码和故障定位展开实战。

六、本章小结

UART 看起来简单,但它是嵌入式调试的命脉。我见过太多工程师,遇到串口乱码就慌了,其实无非就是几个问题:波特率对不对?帧格式配没配错?电平匹配吗?

记住这几点:

  • 起始位是下降沿,停止位是高电平
  • 数据位低位先发,别搞反
  • 波特率误差控制在 ±2% 以内
  • 校验位只能检奇数个错误

把这些基础打牢了,后面讲信号捕获和协议解码,你才能听得明白、用得顺手。