如何与HID设备通讯(二)

三、读写HID设备的步骤

  读写HID设备步骤如下:

①、得到系统HID设备结构数组指针
②、对设备进行遍历
③、得到指定HID设备的句柄
④、readfile/writefile进行读写

  下面分别对各步骤及其所涉及的相关API函数进行介绍。

1、得到设备句柄:这步用到的两面个主要API函数原型为:

A、通过以下函数

 
  1. VOID  HidD_GetHidGuid(OUT LPGUID  HidGuid    );  

得到HID设备的GUID。

B、再通过以下函数

 
  1. HDEVINFO SetupDiGetClassDevs(   
  2.   CONST LPGUID ClassGuid,    
  3.   PCTSTR Enumerator,    
  4.   HWND hwndParent,    
  5.   DWORD Flags   
  6. );  

取得HID设备结构数组指针,以便下一步利用这个数组对所有HID设备进行遍列。

2、对设备进行遍历:遍历过程如下

A、首先执行以下函数:

 
  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);   

的第一个参数,以便取得这个函数的第三个参数DeviceInterfaceDetailData的填充值,然后利用这个值传递给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开发基础--USB命令(请求)和USB描述符》一文中表4),当然您也可以通过一些工具查询得到PID和VID,您可以到USB组织官方网站www.usb.org下载这类工具。

3、根据得到的设备句柄利用ReadFile和WriteFile对设备进行读写操作。

  百合电子工作室已经将以上步骤封装成了一个HID类(参考了其它一些实例代码),它能实现对指定PID和VID设备的查找,并实现了数据收发功能,同时具有设备拨插检测通知功能。点击这里下载

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

 四、读写HID设备实例

实例 1

1、找开Visual C++ 6.0,新建一基于MFC的工程名为:Easy USB 51 Programer Test1。

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:

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

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

 
  1. CHid m_MyHidDevice;  

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

9、在CEasyUSB51ProgramerTest1Dlg类的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、在CEasyUSB51ProgramerTest1Dlg的消息映射中(“BEGIN_MESSAGE_MAP(CEasyUSB51ProgramerTest1Dlg, CDialog)” 与 “END_MESSAGE_MAP()”之间)添加如下代码:

 
  1. ON_MESSAGE(WM_DEVICECHANGE, OnDeviceChange)  

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

 
  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. }  

完成后的实际效果:

下载程序

下载源代码

实例 2

  此例子需要用到扩展板:EXT-BOARD-A。实现功能为通过上位机设定 EXT-BOARD-A 上的8个发光二极管状态。

 1、命令及数据定义

  下位机已经规定了每帧数据的长度为64个字节,我们现在需要对每一个字节的含义作出定义。在这个实例中,我们可作如下规定:每帧数据前5个字节为命令,命令后面紧跟数据(从第6个字节开始)。命令为ASCII编码,在此实例中只有一个命令“ENLED”,代表设备LED状态。命令后面的一个字节为数据,代表D0~D7八个LED的状态,后面的字节无意义。

2、修改下位机程序

  修改main.c文件,其内容如下:

 
  1. /********************************Copyright (c)**************************************************  
  2. **                               百合电子工作室  
  3. **                                 
  4. **                                  
  5. **  
  6. **                            http://www.baiheee.com  
  7. **  
  8. ** 文   件   名: main.c  
  9. ** 最后修改日期: 2008年12月25日  
  10. ** 描        述: 用户应用程序  
  11. ** 版          本: V6.0  
  12. **********************************************************************************************************/  
  13.   
  14. //#include <at89x52.h>   
  15. #include <reg51.h>   
  16. #include "D12Config.h"   
  17. #include "Descriptor.h"   
  18. #include "Chap_9.h"   
  19. #include "D12Driver.h"   
  20. #include <string.h>   
  21.   
  22. main()   
  23. {      
  24.     unsigned char ucLedState;   
  25.        
  26.     if (Init_D12()!=0)                  //初始化D12   
  27.         return;                         //如果初始化不成功,返回   
  28.        
  29.     IT0 = 0;                            //外部中断0为电平触发方式   
  30.        
  31.     EX0 = 1;                            //开外部中断0   
  32.     PX0 = 0;                            //设置外部中断0中断优先级   
  33.     EA = 1;                             //开80C51总中断    
  34.        
  35.     while(1)   
  36.     {   
  37.         usbserve();                     //处理USB事件   
  38.         if(bEPPflags.bits.configuration)       
  39.         {       
  40.             //在这里添加端点操作代码     
  41.                
  42.             if(bEPPflags.bits.ep2_rxdone )  //主端点接收到数据(从主机发往设备的数据)       
  43.             {       
  44.                 bEPPflags.bits.ep2_rxdone       = 0;           
  45.                    
  46.                 //判断是否是 ENLED 命令(EpBuf的前5个字节为”ENLED“)            
  47.                 if(strncmp("ENLED",EpBuf,5)==0)   
  48.                 {   
  49.                     //取得LED状态设定值   
  50.                     ucLedState  = EpBuf[5];   
  51.                        
  52.                     //设定LED状态   
  53.                     P0  = ucLedState;   
  54.                 }                          
  55.             }        
  56.         }     
  57.     }   
  58. }   

 点击这里下载修改好的源代码

 3、上位机程序

  通过实例1的学习,其实上位机程序的编写非常简单,所以在这里只贴出源代码。

                                           
            

              上位机界面                                               LED的状态由上位机控制

 下载程序

 下载源代码

posted @ 2013-03-04 16:57  莫回头  阅读(1112)  评论(0)    收藏  举报