1. UART基础与RS232标准

各位同学好,我是老周。做嵌入式这些年,调试过最多的接口就是UART。说句实话,十个硬件问题里,至少有三四个跟串口通信有关。这一章咱们先把地基打牢——UART怎么工作的,RS232电平到底怎么回事,DB9接口怎么接线。这些东西搞不清楚,后面多机通信、中断处理根本没法谈。

1.1 UART通信原理

UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。说白了,它就是个串行通信的硬件模块。我习惯把它理解成“一个字节的搬运工”——把并行的8位数据,变成一比特一比特串行发送出去。

为什么叫异步?因为没有独立的时钟线。发送方和接收方各用自己的时钟,靠约定的波特率来同步。你想想看,两个人约好每秒说10个字,那就不需要中间有人打拍子了。

UART通信的核心特点:

  • 只需要两根数据线:TX(发送)、RX(接收)
  • 异步通信,无时钟线
  • 全双工:可以同时收发
  • 数据帧格式可配置:数据位、停止位、校验位

我在一个工业项目里遇到过,两个设备明明波特率都设成了115200,但就是收不到正确数据。查了半天,发现一个用的是8位数据位,另一个配成了7位。嗯,这种坑踩过一次就记住了。

1.2 数据帧结构

UART的一帧数据长什么样?我画个图你就明白了。

UART数据帧结构 空闲 起始 D0 D1 D2 D3 D4 D5 D6 D7 校验 停止 数据发送方向(LSB first) 各字段说明: • 空闲位:总线为高电平,表示无数据传输 • 起始位:拉低电平,通知接收方“我要发数据了” • 数据位:5~8位,通常LSB(最低位)先发 • 校验位:可选奇校验/偶校验/无校验 • 停止位:拉高电平,表示一帧结束(1/1.5/2位)

这里有个细节要注意:数据位是LSB first,也就是最低位先发。比如你要发0x55(二进制01010101),先发出去的是最低位的1,而不是最高位的0。我见过有新手用逻辑分析仪抓波形,按MSB顺序去解析,结果数据全反了。

1.3 波特率计算

波特率就是每秒传输的比特数,单位bps。常用的有9600、19200、115200。怎么算?看下面这个公式:

// 以STM32为例,UART波特率计算公式
// 过采样方式:oversampling = 16(常用)
// 时钟频率:fck = 72MHz(假设)
// 目标波特率:baud = 115200

// 计算分频系数
USARTDIV = fck / (8 * (2 - OVER8) * baud)
         = 72000000 / (16 * 115200)
         = 39.0625

// 取整写入寄存器
// 整数部分:39
// 小数部分:0.0625 * 16 = 1
// 最终写入值:0x0271

我的经验:实际项目中,我建议用115200或更高。9600太慢了,传一帧数据要1ms左右,如果数据量大,CPU大部分时间都在等串口。但也要看你的晶振精度——如果晶振误差大,高速率反而容易丢数据。

波特率误差怎么算?举个例子:你算出来分频系数是39.0625,但寄存器只能存整数和小数部分有限精度,实际分频可能是39.0625取整后变成39.0625(如果小数部分精度够)或者39.0。误差超过2%就容易出问题。我有个血的教训:用内部RC振荡器跑115200,误差3.5%,结果10帧里有3帧是错的。

1.4 RS232电平标准 vs TTL电平

这是新手最容易搞混的地方。我直接给你对比表:

项目 TTL电平 RS232电平
逻辑1(高电平) 2.0V ~ 5.0V -15V ~ -3V
逻辑0(低电平) 0V ~ 0.8V +3V ~ +15V
电压摆幅 0~5V ±12V(典型)
传输距离 <1米 15米(标准)
抗干扰能力 弱 强
常见芯片 MCU直接输出 MAX232、SP3232

注意看,RS232的逻辑电平是反的!TTL的1是5V,0是0V;RS232的1是负电压,0是正电压。我第一次用MAX232做转换时,拿示波器一看,波形上下颠倒了,还以为芯片坏了。

警告:千万不要把MCU的TX/RX直接连到RS232接口上!TTL的5V输出到RS232的-12V输入端,轻则烧IO口,重则烧MCU。必须用电平转换芯片,比如MAX232、SP3232、MAX3232。

1.5 RS232接口定义(DB9)

DB9接口,就是那个9针的梯形接口。很多新手拿到DB9接头不知道哪边是1脚。我教你一个办法:宽边朝上,窄边朝下,左上角是1脚。

DB9公头引脚定义(正面视图) 1 2 3 4 5 6 7 8 9 常用引脚功能(嵌入式开发最常用): • 引脚2:RXD(接收数据)—— 从设备接收数据 • 引脚3:TXD(发送数据)—— 向设备发送数据 • 引脚5:GND(信号地)—— 必须连接,否则通信不稳定

实际项目中,我们一般只用三根线:

  • 引脚2(RXD):接收数据
  • 引脚3(TXD):发送数据
  • 引脚5(GND):信号地

其他引脚比如RTS(请求发送)、CTS(清除发送)是硬件流控用的,大部分嵌入式场景用不到。我做过一个项目,客户非要接全9根线,结果线多了反而容易出问题,最后改成三线制,稳得很。

避坑指南:我曾经遇到一个故障,DB9的母头和公头搞反了。公头的2脚是TXD,母头的2脚是RXD。如果你用公头对公头直连,TX对TX,RX对RX,永远通不了。记住:交叉连接——一端的TX接另一端的RX。

1.6 本章小结

这一章我们讲了UART的基础知识。说白了就是三件事:

  1. UART怎么传数据——起始位、数据位、校验位、停止位,一帧一帧串行发
  2. 波特率怎么算——根据时钟频率和分频系数,误差别超过2%
  3. RS232和TTL的区别——电平反了、电压高了,必须用转换芯片