【python】导入serial模块,读写串口数据(嵌入式软件自动化测试)

'# 【python】导入serial模块,读写串口数据(嵌入式软件自动化测试)

一、背景与问题

在嵌入式软件开发和自动化测试领域,串口通信是设备间数据传输的常见方式。Python的serial模块为开发者提供了对串口通信的高级抽象,但其底层原理和使用细节往往被忽视。本文将深入剖析serial模块的工作原理,结合真实开发场景,探讨其在自动化测试中的应用边界和实践技巧。

二、基本原理

串口通信(Serial Communication)是通过异步串行通信协议(RS-232/RS-485等)进行数据传输的技术。核心参数包括:

  1. 波特率(Baud Rate):单位时间内传输的比特数(如9600 bps)
  2. 数据位(Data Bits):通常为8位
  3. 停止位(Stop Bits):1或2位
  4. 校验位(Parity):偶校验/奇校验/无校验

serial模块通过封装底层系统调用(Linux的termios、Windows的CreateFile等),将复杂的通信参数抽象为简单的API。其核心工作原理包括:

  • 设备打开:获取串口文件描述符
  • 参数配置:设置波特率、校验位等
  • 数据传输:通过write/read进行数据交换
  • 异常处理:处理通信中断、超时等异常

三、环境准备

安装依赖

pip install pyserial

系统检测

Linux系统需确认串口设备节点:

dmesg | grep tty

Windows系统需通过设备管理器查看COM端口。

软件配置

确保开发环境支持串口通信,如在Linux中需赋予设备读写权限:

sudo chmod 666 /dev/ttyUSB0

四、核心实现

1. 基础读写示例

import serial

def basic_serial_operations():
    # 创建串口对象
    ser = serial.Serial(
        port='/dev/ttyUSB0',   # 设备路径
        baudrate=9600,         # 波特率
        parity=serial.PARITY_NONE,  # 无校验
        stopbits=serial.STOPBITS_ONE,  # 1位停止位
        bytesize=serial.EIGHTBITS   # 8位数据位
    )
    
    try:
        # 发送数据
        ser.write(b'Hello, Serial!\n')
        
        # 接收数据
        response = ser.read(100)
        print(f"Received: {response.decode('utf-8')}")
    finally:
        # 关闭串口
        ser.close()

关键代码解析:

  • serial.Serial参数含义需与设备实际参数匹配
  • write方法接受bytes类型数据
  • read方法需要指定读取长度,避免阻塞

2. 带超时的读取

def timeout_read_example():
    ser = serial.Serial(
        port='/dev/ttyUSB0',
        baudrate=115200,
        timeout=1  # 设置读取超时时间(秒)
    )
    
    try:
        ser.write(b'Query\n')
        data = ser.read(1024)
        if not data:
            print("No response received")
        else:
            print(f"Response: {data.decode()}")
    finally:
        ser.close()

关键点:

  • timeout参数控制读取阻塞时间
  • 空数据返回时需特殊处理
  • 避免在主线程中长时间阻塞

3. 多线程处理

import threading

def read_thread(ser):
    while True:
        data = ser.read(1024)
        if data:
            print(f"Thread received: {data.decode()}")
        else:
            break

def multi_thread_example():
    ser = serial.Serial('/dev/ttyUSB0', 115200)
    
    # 启动读线程
    reader_thread = threading.Thread(target=read_thread, args=(ser,))
    reader_thread.start()
    
    # 主线程发送数据
    ser.write(b'Test\n')
    ser.close()

注意事项:

  • 多线程需注意资源竞争
  • 建议使用with语句管理资源
  • 线程间通信需使用队列或锁

五、完整案例:温度传感器测试

场景描述

模拟测试一个温度传感器设备,通过串口发送读取指令,验证返回数据格式。

实现代码

import serial
import time
import unittest

class TemperatureSensorTest(unittest.TestCase):
    def setUp(self):
        self.ser = serial.Serial(
            port='/dev/ttyUSB0',
            baudrate=9600,
            timeout=1
        )
    
    def test_temperature_read(self):
        # 发送读取指令
        self.ser.write(b'READ_TEMP\n')
        
        # 等待响应
        time.sleep(0.5)
        
        # 读取响应
        response = self.ser.read(100)
        self.assertIn(b'OK', response)
        self.assertTrue(len(response) > 10)
        
        # 解析温度数据
        temp_str = response.decode().split(':')[1].strip()
        temp = float(temp_str)
        self.assertTrue(-50 <= temp <= 120)
    
    def tearDown(self):
        self.ser.close()

if __name__ == '__main__':
    unittest.main()

关键点:

  • 测试用例包含完整生命周期管理
  • 验证响应格式和数据有效性
  • 模拟真实设备的响应时间

六、源码解析

pyserial的源码核心逻辑位于serial/serial.py,关键部分包括:

class Serial:
    def __init__(self, port, baudrate, ...):
        self.fd = open(port, 'r+b')  # 打开设备文件
        self._set_port_params()  # 设置通信参数
        
    def _set_port_params(self):
        # 设置波特率
        self._set_baudrate()
        
        # 设置校验位
        self._set_parity()
        
        # 设置停止位
        self._set_stopbits()
        
    def write(self, data):
        # 发送数据
        os.write(self.fd, data)
        
    def read(self, size):
        # 读取数据
        return os.read(self.fd, size)

关键机制:

  • 使用os.open获取设备文件描述符
  • 通过底层系统调用设置通信参数
  • 使用try...finally确保资源释放

七、进阶使用

1. 协议解析

处理复杂协议时需添加校验机制:

def parse_data(data):
    # 假设数据格式为:[CMD][LEN][DATA][CRC]
    if len(data) < 5:
        return None
        
    cmd = data[0]
    length = int(data[1:3])
    payload = data[3:3+length]
    crc = data[3+length:]
    
    # 验证CRC
    if compute_crc(payload) != crc:
        return None
        
    return {
        'cmd': cmd,
        'payload': payload
    }

2. 基于事件的通信

使用select模块实现非阻塞通信:

import select

def nonblocking_read(ser):
    r, w, e = select.select([ser.fd], [], [], 0.1)
    if r:
        return ser.read(1024)
    return None

3. 数据缓存机制

处理突发数据时可添加缓冲区:

class Buffer:
    def __init__(self, size=1024):
        self.buf = bytearray()
        self.size = size
        
    def write(self, data):
        self.buf.extend(data)
        if len(self.buf) > self.size:
            self.buf = self.buf[-self.size:]
            
    def read(self, size):
        data = self.buf[:size]
        self.buf = self.buf[size:]
        return data

八、性能与工程实践

1. 性能优化策略

优化措施说明
缓冲读取使用read(1024)代替read(1)
避免频繁open使用with管理资源
非阻塞模式设置timeout=0
数据压缩对连续数据进行压缩处理

2. 安全风险分析

  • 数据完整性:未校验的通信可能导致数据篡改
  • 拒绝服务:恶意数据可导致缓冲区溢出
  • 权限控制:需限制对串口设备的访问权限

3. 异常处理

try:
    ser = serial.Serial('/dev/ttyUSB0', 9600)
    ser.write(b'CMD\n')
except serial.SerialException as e:
    print(f"Serial error: {e}")
finally:
    ser.close()

九、常见问题与踩坑

1. 常见错误

错误现象原因解决方案
无法打开设备端口权限不足使用sudo或修改权限
通信失败波特率不匹配与设备参数完全一致
数据丢失未设置超时合理配置timeout
线程冲突资源未释放使用with语句管理资源

2. 特殊场景处理

Windows系统:

import serial.tools.list_ports

ports = serial.tools.list_ports.comports()
for p in ports:
    print(p.device)

Linux系统:

import serial

ser = serial.Serial('/dev/ttyUSB0', 115200)
print(ser.name)

十、最佳实践

  1. 资源管理:使用with语句确保资源释放
  2. 参数校验:确保所有通信参数与设备一致
  3. 日志记录:添加详细日志便于调试
  4. 异常处理:覆盖所有可能的异常情况
  5. 版本兼容:注意不同系统下参数差异
  6. 测试覆盖:编写完整的测试用例
  7. 性能监控:监控通信吞吐量和延迟

十一、总结

serial模块为嵌入式开发和自动化测试提供了强大的串口通信支持,但其应用需要深入理解底层原理和实际场景。在开发过程中,需要重点关注参数配置、异常处理和资源管理,同时结合具体业务需求选择合适的通信策略。对于高并发或实时性要求的场景,可考虑使用更高级的通信框架(如pyserial-asyncio)。通过合理的实践和规范的开发流程,可以充分发挥串口通信的优势,提升嵌入式系统的可靠性和可维护性。

最后修改于:2026年10月01日 13:33

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日