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 本章知识体系总览

为了让你对本章内容有个整体印象,我画了一张结构图。它把串口通讯的核心要素串在了一起:

串口通讯知识体系结构图 串口通讯实战(一)知识体系 pyserial 库 安装与导入 核心类与方法 参数配置 pip install pyserial import serial Serial() / open() / close() read() / write() / flush() 波特率 / 数据位 / 停止位 校验位 / 流控 打开与关闭串口(实战) 创建时直接打开 先创建后打开 with语句自动关闭

4.5 本章小结

这一章我们干了三件事:

  • 认识了 pyserial 库,知道怎么装、怎么导入、核心类有哪些。
  • 搞懂了串口五大参数的含义和配置要点,尤其是波特率不匹配这个最常见的坑。
  • 写了完整的打开-发送-关闭代码,并且强调了 with 语句的重要性。

串口通讯看起来简单,但实际项目中,90% 的通讯问题都出在参数配置上。你想想看,波特率差一位,数据就全乱了。所以,每次连接新设备,第一件事就是确认设备手册上的参数,然后逐项核对。