4、串口通信基础:RS232/RS485协议原理、串口参数配置、pySerial库实战
串口通信,说白了就是设备之间最古老也最可靠的对话方式之一。我做了这么多年自动化控制,几乎每个项目都离不开它。你想想看,PLC、传感器、变频器、扫码枪……这些工业设备哪个不是靠串口在背后默默传数据?
今天我们就来把串口通信这层窗户纸捅破。从协议原理到参数配置,再到用Python的pySerial库实战,一条龙讲清楚。
4.1 RS232与RS485:两种最常用的串口协议
先说说这两个协议的区别。我刚开始入行时也搞混过,后来在项目里吃过亏才彻底弄明白。
4.1.1 RS232:点对点的老大哥
RS232是最早的串口标准。它的特点是:
- 传输距离短:一般不超过15米。我在一个工厂里试过拉20米的线,数据就开始丢包了。
- 速率不高:常见的是9600、19200、115200bps。再高就容易出错。
- 全双工:可以同时收发数据,就像两个人同时说话也能听见对方。
- 电压摆幅大:逻辑1是-3V到-15V,逻辑0是+3V到+15V。抗干扰能力还行。
4.1.2 RS485:工业现场的通信王者
RS485就不一样了。它是我在工业项目中最常用的协议,没有之一。
- 传输距离远:理论1200米。我在一个污水处理厂里实测过,800米完全没问题。
- 支持多节点:一条总线上可以挂32个设备(现在很多芯片支持256个)。
- 差分信号:用两根线(A和B)传输,抗干扰能力极强。电机启动时电压波动那么大,数据照样稳。
- 半双工:同一时间只能发或者收。需要程序控制收发切换。
4.2 串口参数配置:波特率、数据位、停止位
串口通信就像两个人约定好怎么说话。参数必须完全一致,否则就是鸡同鸭讲。
4.2.1 波特率(Baud Rate)
波特率就是每秒传输的符号数。说白了就是通信速度。
| 波特率 | 每秒传输字节数(近似) | 适用场景 |
|---|---|---|
| 9600 | 约960字节 | 大多数工业设备默认值 |
| 19200 | 约1920字节 | 数据量稍大时使用 |
| 115200 | 约11520字节 | 高速传输,如固件升级 |
我个人习惯:能用9600就用9600。为什么?因为越高的波特率对线路质量要求越高。我在一个粉尘很大的车间里试过,115200根本跑不稳,降到9600就稳如老狗。
4.2.2 数据位(Data Bits)
数据位就是每个字节实际传输的数据位数。常见的有7位和8位。
- 8位:最常用。传输一个完整的字节(0-255)。
- 7位:老设备或ASCII码传输时用。比如一些老式打印机。
嗯,这里要注意:现在99%的设备都用8位数据位。除非你遇到古董设备,否则别纠结。
4.2.3 停止位(Stop Bits)
停止位是一个字节传输结束后的间隔。常见的有1位、1.5位和2位。
- 1位:默认值,够用。
- 2位:给设备更多时间处理数据。我在一些老旧的PLC上遇到过,必须设成2位才能稳定通信。
4.2.4 校验位(Parity)
校验位用于简单的错误检测。有三种:
- 无校验(None):最常用。现在通信质量好了,很少需要校验。
- 偶校验(Even):保证1的个数为偶数。
- 奇校验(Odd):保证1的个数为奇数。
4.3 pySerial库实战:用Python搞定串口通信
理论说完了,咱们来点实际的。pySerial是Python操作串口的标配库。我所有自动化项目都用它。
4.3.1 安装与基本使用
# 安装
pip install pyserial
# 基本使用
import serial
# 打开串口
ser = serial.Serial(
port='COM3', # Windows下用COM口,Linux下用/dev/ttyUSB0
baudrate=9600, # 波特率
bytesize=8, # 数据位
parity='N', # 校验位:N无校验,E偶校验,O奇校验
stopbits=1, # 停止位
timeout=1 # 超时时间(秒)
)
# 发送数据
ser.write(b'Hello, PLC!')
# 读取数据
data = ser.read(10) # 读取10个字节
line = ser.readline() # 读取一行
# 关闭串口
ser.close()
你看,就这么简单。但实际项目中坑不少。我来说几个。
4.3.2 避坑指南:我踩过的那些坑
我曾经写过一个脚本,每次运行都打开串口但不关闭。结果第三次运行时串口就报错了。记住:用完后一定要 ser.close(),或者用 with 语句自动管理。
# 推荐写法:用with自动关闭
with serial.Serial('COM3', 9600, timeout=1) as ser:
ser.write(b'AT\r\n')
response = ser.readline()
print(response)
# 退出with块后自动关闭
RS485是半双工的,发完数据后不能立刻读。需要等设备处理完。我一般发完数据后 sleep 50-100毫秒再读。
import time
ser.write(b'\x01\x03\x00\x00\x00\x01\x84\x0A')
time.sleep(0.1) # 给设备处理时间
response = ser.read(7)
4.3.3 实战:读取Modbus RTU设备数据
Modbus RTU是工业上最常用的串口协议。下面是一个完整的读取示例:
import serial
import time
from binascii import hexlify
def read_modbus_register(port, slave_id, register_addr, count=1):
"""
读取Modbus RTU保持寄存器
"""
# 构建读取命令
# 功能码03:读取保持寄存器
cmd = bytes([
slave_id, # 从站地址
0x03, # 功能码
(register_addr >> 8) & 0xFF, # 寄存器地址高字节
register_addr & 0xFF, # 寄存器地址低字节
(count >> 8) & 0xFF, # 读取数量高字节
count & 0xFF # 读取数量低字节
])
# 计算CRC校验(这里简化了,实际要用完整CRC算法)
# 实际项目中请使用 crcmod 库
crc = calculate_crc16(cmd)
cmd += crc
with serial.Serial(port, 9600, timeout=1) as ser:
ser.write(cmd)
time.sleep(0.1)
# 读取响应
# 响应格式:从站地址 + 功能码 + 数据长度 + 数据 + CRC
response = ser.read(5 + count * 2)
if len(response) >= 3:
data_length = response[2]
data = response[3:3+data_length]
return data
return None
# 使用示例
data = read_modbus_register('COM3', 1, 0x0000, 2)
if data:
print(f"读取到的数据: {hexlify(data).decode()}")
4.4 本章知识体系
下面这张图把串口通信的核心知识点串起来了:
串口通信说白了就是三个环节:选对协议、配好参数、写好代码。每个环节都有坑,但只要你按我说的来,基本不会出大问题。
嗯,今天就先讲到这里。记住:调试串口时,先确认硬件连接,再检查参数配置,最后才怀疑代码。这个顺序能帮你省下大量时间。
公众号:蓝海资料掘金营,微信deep3321