您的位置:首页 > 编程语言 > Qt开发

[Qt5.1]libusb-win32应用

2013-12-11 23:02 155 查看
平台:Windows7 64bit ultimate

环境:Qt5.1,libusb-win32-bin-1.2.6.0[http://sourceforge.net/projects/libusb-win32/files/]

参考资料:

1、《USB与QT编程》—— http://91r.net/ask/16450160.html
这个资料里给出了Win平台下两种USB的解决方案,我是不太会处理Win的东西,尤其还是与设备驱动有关。尤其觉得Unix/Linux下的东西好用一些,即使是移植过来到Win32平台上的。

2、《libusb-win32》—— http://sourceforge.net/apps/trac/libusb-win32/wiki
这是libusb-win32官方wiki页,主要的说明均在这里。

3、《Qt下的libusb-win32的使用方法》—— http://www.cnblogs.com/lknlfy/p/3189949.html
这个博客里主要给出了一些libusb的基本使用:安装(驱动安装),库链接等,然后是一个简单的程序应用,很适合入门。

4、《QT - USB-driver - libusb [转]》—— http://blog.sina.com.cn/s/blog_40b3edd70100n3pi.html
这个博客里主要是对库函数与详细讲解与应用,深入理解的不错文章。

5、《QT5文件管理for windows》—— http://hi.baidu.com/wuliuhong/item/84fe3ec9ee60d07ccfd4f8e3
这个博客是Qt5处理Window消息的一个实例,可作参考。

6、《2.31.2 使用NSIS(Nullsoft Install System)》—— http://book.51cto.com/art/201003/188297.htm
还有一些资料,零碎的查阅过,也无法一一列出来了。连同其上列出来,一同感谢一下,谢谢能有那么多资料可以参考,为我解决我自己的应用提供了很大的帮助。

下面来写正文了,目前我也还不是特别理解libusb的细节,能写出来的只是一种解决方案,在我实践中出现的问题的一种解决。

一. libusb-win32安装

下载libusb-win32-bin-1.2.6.0.zip,解压后进入其下的bin文件夹下,运行inf-wizard.exe程序。该程序主要是用来安装libusb-win32的驱动,以及一些相关库(*.dll,*.a)。官方说安装时建议先将多余的USB设备都移除,这样也避免了安装错设备的驱动。不过可以打开inf-wizard.exe程序,进入到设备列表界面,然后再插上想要安装驱动的USB设备,等待设备列表更新,就能看出自己的设备是哪一个了,如下图。列表若是无法自动刷新,可先上一步再下一步回到设备列表界面。如此,对刚开始折腾USB,不太明了的人,也不失为一个不错的方法,省得折腾了。呵呵~









记住一,最后要点击“Install Now...”进行驱动安装,依赖库回同时安装到系统中去,尤其是libusb0.dll动态库。

记住二,Vendor ID和Product ID, 这两个ID用来识别你的设备,程序中要用到的。

过程中会提示保存驱动程序,记住保存的位置,因为之后若要别的计算机上使用同一个设备,或是你为这个设备写好的程序,那么这个驱动是需要的。否则就必需再重新在其它计算机上用libusb-win32的inf-wizard.exe安装驱动。尤其是若涉及到要发布出去给他人使用的情况,这样子就更不适合了。发布的方法之后会讲到。

二. Qt关于libusb环境的配置

由于libusb-win32是第三方的东西,所以需要配置到Qt中去,才能够使用。

将解压的libusb-win32下的include中的libusb头文件lusb0_usb.h文件拷贝到Qt的include文件夹下。

我的Qt安装的是默认路径,C盘根目录下。Qt5.1自带QtCreator和mingW,mingW的include路径为:C:\Qt\Qt5.1.0\Tools\mingw48_32\i686-w64-mingw32\include。要确认你的环境上的这个路径哦!!

将解压的libusb-win32下的lib中的库文件拷贝到Qt工程下(自己写的Qt程序的工程目录),注意根据编译器的不同选择样应的库。我用的是Qt5.1自带的mingW-gcc编译器,所以拷贝的是lib/gcc下的libusb.a。

然后在Qt工程文件*.pro文件中添加如下内容:(参考自资料3)

LIBS += “D:/QtProject/USBConfig/libusb.a” —— 当然,路径是要和你的工程同步

这里没能找到怎么将libusb.a加载到Qt的环境里,因为没有找到应该放置的位置,如有知道的,解决了的,请一定告知我哦!谢谢了~

三. libusb-win32接口函数在Qt中的应用

接口函数就不介绍了,详细的资料很多,用法也很多,一看就清楚的。这里就写一些我自己写的代码,我也不太会C++,所以很多东西处理的不好,勿见怪了,呵呵~

1、构造函数:

UsbListener::UsbListener(QObject *parent) :
    QThread(parent)
{
    usb_init(); /* initialize the library */
}

2、查找设备函数:——这个函数用来查找自己的设备,之后在监听系统是否有设备已插入/拔出计算机时有用

struct usb_device * UsbListener::findUSBDev(const unsigned short idVendor,
                                            const unsigned short idProduct)
{
    struct usb_bus *bus;
    struct usb_device *dev;

    usb_find_busses(); /* find all busses */
    usb_find_devices(); /* find all connected devices */

    for(bus = usb_get_busses(); bus; bus = bus->next)
    {
        for(dev = bus->devices; dev; dev = dev->next)
        {
            if((dev->descriptor.idVendor == idVendor)
            && (dev->descriptor.idProduct == idProduct))
            {
                return dev;
            }
        }
    }
    return NULL;
}
3、打开函数:

bool UsbListener::openUSB(struct usb_device *dev)
{
    devOpenFlg = false;

    devHandle = usb_open(dev);
    if(!devHandle)
    {
        qDebug() << "error opening device: ";
        qDebug() << usb_strerror();
        return false;
    }
    else
    {
        qDebug() << "success: device " << MY_VID << " : "<<  MY_PID << " opened";
        devOpenFlg = true;
    }

    if (usb_set_configuration(devHandle, MY_CONFIG) < 0)
    {
        qDebug() << "error setting config #" << MY_CONFIG << " : " << usb_strerror();
        usb_close(devHandle);
        return false;
    }
    else
    {
        qDebug() << "success: set configuration #" << MY_CONFIG;
    }

    if (usb_claim_interface(devHandle, 0) < 0)
    {
        qDebug() << "error claiming interface #" << MY_INTF;
        qDebug() << usb_strerror();
        usb_close(devHandle);
        return false;
    }
    else
    {
        qDebug() <<  "success: claim_interface #" << MY_INTF;
    }

    return true;
}
4、关闭函数:——注意要停止线程,否则一但关闭了USB设备,而线程在继续读取数据,这样是错误的。

void UsbListener::closeUSB()
{
    if(devHandle)
    {
        devOpenFlg = false;
        UsbListener::quit();
        int ret = usb_close(devHandle); // Exit Thread
        qDebug() << "Close USB Device [" << ret << "]";
    }
}
发送数据函数:——此处用的是usb_bulk_write()方式向串口发送数据,其后的读函数同样也是用usb_bulk_read()方式接收数据。

bool UsbListener::sendData(QByteArray &data)
{
    if(true == devOpenFlg)
    {
        int ret = 0;
        char *tmp = data.data();

        ret = usb_bulk_write(devHandle, EP_OUT, tmp, data.length(), 5000);
        if (ret < 0)
        {
            qDebug() << "error writing:";
            qDebug() << usb_strerror();

            return false;
        }
        else
        {
            qDebug() << "success: bulk write " << ret <<  " bytes";
            return true;
        }
    }

    return false;
}
5、接收函数:——这里主要是用线程来监听接收数据,注意要将managerData()函数换为你自己的数据处理函数。

void UsbListener::run()
{
    char tmp[BUF_SIZE];
    int ret;

    if(false == devOpenFlg)
    {
        return;
    }

    // Running a sync read test
    while(1)
    {
        ret = usb_bulk_read(devHandle, EP_IN, tmp, sizeof(tmp), 5000);
        if(ret > 0)
        {
            qDebug() << "success: bulk read " << ret << " bytes";
            QString strTmp;
            QString strOut;
            for(int i = 0; i < ret; i++)
            {
                strTmp.sprintf("%02X ", (unsigned char)tmp[i]);
                strOut.append(strTmp);
            }
            qDebug() << strOut;
            managerData(tmp, ret); // Receive Data Proccess Function
        }
    }
}


四. Qt5.1下实现USB设备插入/拔出监听

要实现此功能,首先要做的是让Qt能够监听到Windows传来的消息,可以参考资料5的内容。具体的Windows的东西,我也不是特别清楚,所以就不细说了。还是那句话,我只是在说一种解决方案。

在主窗口下重载函数:
nativeEvent(
)




//// mainwindow.h
protected:
    bool nativeEvent(const QByteArray &eventType, void *message, long *result);

//// mainwindow.cpp
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG *msg = reinterpret_cast<MSG *>(message);
    int msgType = msg->message;
    if(msgType == WM_DEVICECHANGE)
    {
        switch(msg->wParam)
        {
        case DBT_DEVICEARRIVAL:
            /* break; */
        case DBT_DEVICEREMOVECOMPLETE:
            /* break; */
        case DBT_DEVNODES_CHANGED:
        {
            dev = usbListener->findUSBDev(USB_VID, USB_PID);
            if(NULL != dev)
            {
                lStatusInfo->setText(tr("设备已插入!"));
            }
            else
            {
                ui->stackedWidget->setCurrentIndex(0);
                lStatusInfo->setText(tr("设备已移除!"));
            }
        }
            break;
        default:
            break;
        }
    }
    return false;
}


这里有几个点需要注意:

1、查到过很多资料,都是讲的用winEvent()函数来处理,但Qt5.1已经没有此函数了
,参见http://stackoverflow.com/questions/14048565/get-hwnd-on-windows-with-qt5-from-wid中的第5条。另外,此处我用的是QMainWindow基类,其它情况还是多留心处理。

2、在我测试的过程中,libusb-win32设备安装后,会在设备管理器中多一个libusb-win32 devices设备,如下图。



而在插入/拔出该设备时,收到
WM_DEVICECHANGE(0x219)消息时,wParam参数内容只有DBT_DEVNODES_CHANGED(0x7),而测试其它的设备,如U盘,USB转串口线,USB烧录器,都能正常收到DBT_DEVICEARRIVAL和DBT_DEVICEREMOVECOMPLETE两个设备接入和移除的消息。这个应该是驱动的问题,因为用的是libusb-win32的驱动,所以不有原原本本的用Windows的监听流程(到底是不是如此,我也不清楚,呵呵~)。故此处在收到DBT_DEVNODES_CHANGED消息后,通过libusb对总线上的设备查询来实现该功能,这就是为什么要用到findUSBDev()的原因了。

3、在findUSBDev()函数中,需要在每次查找设备时,都调用usb_find_busses()和usb_find_devices()来更新总线上的设备信息。这样才能正常的查询到设备是连接在计算机上,还是未连接。否则设备的状态会出现错误。

五. 发布或在其它未安装libusb环境的计算机上使用

这里的一个最大的问题就是驱动的问题,因为换一台没有安装过libusb驱动的计算机,接上设备后肯定无法使用我们辛辛苦苦编写的程序。所以我经过了一些资料查询与整理,还是解决了这个问题。主要思路如下:

静态编译Qt程序,减少一些对库依赖的问题;

用NSIS进行Qt程序和驱动打包;

发布给用户后,用户连接设备安装驱动时,要求其到指定的安装目录下的Driver目录下找驱动。若是同一款产品,换一台连接这台计算机,驱动会自动安装好。

1、静态编译

这个部分就不说了,网上有不少这个内容的帖子。配置这个环境还是挺麻烦的,我用台式机i5+4G内存,都编译了3个多小时才编译完静态库,不过还是有好处的,呵呵~

参考:http://www.qtcn.org/bbs/read-htm-tid-54355.html

我基本一次就能编译成功,没有遇上什么问题,所以也没办法提供太多的帮助了。

2、NSIS打包

参考资料6里有这个方面的使用说明。

我用的是NSIS3.0。因为NSIS是用脚本配置的方式来处理的,所以还是不太方便使用。不过也不难,对看参考资料,还有NSIS的帮助说明,基本没有大问题。使用上就不细说了,大致职下:

A. 文件准备:

USBConfig.exe —— 换成你自己写的程序

libusb0.dll —— 这个未包含在静态编译库中,属于第三方的东西,还是要拿过来,不然无法使用

License.txt —— 这个是必需要的文件,去找一个吧!我也不知道里边要什么样的内容

win.bmp —— 这个也是必需要的文件,SNIN安装目录下\Contrib\Graphics\Header目录里的win.bmp不错,关键是尺寸(150x57)合适。

Driver —— 这个目录当然就是安装libusb-win32驱动时保存下来的驱动内容,我曾经是想挑出几个必需的文件就行了,后来发现还是都保留的好。因为里面有好几种平台都支持的驱动,若只保留了自己的平台驱动(如win7 64bit),换一个平台后,原来libusb-win32是支持的,但是驱动文件夹下没有,这样就不好了。而且都保留,打包后的程序也没有多大。

HeaderBitmap.nsi —— 这个文件是从NSIS安装目录下拷贝出来的模板,路经为..\NSIS\Examples\Modern UI\下,当然要和你自己的环境相对应。

还有一些其它文件,例如帮助文档,我这没有,所就没有用了。对于驱动的安装,然后帮助文档还是不错的选择。

B. 修改HeaderBitmap.nis模板内容:

这个是关键的一步,控制着打包的内容,生成安装程序后的安装以及卸载过程等内容。这里用到的并不全面,全面的东西可以参考NSIS的帮助文档。

HeaderBitmap.nis可直接用记事本之类的工具进行编辑,但对中文的支持好像会有问题,我尝试过,但是中文要么是乱码,要么就不显示,还是用英文吧!也懒得去看英文帮助了,呵呵~ 若是你找到了这个问题的解决办法,别忘了告诉我哦!

;NSIS Modern User Interface
;Header Bitmap Example Script
;Written by Joost Verburg

;--------------------------------
;Include Modern UI

  !include "MUI2.nsh"

;--------------------------------
;General

  ;Name and file
  Name "USBConfig"    ---- 程序名
  OutFile "Setup.exe"    ---- 生成的目标文件名

  ;Default installation folder
  InstallDir "$PROGRAMFILE\USBConfig"
 ---- 安装的目标文件夹名称,这里要将前缀改成$ProgramFiles,不然会装到其它路经,不用担心64bit平台上目标目录的问题
 ---- $PROGRAMFILE是NSIS的环境变量,会自动映射到C:\Program Files或64平台的C:\Program Files (x86)目录下

  ;Get installation folder from registry if available
  InstallDirRegKey HKCU "Software\USBConfig" ""    ---- 这里是安装注册表的目录,修改对应名称,若是不想要安装注册表内容,则注释掉这行,还以后对的应该删除行

  ;Request application privileges for Windows Vista
  RequestExecutionLevel user

;--------------------------------
;Interface Configuration

  !define MUI_HEADERIMAGE
  !define MUI_HEADERIMAGE_BITMAP "win.bmp" ; optional    ---- 这里就用到了图片文件,是在生成的Setup.exe文件里的图标
  !define MUI_ABORTWARNING

;--------------------------------
;Pages

  !insertmacro MUI_PAGE_LICENSE "License.txt"    ---- 这里就用到了License文件,我试过不用,但好像不行
  !insertmacro MUI_PAGE_COMPONENTS
  !insertmacro MUI_PAGE_DIRECTORY
  !insertmacro MUI_PAGE_INSTFILES

  !insertmacro MUI_UNPAGE_CONFIRM
  !insertmacro MUI_UNPAGE_INSTFILES

;--------------------------------
;Languages

  !insertmacro MUI_LANGUAGE "SimpChinese"    ---- 语言版本,没太研究,好像是控制安装程序安装过程中的显示语言为中文

;--------------------------------
;Installer Sections

Section "Application and Help(required)" SecDummy    ---- 安装的第一部分内容,用Section来划分,这里的内容是程序的内容

  SetOutPath "$INSTDIR"

  ;ADD YOUR OWN FILES HERE...
  File "USBConfig.exe"    ---- 添加要打包进去的文件,这里没有打包win.bmp,若是打包进去,安装后在安装目录下也会有win.bmp文件,没有需要
  File "License.txt"
  File "libusb0.dll"
  File /r "Driver"    ---- 添加文件夹的方法,需要参数/r

  ;Store installation folder
  WriteRegStr HKCU "Software\USBConfig" "" $INSTDIR    ---- 这里是控制安装注册表,不需要则注释掉

  ;Create uninstaller
  WriteUninstaller "$INSTDIR\Uninstall.exe"     ---- 这里是生成卸载程序

SectionEnd

;--------------------------------
;Descriptions

  ;Language strings
  LangString DESC_SecDummy ${LANG_ENGLISH} "Application and Help(required)"    ---- 这里是语言配置,没有太研究

  ;Assign language strings to sections
  !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
    !insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy)
  !insertmacro MUI_FUNCTION_DESCRIPTION_END

;--------------------------------
; Optional section (can be disabled by the user)
Section "StartMenu ShortCuts"    ---- 安装的第二部分,同样用Section来划分,这里的内容是添加“开始菜单”的菜单项

  CreateDirectory "$SMPROGRAMS\USBConfig"    ---- 开始菜单中安装的目录
  CreateShortCut "$SMPROGRAMS\USBConfig\USBConfig.lnk" "$INSTDIR\USBConfig.exe" "" "$INSTDIR\USBConfig.exe" 0    ---- 目录中的快捷选项
  CreateShortCut "$SMPROGRAMS\USBConfig\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0

SectionEnd

;--------------------------------
; Optional section (can be disabled by the user)
Section "Desktop ShortCuts"    ---- 安装的第三部分,同样用Section来划分,这里的内容是添加“桌面”的快捷方式

  CreateShortCut "$DESKTOP\USBConfig\USBConfig.lnk" "$INSTDIR\USBConfig.exe" "" "$INSTDIR\USBConfig.exe" 0
 ---- DESKTOP为NSIS的环境变量,会映射到系统桌面
SectionEnd

;--------------------------------
;Uninstaller Section
Section "Uninstall" ---- 这里是控制卸载程序工作的内容

 ;ADD YOUR OWN FILES HERE...
 Delete "$INSTDIR\Uninstall.exe" ---- 卸载时应该删除的文件
 Delete "$INSTDIR\USBConfig.exe"
 Delete "$INSTDIR\License.txt"
 Delete "$INSTDIR\libusb0.dll"

 RMDir /r "$INSTDIR\Driver" ---- 卸载时应该删除的目录, /r参数是强制删除,不管其目录下是否有文件存在
 RMDir "$INSTDIR" Delete "$SMPROGRAMS\USBConfig\USBConfig.lnk" ---- 卸载时删除开始菜单中的快捷选项

 Delete "$SMPROGRAMS\USBConfig\Uninstall.lnk"
Delele "$DESKTOP\USBConfig.lnk" ---- 卸载时删除桌面快捷方式

 RMDir "$SMPROGRAMS\USBConfig" ---- 卸载时删除开始菜单中的目录

 DeleteRegKey /ifempty HKCU "Software\USBConfig" ---- 卸载时删除安装时安装的注册表内容,不需要时注释掉
SectionEnd


参考上面的注释说明,修改完成后即可使用NSIS软件进行打包。

打开NSIS,在主界面上选择"Compile NSI scripts",打开makeNSISW工具->选择"Open"->选择修改过的HeaderBitmap.nsi文件->makeNSISW工具自动进行打包,并根据配置文件的内容,在同文件夹下生成对应的安装程序,我的配置是生成Setup.exe,故生成的也就是该文件。

顺便提一句,若是配置文件错了,makeNSISW工具还是能检查出来,可以看工具的打印输出来修改调试脚本。参见如下三张图:







C、剩下的就是找另外一台没有libusb-win32环境的计算机去测试了,应该没问题的。

剩下的,就是祝你好运咿呀!哈哈~

P.S. 水平有限,高手就勿见怪了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: