2. Python串口库安装与配置:pyserial库安装、端口扫描、基本读写操作

好,咱们正式开始动手了。上一章聊了串口的基本概念,这一章咱们把Python环境搭起来,让电脑和开发板真正说上话。

我个人习惯把串口通信比作「打电话」——你得先装好电话机(安装库),查一下对方号码(端口扫描),然后才能拨号通话(读写操作)。一步步来,别急。

2.1 pyserial库安装

Python操作串口,最主流的库就是pyserial。它跨平台,Windows、Linux、macOS都能用。我在项目中遇到过不少新手用错库,装了serial结果导入失败——嗯,这里要注意,pyserial安装后的包名是serial,别搞混了。

2.1.1 安装命令

打开终端或命令提示符,直接敲:

pip install pyserial

如果你用的是Python 3,有些系统需要指定pip3:

pip3 install pyserial
小提示: 我曾经在Windows上遇到过pip报错,多半是环境变量没配好。建议安装Python时勾选「Add Python to PATH」,省去很多麻烦。

2.1.2 验证安装

装完之后怎么确认成功了?打开Python交互环境,输入:

import serial
print(serial.__version__)

如果能正常输出版本号(比如3.5),恭喜你,电话机装好了。

常见坑: 如果你同时装了Python 2和Python 3,记得用python -m pip install pyserial指定解释器。我踩过这个坑,装了半天发现导入了旧版本的serial库。

2.2 端口扫描——找到你的设备

电话机装好了,现在得知道对方号码是多少。串口设备插上电脑后,系统会分配一个端口名。Windows上通常是COM3、COM4这样的格式,Linux/macOS则是/dev/ttyUSB0或/dev/ttyAMA0。

手动去设备管理器里翻当然可以,但咱们写程序的人,讲究自动化。pyserial提供了serial.tools.list_ports模块,能帮你扫出所有可用端口。

2.2.1 扫描所有端口

import serial.tools.list_ports as ports

port_list = ports.comports()
for port in port_list:
    print(f"端口: {port.device}")
    print(f"描述: {port.description}")
    print(f"硬件ID: {port.hwid}")
    print("-" * 40)

跑一下这段代码,你会看到类似这样的输出:

端口: COM3
描述: USB Serial Port (COM3)
硬件ID: USB VID:PID=1A86:7523 SER=5 LOCATION=1-2
----------------------------------------

硬件ID那一串很有用。我曾在现场调试时,电脑上插了七八个USB转串口设备,靠硬件ID里的VID/PID才能精准锁定目标设备。

2.2.2 按条件筛选端口

有时候你只想找某个特定厂商的设备,可以加个过滤:

import serial.tools.list_ports as ports

def find_serial_port(vid=None, pid=None):
    """按VID/PID查找串口"""
    for port in ports.comports():
        if vid and pid:
            if f"VID:PID={vid:04X}:{pid:04X}" in port.hwid:
                return port.device
        else:
            # 返回第一个可用的串口
            return port.device
    return None

# 举例:查找CH340芯片的设备(VID=0x1A86, PID=0x7523)
port_name = find_serial_port(vid=0x1A86, pid=0x7523)
print(f"找到设备: {port_name}")
注意: 端口扫描只能列出当前已连接的设备。如果你插拔了设备,需要重新扫描。我曾经写过一个自动重连逻辑,每隔2秒扫一次端口列表,发现新设备就自动连接——这个技巧后面章节会细讲。

2.3 基本读写操作

号码找到了,现在拨号通话。pyserial的读写操作其实就三个核心步骤:打开端口 → 读写数据 → 关闭端口。

2.3.1 打开串口

import serial

# 打开串口
ser = serial.Serial(
    port='COM3',        # 端口名
    baudrate=115200,    # 波特率
    bytesize=8,         # 数据位
    parity='N',         # 校验位:N-无, E-偶校验, O-奇校验
    stopbits=1,         # 停止位
    timeout=1           # 超时时间(秒)
)

# 检查是否打开成功
if ser.is_open:
    print(f"串口 {ser.port} 已打开,波特率 {ser.baudrate}")

参数看着多,其实大部分时候你只需要改port和baudrate。其他参数用默认值就行。我习惯把波特率设成115200,因为大多数开发板默认就是这个值,而且速度够快。

2.3.2 发送数据

发送数据用write()方法。注意它接收的是字节数据,不是字符串:

# 发送字符串(需要编码)
data = "Hello, Serial!\n"
ser.write(data.encode('utf-8'))

# 发送十六进制数据
ser.write(b'\xAA\x55\x01\x02\x03')

为什么强调字节?因为串口底层传输的就是字节流。字符串只是人类可读的表示形式。我在调试一个传感器时,对方协议要求发送0xAA 0x55 0x01三个字节,新手直接发字符串"AA5501",结果设备完全不响应——说白了,协议解析失败。

2.3.3 接收数据

接收数据有三种常用方式:

方法 说明 适用场景
ser.read(size) 读取指定字节数 固定长度数据包
ser.readline() 读取一行(遇到换行符停止) 文本协议、AT指令
ser.read_until(expected) 读取直到遇到指定字节 自定义结束符的协议

举个例子,读取一行文本:

# 读取一行数据
line = ser.readline()
print(f"收到: {line.decode('utf-8').strip()}")

读取固定长度的二进制数据:

# 读取10个字节
data = ser.read(10)
print(f"收到十六进制: {data.hex()}")

2.3.4 关闭串口

用完记得关,不然下次打开会报错:

ser.close()
print("串口已关闭")

更好的做法是用with语句,自动管理资源:

with serial.Serial('COM3', 115200, timeout=1) as ser:
    ser.write(b'Hello\n')
    response = ser.readline()
    print(response.decode().strip())
# 退出with块后自动关闭
避坑指南: 我曾经在程序崩溃时没关串口,导致下次运行时报SerialException: could not open port。后来我养成了两个习惯:一是用with语句,二是在程序入口加一个try...finally确保关闭。你最好也这么做。

2.4 本章知识体系

下面这张图把本章的核心流程串起来了,你保存下来,以后写串口程序时对照着看:

pyserial库安装与配置知识体系 pyserial库安装与配置 · 核心流程 ① 安装pyserial pip install pyserial ② 端口扫描 list_ports.comports() ③ 打开串口 serial.Serial(port, baudrate) ④ 读写操作 发送:ser.write() 接收:ser.read() 读取一行:readline() ⑤ 关闭串口 ser.close() 或 with语句自动关闭

2.5 完整示例:一发一收

最后给一个完整的收发示例,你直接复制就能跑:

import serial
import serial.tools.list_ports as ports
import time

def main():
    # 1. 扫描端口
    port_list = ports.comports()
    if not port_list:
        print("未发现串口设备")
        return
    
    # 自动选择第一个可用端口
    port_name = port_list[0].device
    print(f"使用端口: {port_name}")
    
    # 2. 打开串口
    try:
        ser = serial.Serial(
            port=port_name,
            baudrate=115200,
            timeout=1
        )
        print(f"串口已打开: {ser.port} @ {ser.baudrate}bps")
    except serial.SerialException as e:
        print(f"打开串口失败: {e}")
        return
    
    # 3. 发送数据
    send_data = "Hello from Python!\n"
    ser.write(send_data.encode('utf-8'))
    print(f"发送: {send_data.strip()}")
    
    # 4. 接收响应
    time.sleep(0.1)  # 给设备一点处理时间
    response = ser.readline()
    if response:
        print(f"收到: {response.decode('utf-8').strip()}")
    else:
        print("超时,未收到响应")
    
    # 5. 关闭串口
    ser.close()
    print("串口已关闭")

if __name__ == "__main__":
    main()
重要提醒: 运行这个示例前,请确保你的开发板已经通过USB线连接到电脑,并且开发板的串口输出是开启的。如果收不到响应,先检查波特率是否匹配——这是最容易被忽略的问题。