使用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,大大減少了程式碼量。

具體使用過程:

  1. 下載並安裝pyusb backend
  2. 連接usb device,使用pyusb backend安裝驅動,我選擇libusb,一般可以正常使用。不行就換其他的。
  3. 編寫python腳本,可以參考官方教程

缺點:

  1. windriver有一個可視化的調試工具,可以單獨發送接收數據以確定usb device是否正常,pyusb暫時沒有找到。但找到了一個非官方的基於tk的pywinusb hid調試工具

pyusb demo

我認為官方教程中的操作有些複雜,可以做如下簡化:

  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) + '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