UART协议深度解析:从起始位到电平转换

UART协议,说白了就是两个设备之间串行通信的“握手规则”。我刚开始接触嵌入式时,总觉得这协议太简单——不就一根TX一根RX嘛。直到有一次调试工业设备,数据死活收不对,才发现自己对协议细节的理解还差得远。

今天咱们就把UART协议从头到尾拆开来看。你想想看,一个字节的数据,怎么就变成了一串高低电平在线上跑?这里面每个bit都有讲究。

2.1 协议帧结构:一个字节的旅行

UART是异步通信,没有时钟线。收发双方靠什么同步?靠约定的波特率和帧结构。一个完整的UART数据帧长这样:

字段 位数 说明
起始位 1 bit 固定为低电平,标志传输开始
数据位 5~9 bit 常用8 bit,LSB先发
校验位 0/1 bit 可选,奇校验或偶校验
停止位 1/1.5/2 bit 固定为高电平,标志传输结束

我习惯把UART帧想象成一列火车:起始位是火车头,数据位是车厢,校验位是安检员,停止位是车尾。接收端看到起始位的下降沿,就知道“车来了”。

关键点:UART是异步协议,没有时钟线。收发双方必须提前约定好波特率、数据位、校验位、停止位。任何一个参数不匹配,数据就全乱套。

2.2 起始位:下降沿的“哨声”

空闲时,TX线保持高电平。发送端要发数据时,先把TX拉低一个bit时间。这个下降沿就是接收端的“发令枪”。

接收端以16倍波特率的采样时钟,在下降沿后采样16个点。为什么是16倍?我解释一下:采样时钟越快,越能准确找到每个bit的中心位置。16倍是经典做法,既能抗抖动,又不会太耗资源。

我的经验:在STM32上配置UART时,我习惯把过采样设为16倍。虽然8倍也能用,但16倍在噪声环境下更稳。有一次在电机驱动板上调试,8倍采样频繁出错,换成16倍就稳了。

2.3 数据位:LSB先行的规矩

数据位通常是8位,从最低位(LSB)开始发。比如要发0x55(二进制01010101),发送顺序是:1→0→1→0→1→0→1→0。

为什么是LSB先发?历史原因。早期电传打字机就是这么设计的,后来成了惯例。你想想看,如果MSB先发,很多老设备就没法兼容了。

数据位长度可以配置:

  • 5 bit: 老式电传设备用,现在很少见
  • 7 bit: ASCII字符传输,有些传感器还在用
  • 8 bit: 最常用,一个字节正好
  • 9 bit: 多机通信时做地址/数据标志

注意:收发双方的数据位长度必须一致。我曾经犯过一个低级错误——发送端配了8位,接收端配了7位,结果每个字节都少收1位,后面的数据全错位了。排查了整整一下午。

2.4 校验位:简单的错误检测

校验位是可选的,用来检测传输错误。有两种模式:

校验模式 规则 举例(数据位0x55,即01010101)
奇校验 数据位中1的个数 + 校验位 = 奇数 数据位有4个1,校验位=1,总数为5(奇数)
偶校验 数据位中1的个数 + 校验位 = 偶数 数据位有4个1,校验位=0,总数为4(偶数)

校验位只能检测奇数个bit翻转。如果两个bit同时翻转,校验位就发现不了。所以它只适合低噪声环境,工业现场我一般不用校验位,改用CRC。

我的建议:如果通信距离短、环境干净,可以不用校验位。如果必须用,奇校验比偶校验更常用——因为奇校验能保证线上至少有一个1,有助于检测线路断路。

2.5 停止位:高电平的“休止符”

停止位固定为高电平,长度可以是1、1.5或2个bit。接收端检测到停止位,就知道这一帧结束了。

为什么需要停止位?两个原因:

  1. 给接收端处理时间: 有些老式UART硬件处理慢,需要额外时间把数据从移位寄存器搬到缓冲区
  2. 帧同步: 停止位的高电平为下一帧的起始位下降沿提供了明确的跳变沿

我一般用1位停止位。只有在低速设备或噪声严重的场合,才会用2位停止位。1.5位?那是给老式电传打字机用的,现在基本见不到了。

2.6 波特率计算:时间就是一切

波特率就是每秒传输的bit数。常用的有9600、115200等。波特率决定了每个bit的持续时间:

bit时间 = 1 / 波特率

举例:
波特率 9600 → 每个bit ≈ 104.17 μs
波特率 115200 → 每个bit ≈ 8.68 μs

发送一个字节(8N1格式:8数据位 + 无校验 + 1停止位)需要10个bit时间:

帧时间 = (1 + 8 + 0 + 1) / 波特率 = 10 / 波特率

9600 → 约1.04 ms/字节
115200 → 约86.8 μs/字节

实际应用中,波特率不是随便选的。晶振频率和分频系数决定了你能得到哪些波特率。我遇到过最头疼的事:某款MCU的UART模块,用8MHz晶振配不出精确的115200,误差0.16%勉强能用,但连续传大文件就会丢bit。

经验公式:波特率误差必须小于±2%。超过这个范围,接收端采样点会逐渐偏离bit中心,最终导致帧错误。我一般控制在±1%以内才放心。

2.7 RS-232电平与TTL电平转换

这是新手最容易踩的坑。UART协议定义的是逻辑电平,但物理层有两种标准:

标准 逻辑1 逻辑0 电压范围
TTL电平 3.3V / 5V 0V 0~5V
RS-232电平 -3V ~ -15V +3V ~ +15V ±3V ~ ±15V

注意看,RS-232的逻辑1是负电压,逻辑0是正电压——跟TTL正好相反!而且电压范围也完全不同。直接把MCU的TX接到电脑串口?等着冒烟吧。

转换芯片我常用MAX3232或SP3232。它们内部有电荷泵,能把3.3V升压到±5V以上,同时完成电平反转。

血的教训:我曾经把TTL电平的TX直接连到RS-232的DB9接口上,结果MCU的IO口烧了。后来才知道,RS-232的空闲态是负电压,TTL的空闲态是高电平,两者直接对接相当于短路。从那以后,我每次做串口电路都先确认电平标准。

2.8 知识体系总览

下面这张图把UART协议的核心知识点串起来了。你可以把它当成一张“通信地图”:

UART协议知识体系 UART协议核心 帧结构 波特率 电平标准 起始位 数据位 校验位 停止位 bit时间计算 误差控制 TTL电平 RS-232电平 转换芯片:MAX3232/SP3232 核心原则:收发双方必须约定完全相同的帧格式和波特率 电平转换时注意逻辑反转和电压范围,避免烧毁IO口

这张图把UART协议拆成了三大块:帧结构、波特率、电平标准。每一块下面又有细分。你写驱动时,脑子里要有这张图——配置寄存器时就知道该关注哪些参数了。

2.9 本章小结

UART协议看着简单,但每个细节都藏着坑。起始位的下降沿是同步的关键,数据位的LSB先行是历史惯例,校验位只能防奇数bit错误,停止位给接收端留了处理时间。波特率计算要控制误差在±1%以内,电平转换必须用专用芯片。