4、串口通讯实战(一):pyserial库详解、串口参数配置与开关
串口通讯,说白了就是上位机和下位机之间最传统、也最可靠的一条“电话线”。
我做运动控制这些年,调试过的设备里,十台有八台第一版通讯都是走串口。为什么?因为简单、稳定、几乎所有单片机都支持。你想想看,一个USB转串口的小模块,几块钱就能搞定,多划算。
这一章,我们就来啃下串口通讯最核心的几个动作:pyserial库怎么用、参数怎么配、串口怎么打开和关闭。这些都是基本功,但也是坑最多的地方。
本章核心目标: 掌握 pyserial 库的安装与基本用法,理解串口五大参数(波特率、数据位、停止位、校验位、流控)的实际意义,并能编写稳定的串口打开/关闭代码。
4.1 pyserial库:Python串口通讯的瑞士军刀
pyserial 是 Python 生态里最成熟的串口库,没有之一。它封装了底层操作系统对串口的操作,让你不用关心 Windows 的 COM 口和 Linux 的 tty 设备有什么区别。
4.1.1 安装与导入
安装很简单,一行命令搞定:
pip install pyserial
我个人习惯在项目里用虚拟环境,避免污染全局包。如果你也遇到“装上了但导入报错”的情况,八成是 pip 装到了系统 Python,而你的 IDE 用的是虚拟环境里的 Python。
导入方式:
import serial
注意,库名叫 pyserial,但导入时用的是 serial。这个坑我刚开始也踩过,一直以为要 import pyserial,结果报模块找不到。
4.1.2 核心类与常用方法
pyserial 的核心就是 serial.Serial 这个类。它代表一个串口对象,所有操作都围绕它展开。
| 方法/属性 | 说明 | 我在项目中常用的场景 |
|---|---|---|
Serial(port, baudrate, ...) |
创建串口对象,同时打开串口 | 初始化连接时直接传参 |
open() |
打开串口 | 先创建对象,后续再打开 |
close() |
关闭串口 | 程序退出或异常时一定要调用 |
is_open |
判断串口是否已打开 | 调试时检查状态 |
read(size) |
读取指定字节数 | 接收固定长度的指令帧 |
write(data) |
发送数据(需bytes类型) | 发送运动指令 |
flush() |
清空缓冲区 | 发送前清空,避免残留数据干扰 |
小技巧: 我习惯用 with serial.Serial(...) as ser: 这种上下文管理器写法,它会自动在退出时关闭串口,省得你忘记调用 close()。
4.2 串口参数配置:五个参数决定通讯成败
串口通讯的参数配置,说白了就是让上位机和下位机“说同一种语言”。如果参数对不上,数据就是一堆乱码。
五个核心参数:波特率、数据位、停止位、校验位、流控。
4.2.1 波特率(Baudrate)
波特率代表每秒传输的符号数。常见的值有:9600、19200、38400、115200、921600。
我遇到过最坑的一次,是客户现场的设备波特率设成了 14400,我默认用 9600 去连,死活收不到正确数据。折腾了半小时才发现是波特率不匹配。
避坑指南: 波特率越高,传输越快,但抗干扰能力越差。长距离通讯(比如超过10米)建议用 9600 或 19200。短距离调试,115200 是主流。
4.2.2 数据位(Data Bits)
数据位表示每个数据包中实际数据的位数。常见的是 8 位,也有 7 位、6 位、5 位。
现在绝大多数设备都用 8 位数据位。为什么?因为 8 位刚好是一个字节,方便和计算机内存对齐。如果你遇到 7 位数据位的设备,那多半是老古董了。
4.2.3 停止位(Stop Bits)
停止位用于标识一个数据包的结束。常见值:1、1.5、2。
我个人习惯用 1 位停止位。只有在通讯环境特别恶劣(比如强电磁干扰)时,才会考虑用 2 位停止位来增加容错。
4.2.4 校验位(Parity)
校验位用于简单的错误检测。选项有:
- N(None):无校验。最常用,速度最快。
- E(Even):偶校验。保证数据中1的个数为偶数。
- O(Odd):奇校验。保证数据中1的个数为奇数。
- S(Space):校验位固定为0。
- M(Mark):校验位固定为1。
我曾经在一个老款 PLC 项目里,设备手册写的是“偶校验”,但我配了偶校验还是通讯失败。后来用示波器抓波形才发现,它实际用的是 Space 校验。嗯,手册也会骗人。
4.2.5 流控(Flow Control)
流控用于协调收发双方的速度。分硬件流控(RTS/CTS)和软件流控(XON/XOFF)。
做运动控制上位机,我几乎从来不用流控。因为运动控制指令通常很短,发送频率也不高,流控反而增加了复杂度。除非你是在做高速数据采集,否则关掉它。
4.3 打开与关闭串口:代码实战
理论说完了,我们直接上代码。
4.3.1 创建串口对象并打开
有两种方式:
方式一:创建时直接打开
import serial
ser = serial.Serial(
port='COM3', # Windows下用COM口,Linux下用/dev/ttyUSB0
baudrate=115200,
bytesize=8, # 数据位
parity='N', # 无校验
stopbits=1, # 1位停止位
timeout=1 # 超时时间,单位秒
)
if ser.is_open:
print(f"串口 {ser.port} 已打开,波特率 {ser.baudrate}")
方式二:先创建,后打开
import serial
ser = serial.Serial()
ser.port = 'COM3'
ser.baudrate = 115200
ser.bytesize = 8
ser.parity = 'N'
ser.stopbits = 1
ser.timeout = 1
ser.open()
我个人更推荐方式一,代码更紧凑,参数一目了然。
4.3.2 关闭串口
关闭串口很简单,但很多人会忘记:
ser.close()
你可能会想:“程序退出时系统会自动释放资源吧?” 理论上是,但实际项目中,如果程序异常退出,串口可能还处于占用状态。下次再打开同一个串口,就会报 PermissionError。
避坑指南: 我曾经在一个无人值守的测试机上,因为程序崩溃没来得及关闭串口,导致第二天运维人员重启程序后串口打不开,整条产线停了半小时。从那以后,我所有串口操作都加了 try-finally 或 with 语句。
4.3.3 完整的打开-发送-关闭示例
import serial
def send_command(cmd: str):
try:
with serial.Serial(
port='COM3',
baudrate=115200,
bytesize=8,
parity='N',
stopbits=1,
timeout=1
) as ser:
# 发送指令,需要转成bytes
ser.write(cmd.encode('utf-8'))
print(f"发送: {cmd}")
# 读取响应
response = ser.read(100)
print(f"接收: {response}")
except serial.SerialException as e:
print(f"串口异常: {e}")
except Exception as e:
print(f"其他异常: {e}")
# 调用
send_command("M100\r\n")
这段代码里,with 语句保证了无论是否发生异常,串口都会在退出时自动关闭。这是最安全的写法。
4.4 本章知识体系总览
为了让你对本章内容有个整体印象,我画了一张结构图。它把串口通讯的核心要素串在了一起:
4.5 本章小结
这一章我们干了三件事:
- 认识了 pyserial 库,知道怎么装、怎么导入、核心类有哪些。
- 搞懂了串口五大参数的含义和配置要点,尤其是波特率不匹配这个最常见的坑。
- 写了完整的打开-发送-关闭代码,并且强调了
with语句的重要性。
串口通讯看起来简单,但实际项目中,90% 的通讯问题都出在参数配置上。你想想看,波特率差一位,数据就全乱了。所以,每次连接新设备,第一件事就是确认设备手册上的参数,然后逐项核对。