串口通信基础:UART原理、RS232/RS485区别、波特率、数据位、停止位、校验位、流控
串口通信(Serial Communication)是嵌入式系统、工业控制、仪器仪表以及自动化测试领域最基础、最常用的通信方式之一。无论是通过USB转TTL模块调试单片机,还是通过RS485总线连接远端传感器,其底层核心都是UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)。本章将系统梳理UART的工作原理、电气标准差异、帧格式参数配置以及流控机制,帮助你在Python编程控制串口设备时,能够准确理解设备手册中的参数要求,避免因配置错误导致的通信失败。
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%,否则长时间传输后会出现采样错位。
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信号转换为差分信号。
波特率、数据位、停止位、校验位与流控详解
串口参数配置是通信调试的第一步,任何一项不匹配都会导致乱码或通信失败。以下逐一解析各参数的含义与选择依据。
波特率(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后需要适当延时,等待设备响应。
常见误区与总结复盘
在串口通信调试中,初学者常犯以下错误:
- 忽略电平标准差异:将TTL设备直接接入RS232接口,导致通信失败甚至硬件损坏。
- 波特率误差累积:使用非标准波特率(如100000)时,部分USB转串口芯片可能无法精确生成,导致长时间传输后数据错位。
- 流控配置不当:启用了硬件流控但未连接RTS/CTS线,导致发送端一直等待CTS信号而卡死。
- 半双工时序处理错误:RS485通信中,发送后立即切换为接收模式,可能截断设备响应帧的起始字节。
- 忽略地线连接:TTL通信中,收发双方必须共地,否则电平参考点不一致,导致数据错误。