python 调用usb
'# Python 调用 USB
一、背景与问题
在嵌入式开发、工业自动化、物联网等场景中,Python 作为脚本语言常用于与硬件设备交互。USB(Universal Serial Bus)作为最常用的通信接口,其协议复杂性使得直接通过 Python 调用 USB 设备成为一项挑战。传统方法需要通过底层库(如 libusb)实现,但 Python 提供了更高级的抽象接口,例如 pyusb、libusb1 等。本文将深入解析 Python 调用 USB 的工作原理,结合实际开发场景分析其适用性与注意事项。
二、基本原理
USB 协议的核心是通过主机(Host)与设备(Device)的通信。其通信流程可分为以下阶段:
- 枚举设备:主机通过控制传输获取设备的描述符(Descriptor),包括设备ID、接口配置等信息。
- 端点通信:设备定义多个端点(Endpoint),每个端点对应不同的传输类型(控制、批量、中断、等时)。
- 数据传输:通过特定端点发送/接收数据,传输类型决定数据传输的可靠性与实时性。
Python 调用 USB 的核心是通过库封装 USB 协议细节,提供对设备的访问接口。例如,pyusb 库将 USB 设备抽象为 Device 对象,通过 Interface 和 Endpoint 实现数据交互。
三、环境准备
1. 安装依赖库
# 安装 pyusb(支持 Linux/Windows/macOS)
pip install pyusb
# 安装 libusb1(底层接口,跨平台)
pip install libusb1
# 安装 pywinusb(仅限 Windows)
pip install pywinusb2. 系统要求
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 读取传感器数据。
实现步骤
- 安装
pyserial作为辅助库(用于串口通信)。 - 使用
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.py3. 数据传输错误
错误示例:
endpoint.read(64) # 读取长度超过端点最大包大小原因:端点的 wMaxPacketSize 限制了最大传输长度。
解决方法:分多次读取,或调整端点配置。
十、最佳实践
- 优先使用 pyusb:其封装程度高,适合快速开发。
- 避免直接操作 USB 协议:除非有特殊需求,否则无需手动处理 USB 握手协议。
- 多线程处理:对实时性要求高的场景,可使用线程或异步库(如
asyncio)。 - 日志记录:在关键步骤添加日志,便于调试 USB 通信问题。
十一、总结
Python 调用 USB 的核心在于封装 USB 协议细节,通过 pyusb、libusb1 等库实现设备访问。本文深入分析了 USB 通信原理、代码实现、性能优化及安全风险,并结合实际案例(如 USB 串口通信)说明其应用场景。在开发中,需根据需求选择合适的库,同时注意权限控制、数据校验等安全问题。对于复杂场景,可结合 pyserial 等辅助库实现更高级的通信功能。
评论已关闭