网站首页 文章专栏 使用python开发usb的两种方式(windriver与pyusb)
最近在给一个FPGA板子做上位机界面,上位机与下位机的通信采用USB方案, 驱动采用WinDriver。
但在实际调试过程中,发现WinDriver不同版本之间兼容性差,并且在win10 上表现不佳。实际的数据传输流程如下:
python <-> usb dll(through ctypes) <-> windriver <-> usb device
由于dll文件是在win7机器上编译的,故仅能在win7上使用,在win10机器上出错。
使用python的项目都应该是简洁而优雅地,遂研究了在python操作usb device的两种方式。
驱动无关的调试软件使用bus hound
WinDriver经常与Jungo connectivity联系在一起,安装了WinDriver驱动的usb device在
设备管理器中也显示为Jungo devices
。
完整的WinDriver开发流程应该从驱动开始,使用C/C++调用WinDriver提供的库与usb device 通信,将此程序编译为dll供其他程序调用。
将usb device连接上电脑,使用WinDriver给设备安装驱动。
在python中使用ctypes调用上文中的dll,完成调用过程。
pyusb是一个python库,可以方便地使用python操作usb设备。pyusb的数据传输流程如下:
python <-> pyusb <-> pyusb backend <-> usb device
很明显可以看出省略了dll,大大减少了代码量。
具体使用过程: 1. 下载并安装pyusb backend 2. 连接usb device,使用pyusb backend安装驱动,我选择libusb,一般可以正常使用。不行就换其他的。 3. 编写python脚本,可以参考官方教程
缺点: 1. windriver有一个可视化的调试工具,可以单独发送接收数据以确定usb device是否正常,pyusb暂时没有找到。但 找到了一个非官方的基于tk的pywinusb hid调试工具
我认为官方教程中的操作有些复杂,可以做如下简化:
1. 官方例程中使用get_active_configuration(), usb.util.find_descriptor()
找设备描述符,我没有调试出来且繁杂,不如在 dev.set_configuration()
之后直接dev.write()
,前提是
已经知道设备描述符,这个可以通过一个简单的python脚本查询.
#!/usr/bin/python # 此代码仅供示例,未测试 # 原始链接 http://www.bubuko.com/infodetail-2281652.html import sys import usb.core # find USB devices dev = usb.core.find(find_all=True) # loop through devices, printing vendor and product ids in decimal and hex for cfg in dev: sys.stdout.write('Decimal VendorID=' + str(cfg.idVendor) + ' & ProductID=' + str(cfg.idProduct) + '\n') sys.stdout.write('Hexadecimal VendorID=' + hex(cfg.idVendor) + ' & ProductID=' + hex(cfg.idProduct) + '\n\n')
一个demo代码如下,本代码同时采用了windriver和pyusb的方式。 由于完整运行该代码需要dll库文件、FPGA下位机配合,所以本代码仅供示例,大概率无法复现。
#!/bin/bash import ctypes from ctypes import * # prepared to be called from the same class directory python scripts import sys import os import usb.core # now our hardware is vid = 0x03fd, pid = 0x0100) class hardware_usb(): def __init__(self, vid, pid, read_length = 512, backend='libusb'): ''' vid: vendor id pid: product id read_length : buffer length for reading backend: select one from ['libusb', 'windriver'] ''' self.read_length = read_length self.backend = backend if backend == 'libusb': usb_dev = usb.core.find(idVendor=vid, idProduct=pid) if usb_dev != None : usb_dev.set_configuration() self.read_addr = 0x82 self.write_addr = 0x03 self.dev = usb_dev self.init_status = True else: self.init_status = False elif backend == 'windriver': try: dll = ctypes.cdll.LoadLibrary( "msdev_dll.dll") except Exception as e : print("Error occurs when init usb, %s:%s" %(str(e.__class__), str(e.args))) self.init_status = False else: # input : void # output : int # assum 0 to be successful self.init_driver = getattr(dll, '?InitDriver@@YA_NXZ') # input : PVOID pBuffer, DWORD &dwSize # output: bool self._read = getattr(dll, '?Read_USB@@YA_NPAXAAK@Z') # input : PVOID pBuffer, DWORD dwSize # output: bool self._write = getattr(dll, '?Write_USB@@YA_NPAXK@Z') if 1 == self.init_driver(): self.init_status = True else: self.init_status = False def write(self, data): ''' Write data to usb Note that data must bytes type Return True if success ''' if self.backend == 'windriver': # BUG 此处使用c_char_p, 遇到\x00就被截断,可能有bug buffer = ctypes.c_char_p() buffer.value = data leng = ctypes.c_ulong(len(data)) try: status = self._write(buffer,leng) except Exception as e : print("Error occurs when write usb, %s:%s" %(str(e.__class__), str(e.args))) status = 0 if status == 1: ret = True else : ret = False elif self.backend == 'libusb': write_len = self.dev.write(self.write_addr, data) # Test if data is written if write_len == len(data): ret = True else: ret = False return ret def read(self): ''' Read data from usb Note we will return bytes like data Return None if error occurs ''' if self.backend == 'windriver': buffer_class = c_ubyte * 512 buffer = buffer_class() leng = ctypes.c_ulong() try : status = self._read(buffer, ctypes.byref(leng) ) except Exception as e : print("Error occurs when read from usb, %s:%s" %(str(e.__class__), str(e.args))) status = 0 if status == 1: ret = bytes(buffer[:leng.value]) else: ret = b'' elif self.backend == 'libusb': try: ret = self.dev.read(self.read_addr, self.read_length) ret = bytes(ret) except Exception as e: print("Error occurs when read from usb, %s:%s" %(str(e.__class__), str(e.args))) ret = b'' return ret if __name__ == "__main__": usb_ins = hardware_usb(vid=0x03fd, pid=0x0100) loop_num = 1 # 在测试中发现有一定概率写入出错 while True: print('============================') print('Loop num is', loop_num) print('test write function') data = b'\x7e\x7f\x00\x66' try: write_ret = usb_ins.write(data) read_ret = usb_ins.read() except: print('Error occurs, return') break print('write function returns', write_ret) print('test read function') print(' read function returns', read_ret) loop_num = loop_num + 1