python 调用usb

'# Python 调用 USB

一、背景与问题

在嵌入式开发、工业自动化、物联网等场景中,Python 作为脚本语言常用于与硬件设备交互。USB(Universal Serial Bus)作为最常用的通信接口,其协议复杂性使得直接通过 Python 调用 USB 设备成为一项挑战。传统方法需要通过底层库(如 libusb)实现,但 Python 提供了更高级的抽象接口,例如 pyusb、libusb1 等。本文将深入解析 Python 调用 USB 的工作原理,结合实际开发场景分析其适用性与注意事项。


二、基本原理

USB 协议的核心是通过主机(Host)与设备(Device)的通信。其通信流程可分为以下阶段:

  1. 枚举设备:主机通过控制传输获取设备的描述符(Descriptor),包括设备ID、接口配置等信息。
  2. 端点通信:设备定义多个端点(Endpoint),每个端点对应不同的传输类型(控制、批量、中断、等时)。
  3. 数据传输:通过特定端点发送/接收数据,传输类型决定数据传输的可靠性与实时性。

Python 调用 USB 的核心是通过库封装 USB 协议细节,提供对设备的访问接口。例如,pyusb 库将 USB 设备抽象为 Device 对象,通过 Interface 和 Endpoint 实现数据交互。


三、环境准备

1. 安装依赖库

# 安装 pyusb(支持 Linux/Windows/macOS)
pip install pyusb

# 安装 libusb1(底层接口,跨平台)
pip install libusb1

# 安装 pywinusb(仅限 Windows)
pip install pywinusb

2. 系统要求

  • Linux:需要 libusb-1.0 开发库,安装命令:

    sudo apt-get install libusb-1.0-0-dev
  • Windows:需确保 USB 设备驱动已安装。

3. 硬件准备

需准备一个 USB 设备(如 USB 串口转 TTL 模块、传感器等),或使用虚拟 USB 设备工具(如 usbguard 或 libusb 的虚拟设备功能)进行测试。


四、核心实现

1. 基础调用流程(pyusb)

import usb.core
import usb.util

# 1. 查找设备
device = usb.core.find(idVendor=0x1234, idProduct=0x5678)
if device is None:
    raise ValueError("Device not found")

# 2. 设置配置
device.set_configuration()

# 3. 获取接口
interface = device[0][(0, 0)]  # 索引为0的接口,配置索引0

# 4. 获取端点
endpoint = interface[0]  # 索引为0的端点

# 5. 读取数据
data = endpoint.read(64)  # 读取64字节
print("Received data:", data)

关键解释:

  • find() 方法通过 Vendor ID 和 Product ID 定位设备,若未指定参数则枚举所有设备。
  • set_configuration() 用于激活设备的默认配置。
  • endpoint.read() 用于从端点读取数据,参数为最大字节数。

2. 控制传输(Control Transfer)

# 控制传输示例:请求设备返回描述符
request_type = usb.util.CONTROL_OUT
request = 0x06  # GET_DESCRIPTOR
value = 0x0003  # 设备描述符
index = 0x0000

# 发送控制传输请求
device.ctrl_transfer(
    request_type, request, value, index, 64
)

关键解释:

  • 控制传输用于设备初始化、配置等场景,例如获取设备描述符。
  • ctrl_transfer() 方法的参数含义:

    • request_type:请求类型(CONTROL_OUT 表示主机向设备发送请求)。
    • request:请求代码(如 GET_DESCRIPTOR)。
    • value:参数值(如描述符类型)。
    • index:接口索引。
    • wLength:数据长度。

3. 批量传输(Bulk Transfer)

# 批量传输示例:向设备发送数据
endpoint.write(b"Hello USB")  # 发送字节数据

# 接收数据
response = endpoint.read(64)
print("Received:", response)

关键解释:

  • 批量传输适用于大块数据传输,具有重传机制,但延迟较高。
  • write() 和 read() 方法需确保数据长度与端点的 wMaxPacketSize 匹配。

五、完整案例:USB 串口通信

场景描述

假设使用 USB 串口转 TTL 模块(如 CP2102),通过 Python 读取传感器数据。

实现步骤

  1. 安装 pyserial 作为辅助库(用于串口通信)。
  2. 使用 pyusb 与设备通信,模拟串口数据传输。
import usb.core
import usb.util
import serial

# 查找 USB 串口设备
device = usb.core.find(idVendor=0x067B, idProduct=0x2303)  # CP2102 的 Vendor ID

if device is None:
    raise ValueError("CP2102 device not found")

# 设置配置
device.set_configuration()

# 获取接口
interface = device[0][(0, 0)]

# 获取端点
endpoint = interface[0]  # 假设使用端点0

# 转换为串口通信(模拟)
ser = serial.Serial(
    port='/dev/ttyUSB0',  # Linux 下的串口路径
    baudrate=115200,
    timeout=1
)

# 读取数据
while True:
    data = ser.read(1024)
    print("Received from serial:", data)

关键点:

  • 实际 USB 串口设备可能需要通过 libusb 或 pyusb 直接读写,或通过 pyserial 抽象。
  • 需在系统中设置串口权限(如 sudo chmod 666 /dev/ttyUSB0)。

六、源码解析

以 pyusb 的核心模块为例,分析其如何封装 USB 协议:

1. 设备枚举(find() 方法)

def find(**kwargs):
    # 遍历所有 USB 设备,匹配参数
    for dev in usb.util.find_all_devices():
        if all(getattr(dev, key) == value for key, value in kwargs.items()):
            return dev
    return None

关键点:

  • 使用 libusb 的底层 API 实现设备枚举。
  • 通过 getattr() 检查设备属性是否匹配。

2. 控制传输实现

def ctrl_transfer(self, request_type, request, value, index, wLength):
    # 调用 libusb 的控制传输 API
    return usb.libusb.ctrl_transfer(
        self.handle, request_type, request, value, index, wLength
    )

关键点:

  • libusb.ctrl_transfer() 是底层 USB 控制传输的核心函数。
  • request_type 决定请求方向(主机到设备或设备到主机)。

七、进阶使用

1. 多端点通信

# 获取多个端点
endpoint_in = interface[0]  # 输入端点
endpoint_out = interface[1]  # 输出端点

# 同时发送和接收数据
endpoint_out.write(b"Command")
response = endpoint_in.read(64)

2. 非阻塞模式

# 设置端点为非阻塞模式
endpoint.set_options(usb.util.ENDPOINT_TYPE_BULK)

3. 自定义协议

通过解析设备返回的数据包,实现自定义通信协议:

def parse_data(data):
    # 假设数据格式为 4 字节长度 + 2 字节数据
    if len(data) < 6:
        return None
    length = int.from_bytes(data[:4], 'little')
    payload = data[4:4+length]
    return payload

八、性能与工程实践

1. 性能优化

  • 批量传输:优先使用批量传输处理大块数据。
  • 减少阻塞:使用非阻塞模式(set_options(usb.util.ENDPOINT_TYPE_BULK))。
  • 预分配缓冲区:避免频繁内存分配,使用固定大小的缓冲区。

2. 异常处理

try:
    data = endpoint.read(64)
except usb.core.USBError as e:
    print("USB Error:", e)

3. 安全风险

  • 权限控制:避免以 root 权限运行,使用 sudo 管理设备访问。
  • 数据校验:对设备返回的数据进行校验,防止注入攻击。
  • 设备签名:在固件中对 USB 设备进行签名,防止伪造设备。

九、常见问题与踩坑

1. 设备未找到

错误示例:

device = usb.core.find(idVendor=0x1234)

原因:未指定 idProduct,导致枚举所有设备时未匹配。

解决方法:明确指定设备 ID 或使用 find_all_devices() 遍历。

2. 权限问题(Linux)

错误示例:

device = usb.core.find(idVendor=0x1234)
device.set_configuration()

原因:未以 root 权限运行,导致设备未被正确访问。

解决方法:

sudo python script.py

3. 数据传输错误

错误示例:

endpoint.read(64)  # 读取长度超过端点最大包大小

原因:端点的 wMaxPacketSize 限制了最大传输长度。

解决方法:分多次读取,或调整端点配置。


十、最佳实践

  1. 优先使用 pyusb:其封装程度高,适合快速开发。
  2. 避免直接操作 USB 协议:除非有特殊需求,否则无需手动处理 USB 握手协议。
  3. 多线程处理:对实时性要求高的场景,可使用线程或异步库(如 asyncio)。
  4. 日志记录:在关键步骤添加日志,便于调试 USB 通信问题。

十一、总结

Python 调用 USB 的核心在于封装 USB 协议细节,通过 pyusb、libusb1 等库实现设备访问。本文深入分析了 USB 通信原理、代码实现、性能优化及安全风险,并结合实际案例(如 USB 串口通信)说明其应用场景。在开发中,需根据需求选择合适的库,同时注意权限控制、数据校验等安全问题。对于复杂场景,可结合 pyserial 等辅助库实现更高级的通信功能。

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

评论已关闭

推荐阅读

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日