4. UART 总线协议详解:起始位、数据位、校验位、停止位配置与异步通信机制

UART,说白了就是串口。这可能是嵌入式工程师打交道最多的接口了。我刚开始做单片机开发那会儿,调试代码全靠它打印日志。后来做低功耗项目,才发现UART的每一个位都有讲究。

UART是异步通信,没有时钟线。收发双方靠约定的波特率来同步。你想想看,一个说“我每秒发9600个符号”,另一个也按这个速度收。如果两边时钟不准,数据就错位了。

4.1 UART 数据帧结构

一个完整的UART数据帧,包含这几部分:

  • 起始位:1位,逻辑低电平。告诉接收方“我要开始发了”。
  • 数据位:5~9位,常见的是8位。低位在前,高位在后。
  • 校验位:0或1位。可选奇校验、偶校验或无校验。
  • 停止位:1、1.5或2位,逻辑高电平。表示一帧结束。

我习惯用8N1的配置——8位数据、无校验、1位停止位。这是最通用的设置,兼容性最好。

典型配置: 8N1 = 8数据位 + 无校验 + 1停止位,总帧长10位(含起始位)。

4.2 波特率误差容限

异步通信的核心问题:两边时钟不同步,怎么保证不出错?

接收方会在每个数据位的中间采样。如果波特率有误差,采样点会逐渐偏移。误差太大,采样点就跑到位边界上去了。

我做过一个项目,用内部RC振荡器跑115200波特率。手册说RC精度±2%,结果通信时好时坏。后来换成外部晶振,问题就解决了。

波特率 典型误差容限 推荐时钟源
9600 ±3% 内部RC(±1.5%)
115200 ±2% 外部晶振(±50ppm)
921600 ±1% 外部晶振(±25ppm)

误差容限怎么算?简单说,一帧10位,误差累积不能超过半个位宽。所以波特率越高,对时钟精度要求越严。

避坑指南: 我曾经在STM32上用内部HSI跑2M波特率,结果丢包率高达30%。后来查手册才发现,HSI在高温下会漂到±3%,根本扛不住2M。换成外部8MHz晶振+PLL,稳了。

4.3 异步通信机制详解

UART为什么叫“异步”?因为没有独立的时钟线。收发双方各自用自己的时钟来采样。

接收方怎么知道什么时候开始采样?靠起始位。空闲时总线是高电平。一旦检测到下降沿(高变低),就知道起始位来了。然后按波特率定时,在每个数据位的中间采样。

我画了一张流程图,帮你理解这个过程:

UART异步接收流程 UART 异步接收流程图 总线空闲(高电平) 检测下降沿 采样起始位(确认低电平) 按波特率逐位采样(8次) 校验位检查(可选) 关键点 • 起始位下降沿触发 • 每位中间采样 • 波特率误差累积 • 停止位高电平确认

接收流程其实不复杂。关键是采样点的位置。我习惯在波特率发生器里把采样点设在位宽的50%~60%位置,这样抗干扰能力最强。

4.4 校验位配置

校验位用来检测单比特错误。有三种选择:

  • 无校验(N):省掉校验位,帧长最短,效率最高。适合噪声小的环境。
  • 奇校验(O):数据位+校验位中1的个数为奇数。
  • 偶校验(E):数据位+校验位中1的个数为偶数。

举个例子,数据位是0x55(二进制01010101),有4个1:

  • 奇校验:校验位=1,使总1数为5(奇数)
  • 偶校验:校验位=0,使总1数为4(偶数)

我的习惯: 短距离通信(板内、机箱内)用无校验,省带宽。长距离或工业环境用偶校验,因为偶校验的硬件实现更简单,很多MCU的UART外设默认支持偶校验。

4.5 停止位与帧间隔

停止位至少1位高电平。为什么要有停止位?

第一,给接收方时间处理刚收到的数据。第二,保证下一帧的起始位下降沿能被正确检测到。

我遇到过一个问题:两个设备通信,一帧接一帧地发,中间没有间隔。结果接收方处理不过来,丢数据。后来在每帧之间加了2个位的空闲时间,问题就解决了。

停止位配置 适用场景 注意事项
1位 通用场景 标准配置,兼容性好
1.5位 低速设备 给接收方更多处理时间
2位 噪声环境 增加帧间隔,抗干扰

4.6 低功耗设计中的UART配置

做低功耗项目时,UART的配置直接影响功耗。我总结了几条经验:

  • 降低波特率:9600比115200省电,因为时钟分频比大,振荡器起振时间短。
  • 减少帧长:用7位数据+无校验,比8N1省10%的传输时间。
  • 关闭空闲中断:很多MCU的UART在空闲时会产生中断,关掉能省电。
  • 使用DMA:批量收发用DMA,CPU可以睡大觉。

实战案例: 一个电池供电的传感器节点,用115200波特率+8N1,每天发1000条数据,平均电流1.2mA。改成9600+7N1+DMA后,平均电流降到0.4mA。电池寿命从3个月延长到9个月。

4.7 常见问题与调试技巧

UART调试,我踩过的坑不少。列几个典型的:

  • 乱码:多半是波特率不匹配。用示波器量一下位宽,算算实际波特率。
  • 丢第一个字节:接收方还没准备好。加个100ms延时再发。
  • 偶尔丢包:检查中断优先级,别被其他中断抢了。
  • 全是0xFF:TX和RX接反了,或者地线没共地。

避坑指南: 我曾经用逻辑分析仪抓UART波形,发现停止位只有0.8位宽。查了半天,原来是MCU的时钟分频器配置错了,导致波特率偏了20%。从那以后,我每次调UART第一件事就是量位宽。

UART看似简单,但每个参数都有它的物理意义。理解了起始位、数据位、校验位、停止位背后的机制,你就能在低功耗设计中做出最优的取舍。