这节讲解如何编写Windows应用程序与USB HID设备通讯(读写HID),下面的实例以Visual C++ 6.0为实例,在本站推出的USB学习板中还包括VB,Delphi实例。
一、编写HID通讯程序的一般步骤
①、得到系统HID设备结构数组指针
②、对所有USB设备进行遍历
③、得到指定HID设备的句柄
④、readfile/writefile进行读写
上面各步骤需要用Windwos API和Windows DDK相关 API 来实现,以下就各步骤用到的主要API函数进行说明:
1、得到系统HID设备结构数组指针需要用到HidD_GetHidGuid和SetupDiGetClassDevs函数,先用HidD_GetHidGuid函数得到HID设备的GUID,GUID用来标识接口、设备甚至文档记录,详细信息可以参考http://baike.baidu.com/view/185358.htm。HidD_GetHidGuid的函数原型如下:
- VOID HidD_GetHidGuid(OUT LPGUID HidGuid );
得到HID设备的GUID后,再把它作为SetupDiGetClassDevs函数的一个参数来取得对应HID设备结构数组指针,SetupDiGetClassDevs的函数原型如下:
- WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfaces(
- IN HDEVINFO DeviceInfoSet,
- IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
- IN LPGUID InterfaceClassGuid,
- IN DWORD MemberIndex,
- OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
2、利用第一步得到的HID设备结构数组指针对所有HID设备进行遍列,找到我们所要找的设备。
1)首先利用以下函数:
- WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfaces(
- IN HDEVINFO DeviceInfoSet,
- IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
- IN LPGUID InterfaceClassGuid,
- IN DWORD MemberIndex,
- OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
运行此函数的主要目的是取得第一个参数DeviceInfoSet的填充值,又将此值作为以下函数
- BOOL SetupDiGetDeviceInterfaceDetail(
- HDEVINFO DeviceInfoSet,
- PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
- PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,
- DWORD DeviceInterfaceDetailDataSize,
- PDWORD RequiredSize,
- PSP_DEVINFO_DATA DeviceInfoData);
的第一个参数,以便取得这个函数的第三个参数eviceInterfaceDetailData的填充值,然后利用这个值传递给CreateFile函数,此时CreateFile会返回一个指向HID设备的句柄,再根据以下函数
- BOOLEAN HidD_GetAttributes(
- IN HANDLE HidDeviceObject,
- OUT PHIDD_ATTRIBUTES Attributes
- );
取得此HID设备的属性(第二个参数的填充值),然后判断属性里的PID(Attributes->ProductID)和VID(Attributes->VendorID)是否是我们要查找的设备的PID和VID。PID和VID在设备固件代码的设备描述符里提供(设备描述里的idProduct域和idVendor,参考《USB命令请求及描述符详解(速查手册)》一文中表4),当然您也可以通过一些工具查询得到PID和VID,您可以到USB组织官方网站www.usb.org下载这类工具。
3、根据得到的设备句柄利用ReadFile和WriteFile对设备进行读写操作。
本工作室已经将以上步骤封装成了一个HID类,它能实现对指定PID和VID设备的查找,并实现了数据收发功能,同时具有设备拨插检测通知功能。
二、下面用实例说明如何使用这个类。
实例 1- 以上一节HID设备固件为基础,编写一个应用程序,可以向设备发送字符串,设备收到后立即将收到的字符串又发回来,应用程序能接收并显示回传的字符串。
1、找开Visual C++ 6.0,新建一基于MFC的工程名为:RWHid。
2、MFC AppWizard Step 1对话框中选择基于对话框的应用程序,然后点“Finish”按钮,如图所示:
3、创建3个静态文本标签(Static Text),文本内容分别为:Write、Read和Message;创建两个文本框和一个列表框,ID分别为:IDC_EDIT_TX、IDC_EDIT_RX和IDC_LIST_MESSAGE;两个按钮ID和文本分别为:IDC_BTN_WRITE(Write)和IDCANCEL(Exit)。界面如下:
4、添加控件所对应的变量,如下图所示:
5、将Hid.c和Hid.h导入工程,并将“要用到的windows ddk里的几个文件”文件夹内的文件复制到工程所在目录,在Procect->Settings->Link页的“Object/Library moudles”设置中添加“hid.lib setupapi.lib”,如下图所示:
6、在stdafx.h文件中包含头文件语句前添加:#define WINVER 0x0500
7、修改Hid.c中的PID和VID宏定义来设置需要访问的HID设备,此处的PID和VID值分别为0x0666和0x0471:
- #define VID 0x0471
- #define PID 0x0666
8、在CRWHidDlg类中添加成员变量m_MyHidDevice,其定义如下
当然您得在RWHidDlg.h里包含头文件Hid.h。
9、在CRWHidDlg类的OnInitDialog函数中添加如下语句:
- m_MyHidDevice.m_hParentWnd = (HANDLE*) this->GetSafeHwnd( );
- if(m_MyHidDevice.FindHid())
- {
- m_ctrlMessage.InsertString(-1,"My hid device detected");
- }
- else
- {
- m_ctrlMessage.InsertString(-1,"My hid device not detected");
- m_ctrlWrite.EnableWindow(FALSE);
- }
10、在CRWHidDlg类的消息映射中(“BEGIN_MESSAGE_MAP(CRWHidDlg, CDialog)” 与 “END_MESSAGE_MAP()”之间)添加如下代码:
- ON_MESSAGE(WM_DEVICECHANGE, OnDeviceChange)
11、在CRWHidDlg类中添加成员函数:
- LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam);
12、成员函数OnDeviceChange的结构如下:
- LRESULT CEasyUSB51ProgramerTest1Dlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)
- {
-
-
- switch(LOWORD(wParam))
- {
-
-
- case DBT_DEVICEARRIVAL:
- {
-
- if(m_MyHidDevice.FindHid())
- {
-
-
- }
- break;
- }
-
-
- case DBT_DEVICEREMOVECOMPLETE:
- {
- if(!m_MyHidDevice.FindHid())
- {
-
-
- }
- break;
- }
- }
-
- return true;
- }
13、这里为了实现在Message信息框里显示HID设备的拨插操作,现对OnDeviceChange函数作如下填充:
- LRESULT CEasyUSB51ProgramerTest1Dlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)
- {
-
-
- switch(LOWORD(wParam))
- {
-
-
- case DBT_DEVICEARRIVAL:
- {
-
- if(m_MyHidDevice.FindHid())
- {
-
-
- unsigned short nIndex = m_ctrlMessage.InsertString(-1,"My hid device detected");
- m_ctrlMessage.SetCurSel(nIndex);
- m_ctrlWrite.EnableWindow(TRUE);
- }
- break;
- }
-
-
- case DBT_DEVICEREMOVECOMPLETE:
- {
- if(!m_MyHidDevice.FindHid())
- {
-
-
- unsigned short nIndex = m_ctrlMessage.InsertString(-1,"My hid device removed");
- m_ctrlMessage.SetCurSel(nIndex);
- m_ctrlWrite.EnableWindow(FALSE);
- }
- break;
- }
- }
-
- return true;
- }
14、对HID的读写可通过Hid类的成员函数WriteHid和ReadHid。以下是"write"按钮的响应函数,实现对HID设备的读写操作:
- void CEasyUSB51ProgramerTest1Dlg::OnBtnWrite()
- {
- unsigned char ucTxBuffer[64];
- unsigned char ucRxBuffer[64];
-
- UpdateData(TRUE);
-
-
- if(m_strTx.GetLength()>64)
- {
- AfxMessageBox("发送字节数不能超过64个字节");
- }
-
-
- for(int i=0; i<64 ; i++)
- {
- if(i <= (m_strTx.GetLength()-1) )
- ucTxBuffer[i] = m_strTx.GetAt(i);
- else
- ucTxBuffer[i] = 0;
- }
-
-
- m_MyHidDevice.WriteHid(ucTxBuffer,64);
-
- m_MyHidDevice.ReadHid(ucRxBuffer,64);
-
- m_strRx = ucRxBuffer;
- UpdateData(FALSE);
- }
完成后的实际效果:
设备固件程序就是使用的上一节实现的例子。
设备固件源码
|