您现在的位置: USB开发网 > USB开源项目 > Easy USB 51 Programer Plus
- Easy USB 51 Programer Plus

自定义USB HID设备—PC主机应用程序开发

------分隔线----------------------------

  这节讲解如何编写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的函数原型如下:

  1. VOID  HidD_GetHidGuid(OUT LPGUID  HidGuid    );    

得到HID设备的GUID后,再把它作为SetupDiGetClassDevs函数的一个参数来取得对应HID设备结构数组指针,SetupDiGetClassDevs的函数原型如下:

  1. WINSETUPAPI BOOL WINAPI  SetupDiEnumDeviceInterfaces(      
  2.     IN HDEVINFO  DeviceInfoSet,      
  3.     IN PSP_DEVINFO_DATA  DeviceInfoData  OPTIONAL,      
  4.     IN LPGUID  InterfaceClassGuid,      
  5.     IN DWORD  MemberIndex,      
  6.     OUT PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData);    

 2、利用第一步得到的HID设备结构数组指针对所有HID设备进行遍列,找到我们所要找的设备。

1)首先利用以下函数:

  1. WINSETUPAPI BOOL WINAPI  SetupDiEnumDeviceInterfaces(     
  2.     IN HDEVINFO  DeviceInfoSet,     
  3.     IN PSP_DEVINFO_DATA  DeviceInfoData  OPTIONAL,     
  4.     IN LPGUID  InterfaceClassGuid,     
  5.     IN DWORD  MemberIndex,     
  6.     OUT PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData);    

 

运行此函数的主要目的是取得第一个参数DeviceInfoSet的填充值,又将此值作为以下函数

  1. BOOL SetupDiGetDeviceInterfaceDetail(     
  2.     HDEVINFO DeviceInfoSet,      
  3.     PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,      
  4.     PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,      
  5.     DWORD DeviceInterfaceDetailDataSize,      
  6.     PDWORD RequiredSize,      
  7.     PSP_DEVINFO_DATA DeviceInfoData);   

 

的第一个参数,以便取得这个函数的第三个参数eviceInterfaceDetailData的填充值,然后利用这个值传递给CreateFile函数,此时CreateFile会返回一个指向HID设备的句柄,再根据以下函数

  1. BOOLEAN  HidD_GetAttributes(     
  2.     IN HANDLE  HidDeviceObject,     
  3.     OUT PHIDD_ATTRIBUTES  Attributes     
  4.     );  

取得此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设备的查找,并实现了数据收发功能,同时具有设备拨插检测通知功能。

HID设备通信类 for VC

二、下面用实例说明如何使用这个类。

实例 1- 以上一节HID设备固件为基础,编写一个应用程序,可以向设备发送字符串,设备收到后立即将收到的字符串又发回来,应用程序能接收并显示回传的字符串。

1、找开Visual C++ 6.0,新建一基于MFC的工程名为:RWHid。

 
 
2、MFC AppWizard Step 1对话框中选择基于对话框的应用程序,然后点“Finish”按钮,如图所示:
 
usb_hid_class_demo_step2.png
 
3、创建3个静态文本标签(Static Text),文本内容分别为:Write、Read和Message;创建两个文本框和一个列表框,ID分别为:IDC_EDIT_TX、IDC_EDIT_RX和IDC_LIST_MESSAGE;两个按钮ID和文本分别为:IDC_BTN_WRITE(Write)和IDCANCEL(Exit)。界面如下:
usb_hid_class_demo_step3.png

 4、添加控件所对应的变量,如下图所示:

usb_hid_class_demo_step4.png

 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:

  1. #define VID 0x0471     
  2. #define PID 0x0666    

 8、在CRWHidDlg类中添加成员变量m_MyHidDevice,其定义如下

  1. CHid m_MyHidDevice; 

当然您得在RWHidDlg.h里包含头文件Hid.h。

 

9、在CRWHidDlg类的OnInitDialog函数中添加如下语句:

  1. m_MyHidDevice.m_hParentWnd = (HANDLE*) this->GetSafeHwnd( );     
  2. if(m_MyHidDevice.FindHid())     //找到指定HID设备     
  3. {     
  4.     m_ctrlMessage.InsertString(-1,"My hid device detected");     
  5. }     
  6. else                            //没有找到指定HID设备     
  7. {     
  8.     m_ctrlMessage.InsertString(-1,"My hid device not detected");     
  9.     m_ctrlWrite.EnableWindow(FALSE);    //禁用"write"按钮     
  10. }  

 10、在CRWHidDlg类的消息映射中(“BEGIN_MESSAGE_MAP(CRWHidDlg, CDialog)” 与 “END_MESSAGE_MAP()”之间)添加如下代码:

  1. ON_MESSAGE(WM_DEVICECHANGE, OnDeviceChange)    

11、在CRWHidDlg类中添加成员函数:

  1. LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam);    

12、成员函数OnDeviceChange的结构如下:

  1. LRESULT CEasyUSB51ProgramerTest1Dlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)     
  2. {     
  3.          
  4.     /* take the appropriate action for the message */    
  5.     switch(LOWORD(wParam))      
  6.     {     
  7.              
  8.         /* HID device arrival */    
  9.         case DBT_DEVICEARRIVAL:     
  10.         {     
  11.             /* try to open a handle to the device */    
  12.             if(m_MyHidDevice.FindHid()) /*此处必须调用FindHid()判断是否是“ 我”的HID设备插入USB口*/    
  13.             {     
  14.                 /*检测到指定的HID设备插入USB口*/    
  15.                 /*您在这里可以添加其它功能代码*/                   
  16.             }     
  17.             break;     
  18.         }     
  19.     
  20.         /* HID device removal */    
  21.         case DBT_DEVICEREMOVECOMPLETE:     
  22.         {     
  23.             if(!m_MyHidDevice.FindHid())    /*此处必须调用FindHid()判断是否是“ 我”的HID设备拨出USB口*/    
  24.             {     
  25.                 /*检测到指定的HID设备拨出USB口*/    
  26.                 /*您在这里可以添加其它功能代码*/                   
  27.             }     
  28.             break;     
  29.         }     
  30.     }     
  31.     
  32.     return true;     
  33. }    

 

13、这里为了实现在Message信息框里显示HID设备的拨插操作,现对OnDeviceChange函数作如下填充:

  1. LRESULT CEasyUSB51ProgramerTest1Dlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)     
  2. {     
  3.          
  4.     /* take the appropriate action for the message */    
  5.     switch(LOWORD(wParam))      
  6.     {     
  7.              
  8.         /* HID device arrival */    
  9.         case DBT_DEVICEARRIVAL:     
  10.         {     
  11.             /* try to open a handle to the device */    
  12.             if(m_MyHidDevice.FindHid()) /*此处必须调用FindHid()判断是否是“ 我”的HID设备插入USB口*/    
  13.             {     
  14.                 /*检测到指定的HID设备插入USB口*/    
  15.                 /*您在这里可以添加其它功能代码*/    
  16.                 unsigned short nIndex   = m_ctrlMessage.InsertString(-1,"My hid device detected");     
  17.                 m_ctrlMessage.SetCurSel(nIndex);    //流动信息窗口     
  18.                 m_ctrlWrite.EnableWindow(TRUE);     //启用"write"按钮     
  19.             }     
  20.             break;     
  21.         }     
  22.     
  23.         /* HID device removal */    
  24.         case DBT_DEVICEREMOVECOMPLETE:     
  25.         {     
  26.             if(!m_MyHidDevice.FindHid())    /*此处必须调用FindHid()判断是否是“ 我”的HID设备拨出USB口*/    
  27.             {     
  28.                 /*检测到指定的HID设备拨出USB口*/    
  29.                 /*您在这里可以添加其它功能代码*/    
  30.                 unsigned short nIndex   = m_ctrlMessage.InsertString(-1,"My hid device removed");     
  31.                 m_ctrlMessage.SetCurSel(nIndex);    //流动信息窗口     
  32.                 m_ctrlWrite.EnableWindow(FALSE);    //禁用"write"按钮     
  33.             }     
  34.             break;     
  35.         }     
  36.     }     
  37.     
  38.     return true;     
  39. }    

14、对HID的读写可通过Hid类的成员函数WriteHid和ReadHid。以下是"write"按钮的响应函数,实现对HID设备的读写操作:

  1. void CEasyUSB51ProgramerTest1Dlg::OnBtnWrite()      
  2. {     
  3.     unsigned char ucTxBuffer[64];   //发送缓冲     
  4.     unsigned char ucRxBuffer[64];   //接收缓冲     
  5.     
  6.     UpdateData(TRUE);     
  7.     
  8.     //判断发送框中内容是否超过64字节     
  9.     if(m_strTx.GetLength()>64)     
  10.     {     
  11.         AfxMessageBox("发送字节数不能超过64个字节");     
  12.     }     
  13.     
  14.     //准备发送缓冲区中的内容     
  15.     for(int i=0; i<64 ; i++)     
  16.     {     
  17.         if(i <= (m_strTx.GetLength()-1) )     
  18.             ucTxBuffer[i]   = m_strTx.GetAt(i);     
  19.         else    
  20.             ucTxBuffer[i]   = 0;     
  21.     }     
  22.     
  23.     //写操作     
  24.     m_MyHidDevice.WriteHid(ucTxBuffer,64);     
  25.     //读操作     
  26.     m_MyHidDevice.ReadHid(ucRxBuffer,64);     
  27.     
  28.     m_strRx     = ucRxBuffer;     
  29.     UpdateData(FALSE);     
  30. }    

完成后的实际效果:

usb_hid_class_demo_step6.png

 

PC主机程序

PC主机源码(VC)

设备固件程序就是使用的上一节实现的例子。

  设备固件源码

 

------分隔线----------------------------
联系我们
  • Q Q: 1148374829 点击这里给我发消息
  • 旺旺:jhoneqhsieh 点击这里给我发消息
  • 电话:(0)15923141204
  • 淘宝网店