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帧的完整结构:

UART帧格式结构图(8位数据位,无校验,1位停止位) 空闲 (高电平) 起始 0 D0 LSB D1 D2 D3 D4 D5 D6 D7 MSB 停止 1 空闲(或下一帧) 下降沿触发 上升沿结束 一帧总共:1(起始)+ 8(数据)+ 1(停止)= 10位 传输1字节实际需要10位时间 例如115200bps:每bit约8.68μs,每字节约86.8μs 起始位 数据位 停止位 空闲

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 实战中的串口配置清单

每次我调试串口,都会按这个清单检查一遍:

  1. 波特率:收发双方是否一致?误差在2%以内吗?
  2. 数据位:8位还是7位?LSB first没错吧?
  3. 校验位:None/Even/Odd?收发要一致。
  4. 停止位:1位还是2位?
  5. 电平标准:TTL还是RS232?有没有接电平转换?
  6. 接线:TX接RX,RX接TX,GND接GND。别接反了。
  7. 流控:硬件流控(RTS/CTS)是否启用?默认关掉。

嗯,就这些。串口协议本身不复杂,但细节决定成败。你把这些搞懂了,后面做MIPI采集和串口联动的项目,心里就有底了。

可可精品资料库