网站首页 文章专栏 使用python开发usb的两种方式(windriver与pyusb)
使用python开发usb的两种方式(windriver与pyusb)
创建于:2018-09-26 16:00:00 更新于:2024-03-19 09:40:10 羽瀚尘 7629
python python,usb

背景

最近在给一个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) + '\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