3、UART串口协议精讲:UART帧格式、波特率计算、RS232与TTL电平区别
各位同学,今天我们来聊聊UART串口协议。说实话,串口这东西,做嵌入式的人天天都在用。但你真的吃透它了吗?我在项目中见过太多人因为串口配置不对,折腾好几天找不出问题。嗯,今天我们就把它彻底讲明白。
3.1 UART帧格式——一个字节是怎么飞出去的?
UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。注意「异步」这两个字,它意味着收发双方没有共享时钟。那怎么保证同步呢?靠的就是帧格式。
一个标准的UART帧,由以下几部分组成:
- 起始位:1位,逻辑0。告诉接收方「我要开始发数据了」。
- 数据位:5~9位,常见的是8位。注意,先发低位(LSB first)。
- 校验位:可选,0或1位。用于简单的错误检测。
- 停止位:1、1.5或2位,逻辑1。表示这一帧结束。
我习惯把帧格式想象成「包裹」。起始位是包裹上的「易碎品」标签,数据位是里面的货物,校验位是封条,停止位是签收确认。缺了哪个环节,快递员(接收方)都会懵。
核心要点:空闲时,TX线保持逻辑1。起始位的下降沿,就是接收方开始采样的触发信号。
为什么会这样设计?你想想看,如果空闲时是0,那起始位也是0,接收方怎么区分「空闲」和「开始」?所以必须空闲为1,起始为0,用下降沿来同步。这个细节,我在调试一块老旧的工业板卡时吃过亏——当时怎么都收不到数据,后来用示波器一看,TX线一直拉低,原来是芯片坏了。
3.2 波特率计算——快与慢的平衡
波特率,说白了就是每秒传输的符号数。对于UART来说,一个符号就是一个bit,所以波特率就等于比特率。单位是bps(bits per second)。
常见的波特率有:9600、19200、38400、115200、921600等。我个人最常用的是115200,速度够快,而且大部分MCU和USB转串口芯片都支持。
波特率怎么算?其实很简单:
波特率 = 系统时钟频率 / (16 × 波特率分频系数)
以STM32为例,假设系统时钟为72MHz,想得到115200的波特率:
分频系数 = 72,000,000 / (16 × 115200) ≈ 39.0625
取整为39,实际波特率 = 72,000,000 / (16 × 39) ≈ 115,384
误差 = (115,384 - 115,200) / 115,200 ≈ 0.16%
误差在±2%以内通常没问题。超过这个范围,就可能出现误码。我曾经在一个项目中,因为晶振精度不够,波特率误差到了3.5%,结果每隔几十个字节就丢一个。查了两天才发现是晶振的问题。
小技巧:如果你用FPGA实现UART,建议用16倍过采样。也就是每个bit采样16次,取中间3次的值做多数判决。这样能有效抗抖动。
下面我用一张图来展示UART帧的完整结构:
3.3 校验位——给数据加把锁
校验位有几种模式:
- 无校验(None):最常用,省去校验位,帧长固定为10位(8N1)。
- 奇校验(Odd):数据位中1的个数加上校验位,总数为奇数。
- 偶校验(Even):数据位中1的个数加上校验位,总数为偶数。
- 固定校验(Mark/Space):校验位固定为1或0,很少用。
举个例子,数据位是0b01010101(4个1),如果用偶校验,校验位就是0,因为4已经是偶数了。如果用奇校验,校验位就是1,凑成5个1(奇数)。
注意:校验位只能检测奇数个bit的错误。如果两个bit同时翻转,校验位是检测不出来的。所以它只适用于噪声较小的环境。真正可靠的通信,还是得上CRC。
我在调试一个GPS模块时,遇到过校验位配置错误导致数据全乱的情况。模块输出的是8E1(8数据位+偶校验+1停止位),我配置成了8N1。结果每个字节都差一个bit,解析出来的NMEA语句全是乱码。嗯,这种低级错误,犯过一次就记住了。
3.4 RS232电平 vs TTL电平——别插错了!
这是新手最容易踩的坑。我见过有人直接把RS232的DB9接口怼到FPGA的GPIO上,结果「啪」的一声,芯片冒烟了。
两者的区别,我用表格说清楚:
| 项目 | TTL电平 | RS232电平 |
|---|---|---|
| 逻辑1 | 2.0V ~ 5.0V | -3V ~ -15V |
| 逻辑0 | 0V ~ 0.8V | +3V ~ +15V |
| 电压摆幅 | 0~5V(小) | ±12V(大) |
| 传输距离 | 几米以内 | 可达15米以上 |
| 常见接口 | 2.54mm排针、杜邦线 | DB9串口母头 |
| 典型芯片 | MCU直接输出 | MAX232、SP3232 |
你发现没有?RS232的逻辑电平是反的!逻辑1是负电压,逻辑0是正电压。而TTL正好相反。所以两者不能直接相连,必须用电平转换芯片。
我建议你在做FPGA或MCU项目时,优先用TTL电平。原因很简单:
- TTL电平与FPGA/MCU的GPIO直接兼容,不需要额外芯片。
- TTL电平功耗更低,电压摆幅小,适合板级通信。
- 如果确实需要远距离传输,再加MAX232做电平转换。
避坑指南:我曾经在调试一块ARM开发板时,用USB转TTL模块连接,结果发现收不到数据。用示波器一量,TX引脚电压只有1.8V,而USB转TTL模块是3.3V电平。原来那块板子是1.8V的IO电平!所以,连接前一定要确认双方的IO电压是否匹配。
3.5 实战中的串口配置清单
每次我调试串口,都会按这个清单检查一遍:
- 波特率:收发双方是否一致?误差在2%以内吗?
- 数据位:8位还是7位?LSB first没错吧?
- 校验位:None/Even/Odd?收发要一致。
- 停止位:1位还是2位?
- 电平标准:TTL还是RS232?有没有接电平转换?
- 接线:TX接RX,RX接TX,GND接GND。别接反了。
- 流控:硬件流控(RTS/CTS)是否启用?默认关掉。
嗯,就这些。串口协议本身不复杂,但细节决定成败。你把这些搞懂了,后面做MIPI采集和串口联动的项目,心里就有底了。