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