3、UART串口通信基础:UART原理、波特率、数据位、停止位、校验位、TTL电平与RS232区别

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是嵌入式系统与PC之间最古老、最广泛使用的串行通信协议之一。在GPIO控制与硬件自动化场景中,UART常被用于连接传感器模块(如GPS、蓝牙HC-05)、工业控制板(如PLC)、单片机调试口等。理解UART的帧结构、电平标准与参数配置,是编写稳定可靠的Python串口控制程序的前提。本节将从物理层到协议层,系统拆解UART的核心机制,并重点辨析TTL与RS232两种电平标准的工程差异。

UART通信链路与帧结构工程视图 发送端MCU UART_TX (Pin PA9) GND 共地 TX Buffer → 移位寄存器 接收端设备 UART_RX (Pin PA10) GND 共地 移位寄存器 → RX Buffer TX → RX 数据线(TTL电平 0~3.3V) GND 参考地(必须共地) UART 异步帧结构(10位/帧,8N1格式) 起始位 低电平 D0 LSB D1 D2 D3 D4 D5 D6 D7 MSB 停止位 高电平 空闲态(高电平)→ 等待下一帧起始位 波特率 = 每秒传输的码元数(如9600bps → 每bit约104.2μs)

上图展示了UART通信中最核心的物理连接与帧结构。UART属于异步串行通信,收发双方不需要共享时钟信号,而是通过约定相同的波特率(即码元速率)来同步采样时刻。每一帧数据以起始位(低电平)作为同步标志,随后依次发送数据位(通常为8位,低位在前),最后以停止位(高电平)结束。这种帧结构保证了即使没有时钟线,接收端也能在噪声干扰下准确恢复数据。

3.1 UART核心原理与电气特性

UART的物理层本质是两条信号线(TX与RX)加上一条共地线(GND)。发送端通过拉低TX线电平产生起始位,接收端检测到下降沿后启动内部波特率计数器,在每一位的中间时刻采样电平值。这种设计使得UART对时钟精度要求不高(通常允许±3%的误差),非常适合低成本MCU与传感器通信。

关键电气参数:

  • 空闲态:TX线默认保持高电平(逻辑1),当检测到低电平时视为起始位。
  • 起始位:1位低电平(0V),用于同步接收端采样时钟。
  • 数据位:5~9位可选,常用8位(一个字节),先发送最低有效位(LSB)。
  • 停止位:1~2位高电平,标志一帧结束,同时保证下一帧起始位有明确下降沿。
  • 校验位:可选奇校验、偶校验或无校验,用于检测传输错误。

在Python的pyserial库中,这些参数通过serial.Serial()构造函数配置。例如ser = serial.Serial('/dev/ttyUSB0', 9600, bytesize=8, parity='N', stopbits=1)即对应8N1格式(8数据位、无校验、1停止位),这是最常用的配置。

3.2 波特率、数据位、停止位与校验位的工程选择

波特率决定了数据传输速率,但并非越高越好。波特率越高,每位持续时间越短,对线路质量、电平转换芯片的压摆率要求越高,误码率也会上升。常见波特率有9600、115200、460800等。在GPIO控制场景中,若控制LED或继电器,9600bps已足够;若传输传感器批量数据(如IMU九轴数据),则建议115200bps以上。

参数典型值适用场景注意事项
波特率9600 / 115200调试口、GPS模块、蓝牙模块双方必须一致,误差<3%
数据位8位标准ASCII码、二进制数据7位用于老式ASCII,较少用
停止位1位绝大多数场景2位用于慢速设备或强干扰环境
校验位无(N)短距离、低噪声环境偶校验(E)可检测单bit错误

校验位的作用是检测传输过程中的单比特翻转。偶校验要求数据位加校验位中“1”的个数为偶数,接收端重新计算并比对。但校验位只能检错,不能纠错,且会降低有效吞吐量。在工业现场(如RS485总线)中,更推荐使用CRC校验或更高层协议(如Modbus)来保证数据完整性。

3.3 TTL电平与RS232电平的本质区别

这是UART工程应用中最容易混淆的概念。TTL电平是MCU直接输出的信号标准,逻辑1对应3.3V(或5V),逻辑0对应0V。而RS232是早期串口标准,采用负逻辑:逻辑1对应-3V~-15V,逻辑0对应+3V~+15V。两者不能直接相连,必须通过电平转换芯片(如MAX3232、SP3232)桥接。

风险提示:将TTL电平的TX引脚直接连接到PC的RS232串口(DB9接口),会因电平不匹配导致芯片烧毁。同样,将RS232信号接入树莓派GPIO(3.3V容忍)也会损坏SoC。务必使用转换模块或USB-TTL转接器。

在Python硬件控制中,我们通常使用USB转TTL模块(如CH340、CP2102)连接MCU的UART引脚。此时PC端识别为虚拟串口(如COM3或/dev/ttyUSB0),实际电平是TTL标准,而非RS232。若连接老式PLC或工业仪表,则需使用RS232转TTL模块,或直接选用带RS232电平的USB转串口线(内部已集成转换芯片)。

3.4 实战案例:用Python读取GPS模块数据并解析

以常见的NEO-6M GPS模块为例,其UART默认配置为9600bps、8N1,输出NMEA 0183协议语句。我们通过树莓派GPIO的UART引脚(TXD=GPIO14, RXD=GPIO15)连接模块,编写Python脚本读取并解析GGA语句中的经纬度信息。

import serial
import time

# 初始化串口:/dev/ttyAMA0为树莓派硬件串口
ser = serial.Serial('/dev/ttyAMA0', 9600, timeout=1)
ser.bytesize = 8
ser.parity = serial.PARITY_NONE
ser.stopbits = 1

def parse_gga(sentence):
    if sentence.startswith('$GPGGA'):
        parts = sentence.split(',')
        if len(parts) > 6 and parts[2] and parts[4]:
            lat = float(parts[2]) / 100.0
            lon = float(parts[4]) / 100.0
            return lat, lon
    return None

try:
    while True:
        line = ser.readline().decode('ascii', errors='ignore').strip()
        if line:
            result = parse_gga(line)
            if result:
                print(f"纬度: {result[0]:.6f}, 经度: {result[1]:.6f}")
except KeyboardInterrupt:
    ser.close()
    print("串口已关闭")

该案例中,我们首先配置了与GPS模块匹配的串口参数。若波特率设置错误(如误设为115200),读取到的将是乱码。通过timeout=1参数避免阻塞,使用errors='ignore'容忍个别字节错误。实际运行时,GPS冷启动可能需要30秒以上才能输出有效定位数据,这是正常现象。

排障经验:若读取无数据,先用示波器或万用表测量TX引脚是否有电平跳变;若确认有信号但Python读不到,检查是否启用了串口控制台(树莓派需通过raspi-config关闭串口登录功能)。另外,GPS模块的TX应接树莓派的RX(GPIO15),反之亦然。

3.5 常见误区与总结复盘

在UART开发中,以下误区高频出现:

  • 误区一:认为波特率越高越好。实际在长线传输或电磁干扰环境下,高速率会导致眼图闭合,误码率急剧上升。
  • 误区二:忽略共地。UART是单端信号,收发双方必须共地,否则参考电压不一致会导致数据错误。
  • 误区三:混淆TX/RX交叉连接。两个设备通信时,A的TX接B的RX,A的RX接B的TX,直连同端会收不到数据。
  • 误区四:在Python中未设置合理的timeout,导致程序卡死在readline()。