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
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.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 本章知识体系
下面这张图把本章的核心流程串起来了,你保存下来,以后写串口程序时对照着看:
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()