第3章:Python环境搭建与串口通信库

做嵌入式调试这么多年,我有个习惯——拿到一块新板子,第一件事就是看看串口能不能通。为什么?因为串口是嵌入式系统的「生命线」。你想想看,系统跑没跑起来、打印了什么信息、哪里卡住了,全得靠它。这一章,我们就来搞定Python环境下的串口通信。

3.1 Python环境搭建

我个人习惯用Python 3.8以上的版本。为什么?因为pySerial在3.8上跑得最稳,我踩过3.6的坑,有些旧版本对串口缓冲区的处理不太一样。

安装Python很简单,去官网下载安装包就行。但有个细节要注意——安装时记得勾选「Add Python to PATH」。我曾经帮一个同事排查了半天,结果发现他没勾这个选项,命令行里死活找不到python命令。

装好之后,打开命令行(Windows用cmd或PowerShell,Mac/Linux用终端),输入:

python --version

如果看到版本号,说明环境搭好了。嗯,这一步很简单,但很多人会卡在这里。

3.2 pySerial的安装

pySerial是Python操作串口的标配库。安装它只需要一行命令:

pip install pyserial

如果你用的是Python 3,可能需要用pip3:

pip3 install pyserial

装完之后,验证一下:

python -c "import serial; print(serial.__version__)"

如果输出版本号,比如3.5,那就搞定了。我记得有一次在Linux上装,系统自带的Python和pip版本不匹配,折腾了半天。后来发现用python3 -m pip install pyserial就能解决。

小提示: 如果你在公司内网,pip可能连不上外网。可以先用手机热点下载whl文件,或者配置国内镜像源,比如清华的镜像:pip install pyserial -i https://pypi.tuna.tsinghua.edu.cn/simple

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

拿到一块新板子,插上USB转串口线,系统里会多出一个端口。但端口号是多少?Windows上可能是COM3、COM5,Linux上可能是ttyUSB0、ttyAMA0。手动去设备管理器里翻?太慢了。

我写了个小脚本,专门用来扫描可用端口:

import serial.tools.list_ports

ports = serial.tools.list_ports.comports()
for port in ports:
    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=12345678 LOCATION=1-2
----------------------------------------

这里有个小技巧——看硬件ID。VID和PID是USB设备的身份标识。比如1A86:7523是CH340芯片的,067B:2303是PL2303的。我在项目中遇到过,同一个电脑插了两个不同厂家的串口线,光看端口号分不清谁是谁。这时候看硬件ID就一目了然。

避坑指南: 我曾经在Linux上扫描端口,发现有些端口虽然列出来了,但实际打不开。原因是权限不够。解决方法:把用户加到dialout组里——sudo usermod -a -G dialout $USER,然后重新登录。

3.4 基础读写操作——让数据流动起来

端口找到了,接下来就是打开它、读写数据。这是串口通信的核心。

先看一个最简单的例子:

import serial

# 打开串口
ser = serial.Serial(
    port='COM3',      # 端口号,根据实际情况改
    baudrate=115200,  # 波特率,常见的有9600、115200
    timeout=1         # 超时时间,单位秒
)

# 发送数据
ser.write(b'Hello, World!\n')

# 读取数据
data = ser.readline()
print(f"收到: {data}")

# 关闭串口
ser.close()

这段代码看着简单,但有几个坑要注意。

第一个坑:波特率要匹配。 板子设的是115200,你这边用9600,收到的全是乱码。我刚开始做调试时,经常被这个问题搞晕。后来养成了习惯——先看板子的代码,确认波特率是多少。

第二个坑:超时设置。 timeout=1表示读数据时最多等1秒。如果不设超时,readline()会一直等下去,程序就卡死了。你想想看,要是板子突然不发了,你的程序就永远停在那了。

第三个坑:数据是字节流。 write()里传的是b'Hello',不是'Hello'。Python 3里字符串和字节串是两回事。我见过有人直接写ser.write('Hello'),结果报错TypeError。

3.5 实战:循环读取传感器数据

实际项目中,我们通常要持续读取数据。比如一个温度传感器,每秒发一次数据。代码可以这样写:

import serial
import time

ser = serial.Serial('COM3', 115200, timeout=1)

try:
    while True:
        # 读取一行数据
        line = ser.readline()
        if line:
            # 去掉换行符,转成字符串
            data = line.decode('utf-8').strip()
            print(f"[{time.strftime('%H:%M:%S')}] {data}")
        else:
            # 超时了,没收到数据
            print("等待数据中...")
except KeyboardInterrupt:
    print("用户中断")
finally:
    ser.close()

这里用try-finally保证串口一定能关闭。为什么?因为串口资源很宝贵,不关的话,下次再打开可能会报「端口被占用」。我在调试一个无人机飞控时,就因为忘了关串口,导致地面站软件连不上,排查了半天才发现是端口没释放。

注意: decode('utf-8')可能会报错,如果收到的数据不是有效的UTF-8编码。稳妥的做法是用errors='ignore'参数:line.decode('utf-8', errors='ignore')。这样遇到乱码就直接跳过,不会让程序崩溃。

3.6 常见问题与排查

串口通信出问题了怎么办?别慌,按这个顺序排查:

  1. 端口对不对? 用端口扫描脚本确认一下。
  2. 波特率对不对? 检查板子的配置。
  3. 接线对不对? TX接RX,RX接TX,GND接GND。我见过有人把TX接TX,结果数据发出去没人收。
  4. 权限够不够? Linux下记得加dialout组。
  5. 是不是被占用了? 关掉其他可能占用串口的软件,比如串口助手、IDE的串口监视器。

嗯,这套排查流程我用了十年,基本能解决90%的问题。

3.7 小结

这一章我们搞定了三件事:Python环境搭建、pySerial安装、端口扫描和读写操作。说白了,就是让电脑和板子能「说上话」。下一章,我们会深入解析传感器原始数据的格式,看看那些0和1背后到底藏着什么信息。

记住一句话:串口通了,调试就成功了一半。