使用python開發usb的兩種方式(windriver與pyusb)
- 2019 年 11 月 20 日
- 筆記
背景
最近在給一個FPGA板子做上位機介面,上位機與下位機的通訊採用USB方案,驅動採用WinDriver。
但在實際調試過程中,發現WinDriver不同版本之間兼容性差,並且在win10上表現不佳。實際的數據傳輸流程如下:
python <-> usb dll(through ctypes) <-> windriver <-> usb device
由於dll文件是在win7機器上編譯的,故僅能在win7上使用,在win10機器上出錯。
使用python的項目都應該是簡潔而優雅地,遂研究了在python操作usb device的兩種方式。
驅動無關的調試軟體使用bus hound
WinDriver
WinDriver經常與Jungo connectivity聯繫在一起,安裝了WinDriver驅動的usb device在設備管理器中也顯示為Jungo devices
。
完整的WinDriver開發流程應該從驅動開始,使用C/C++調用WinDriver提供的庫與usb device通訊,將此程式編譯為dll供其他程式調用。
將usb device連接上電腦,使用WinDriver給設備安裝驅動。
在python中使用ctypes調用上文中的dll,完成調用過程。
PyUsb
pyusb是一個python庫,可以方便地使用python操作usb設備。pyusb的數據傳輸流程如下:
python <-> pyusb <-> pyusb backend <-> usb device
很明顯可以看出省略了dll,大大減少了程式碼量。
具體使用過程:
缺點:
- windriver有一個可視化的調試工具,可以單獨發送接收數據以確定usb device是否正常,pyusb暫時沒有找到。但找到了一個非官方的基於tk的pywinusb hid調試工具
pyusb demo
我認為官方教程中的操作有些複雜,可以做如下簡化:
- 官方常式中使用
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) + 'nn')
一個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'x7ex7fx00x66' 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