串口通信基础:UART原理、RS232/RS485区别、波特率、数据位、停止位、校验位、流控

串口通信(Serial Communication)是嵌入式系统、工业控制、仪器仪表以及自动化测试领域最基础、最常用的通信方式之一。无论是通过USB转TTL模块调试单片机,还是通过RS485总线连接远端传感器,其底层核心都是UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)。本章将系统梳理UART的工作原理、电气标准差异、帧格式参数配置以及流控机制,帮助你在Python编程控制串口设备时,能够准确理解设备手册中的参数要求,避免因配置错误导致的通信失败。

UART串口通信核心链路与参数关系图 发送端 MCU UART 外设 TX / RX 引脚 发送移位寄存器 UART 数据帧格式 起始位 0 数据位 (5~8 bit) LSB → MSB 校验 位 停止位 1/1.5/2 波特率:9600 / 115200 / 460800 ... 数据位:8(常用)| 7 | 6 | 5 校验位:None / Even / Odd 停止位:1(常用)| 1.5 | 2 流控:None / RTS/CTS / XON/XOFF 接收端 MCU UART 外设 RX / TX 引脚 接收移位寄存器 电气标准转换 TTL (3.3V/5V) → RS232 (±12V) → RS485 (差分) MAX3232 / SP3485 / MAX485 等转换芯片 图:UART通信链路、帧格式与电气标准关系

UART异步串行通信核心原理

UART是一种异步全双工串行通信协议,其核心特点是不需要独立的时钟线,发送端与接收端各自使用内部时钟,通过约定相同的波特率(Baud Rate)来保证采样同步。在物理链路上,UART至少需要两根数据线:TX(发送)和RX(接收),以及一根公共地线GND。当设备A的TX连接到设备B的RX,设备A的RX连接到设备B的TX时,即可建立双向通信。

UART的数据传输以帧(Frame)为单位。空闲状态下,TX线保持高电平。当发送端准备发送数据时,首先拉低电平一个位时间,产生起始位(Start Bit),接收端检测到下降沿后开始按波特率采样。随后依次发送数据位(通常低位在前)、可选的校验位(Parity Bit)以及停止位(Stop Bit,必须为高电平)。停止位结束后,线路恢复空闲高电平,等待下一帧起始位。这种基于起始位和停止位实现字节同步的机制,使得通信双方无需共享时钟,但要求双方的波特率误差不超过±2%~±3%,否则长时间传输后会出现采样错位。

关键理解:UART的“异步”指的是字节之间异步,即每个字节独立同步,而不是每个比特都有独立时钟。因此,波特率精度和帧格式的一致性至关重要。在Python中使用pyserial库时,serial.Serial(port, baudrate, bytesize, parity, stopbits)的参数必须与设备端完全一致。

RS232、RS485与TTL电平标准对比

UART定义了数据帧的时序逻辑,但并未规定电气特性。实际工程中,根据传输距离、抗干扰能力和连接方式,需要选择不同的电气标准。最常见的三种标准为TTL、RS232和RS485。

特性 TTL (3.3V/5V) RS232 RS485
信号电平 高电平=3.3V/5V,低电平=0V 逻辑1=-3V~-15V,逻辑0=+3V~+15V 差分信号,A-B电压差表示逻辑
传输距离 ≤1米(板级通信) ≤15米(典型) ≤1200米(低速时)
通信模式 全双工,点对点 全双工,点对点 半双工(常用),多点总线(最多32节点)
抗干扰能力 弱 中等(电压摆幅大) 强(差分传输)
典型应用 MCU调试、传感器模块 工业设备、老式PC串口 工业现场总线、门禁系统、环境监测

在Python控制硬件时,通常通过USB转TTL芯片(如CH340、CP2102)连接开发板,或通过USB转RS485转换器连接工业设备。需要注意的是,RS232电平不能直接连接TTL电平的MCU引脚,必须经过MAX3232等电平转换芯片。RS485则使用差分信号,A、B两根线之间的电压差决定逻辑状态,因此需要专用的收发器(如SP3485)将UART的TX/RX信号转换为差分信号。

常见风险:将RS232设备直接连接到TTL串口会导致芯片烧毁。连接前务必确认设备手册中的电气标准,并使用万用表测量空闲状态下的电平(TTL空闲为高电平3.3V/5V,RS232空闲为负电压)。

波特率、数据位、停止位、校验位与流控详解

串口参数配置是通信调试的第一步,任何一项不匹配都会导致乱码或通信失败。以下逐一解析各参数的含义与选择依据。

波特率(Baud Rate)

波特率表示每秒传输的符号数,单位为bps(bits per second)。常见波特率有9600、19200、38400、115200、460800等。波特率越高,传输速度越快,但对线路质量和时钟精度要求也越高。例如,115200bps下每比特持续时间约为8.68微秒,而9600bps下约为104微秒。在Python中设置波特率时,需确保设备端支持该速率,且双方误差在允许范围内。

数据位(Data Bits)

数据位表示一帧中实际传输的数据比特数,可选5、6、7或8位。最常用的是8位,因为一个字节正好是8位,无需拆包。7位数据位常用于ASCII码传输(0-127),可节省一位开销。在Python的pyserial中,通过bytesize=serial.EIGHTBITS或serial.SEVENBITS设置。

停止位(Stop Bits)

停止位表示一帧结束后的高电平持续时间,可选1、1.5或2位。停止位越长,帧间隔越大,容错性越好,但传输效率降低。大多数设备使用1位停止位。1.5位停止位仅用于某些特殊设备,2位停止位常见于慢速机械式设备。

校验位(Parity Bit)

校验位用于简单的错误检测,可选无校验(N)、偶校验(E)、奇校验(O)。偶校验要求数据位和校验位中“1”的总数为偶数;奇校验则要求为奇数。校验位只能检测奇数个比特错误,无法纠错。对于可靠性要求较高的场景,建议使用更高级的CRC校验(在应用层实现),而非依赖UART的奇偶校验。

流控(Flow Control)

流控用于协调收发双方的速率,防止接收端缓冲区溢出。硬件流控使用RTS(Request to Send)和CTS(Clear to Send)引脚,当接收端缓冲区满时拉低RTS,发送端检测到CTS为低时暂停发送。软件流控使用XON/XOFF字符(0x11/0x13),接收端发送XOFF请求暂停,发送XON请求恢复。在Python中,通过rtscts=True或xonxoff=True启用对应流控。

参数 常用值 Python pyserial 设置 注意事项
波特率 9600 / 115200 baudrate=115200 高速率需短线路、屏蔽线
数据位 8 bytesize=serial.EIGHTBITS 7位用于ASCII场景
停止位 1 stopbits=serial.STOPBITS_ONE 2位用于慢速设备
校验位 N parity=serial.PARITY_NONE 偶校验用于Modbus RTU
流控 无 rtscts=False, xonxoff=False 长线传输建议硬件流控

实战案例:通过Python配置RS485设备通信参数

假设我们需要通过USB转RS485转换器读取一个工业温湿度传感器的数据。传感器手册要求:波特率9600,数据位8,无校验,停止位1,Modbus RTU协议,设备地址0x01,读取保持寄存器起始地址0x0000,读取2个寄存器(温度、湿度)。

首先,在Linux系统中查看USB转串口设备节点:

ls /dev/ttyUSB*
# 输出示例:/dev/ttyUSB0

然后,使用Python的pyserial库配置串口参数并发送Modbus请求帧:

import serial
import time

ser = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    timeout=1
)

# 构建Modbus RTU读保持寄存器请求帧
# 设备地址: 0x01, 功能码: 0x03, 起始地址: 0x0000, 寄存器数量: 0x0002
request = bytes.fromhex('01 03 00 00 00 02 C4 0B')
ser.write(request)
time.sleep(0.1)

response = ser.read(9)  # 期望响应9字节
print('原始响应:', response.hex())

# 解析温度(寄存器0)和湿度(寄存器1),假设数据格式为有符号整数,缩放系数0.1
if len(response) == 9 and response[1] == 0x03:
    temp_raw = int.from_bytes(response[3:5], byteorder='big', signed=True)
    humi_raw = int.from_bytes(response[5:7], byteorder='big', signed=True)
    temperature = temp_raw * 0.1
    humidity = humi_raw * 0.1
    print(f'温度: {temperature:.1f} °C, 湿度: {humidity:.1f} %RH')
else:
    print('响应异常,请检查地址、功能码或CRC校验')

ser.close()

在上述案例中,如果波特率或数据位配置错误,ser.read(9)可能返回空字节或乱码。此时应使用串口调试助手(如cutecom、minicom)先验证物理链路,再检查参数。另外,RS485是半双工通信,发送和接收不能同时进行,因此代码中在write后需要适当延时,等待设备响应。

排障经验:当遇到串口通信乱码时,优先检查波特率是否一致;当无响应时,检查RS485的A/B线是否接反、终端电阻是否匹配(120Ω)、设备地址是否正确。使用示波器或逻辑分析仪观察TX/RX波形,可以快速定位电平标准或帧格式问题。

常见误区与总结复盘

在串口通信调试中,初学者常犯以下错误:

  • 忽略电平标准差异:将TTL设备直接接入RS232接口,导致通信失败甚至硬件损坏。
  • 波特率误差累积:使用非标准波特率(如100000)时,部分USB转串口芯片可能无法精确生成,导致长时间传输后数据错位。
  • 流控配置不当:启用了硬件流控但未连接RTS/CTS线,导致发送端一直等待CTS信号而卡死。
  • 半双工时序处理错误:RS485通信中,发送后立即切换为接收模式,可能截断设备响应帧的起始字节。
  • 忽略地线连接:TTL通信中,收发双方必须共地,否则电平参考点不一致,导致数据错误。