USB+Hid

 在应用程序一个文档内引用头文件如下,并将hidsdi,hidpi.h,hidusage.h,hid.lib,setupapi.lib拷贝进工程文件夹下,接下来调用API函数,完成需求。

extern "C" {  
// Declare the C libraries used  
#include "hidsdi.h"            // Must link in hid.lib  
#include <setupapi.h>          // Must link in setupapi.lib  
}  

 本文介绍Visual C++6.0环境下利用Windows API函数实现与HID设备类的USB接口通信,并给出了通信程序的部分代码

Windows下,与USB外设的任何通信需通过设备驱动,该驱动知道如何与系统的USB驱动和访问设备的应用程序通信,Windows

包含应用程序与HID通信所需要的各种信息,不需要再安装设备驱动。Win32的应用程序接口API函数,使得设备驱动能与应用程序之间相互通信,应用程序也不需要为了和USB设备通信去了解复杂的USB协议。

        下面用Visual C++编写应用程序调用API函数,从而简化了与硬件通信的过程。

1、查找USB设备

在应用程序能与HID交换数据之前,它先要找到设备,获取关于它的报告信息。

1)HidD_GetHidGuid(&guidHID);来获得HID设备的标识,HID类设备是通过GUID类型值作标识的。GUID是16字节大小的结构,用来标识通信接口及类对象,它的定义为:

typedef struct _GUID   // size is 16  
{            
    DWORD Data1;  
    WORD  Data2;  
    WORD  Data3;  
    BYTE  Data4[8];  
} GUID; 

2)还调用了其他与硬件相关的API函数,这些函数都在setupapi.h中定义。

SetupDiGetClassDevs函数用来获得一类硬件设备的信息,设备信息集句柄hDevInfo

HDEVINFO hDevInfo=SetupDiGetClassDevs(  
&guidHID, //这类设别配置或接口类GUID  
NULL,     //特定的字符串,用来选择符合条件的设备  
0,        //与获得信息相关的顶层窗体句柄  
DIGCF_PRESENT|DIGCF_DEVICEINTERFACE  //给出了设置信息的方式  
); 
3)SetupDiEnumDeviceInterface函数得到设备接口信息反复调用得到所有设备接口信息strInterfaceData,若要找到特定设备,可在循环语句内调用 该函数,直到找到预期设备或函数返回False值, 定义为:
Bool bSuccess=SetupDiEnumDeviceInterface(  
hDevInfo,   //感兴趣的接口句柄  
NULL,       //指向SP_DEVINFO_DATA类型结构的指针,该结构定义了特定接口  
&guidHID,   //确定了接口的GUID标识  
Index,      //所关心的索引号,以0为起点  
&strInterfaceData,  //指向SP_DEVICE_INTERFACE_DATA类型的指针,他所指向的内容就是调用函数的目的所在,当函数返回时,strInterfaceData指向的结构就存在相关接口信息  
);  
其中SP_DEVINFO_DATA结构定义为:
typedef struct SP_DEVINFO_DATA{  
DWORD cbsize;   //指定结构的大小  
GUID calssGuid; //设备的GUID标识  
DWORD DevInst;  //用来访问设备的句柄  
ULONG_PTR Reserved;  
}SP_DEVINFO_DATA,*PSP_DEVINFO_DATA;  
SP_DEVICE_INTERFACE_DATA结构的定义如下:
typedef struct SP_DEVICE_INTERFACE_DATA{  
DWORD cbsize;            //是SP_DEVICE_INTERFACE_DATA结构的大小  
GUID InterfaceClassGuid; //指定了接口的GUID标识  
DWORD Flags;             //接口所处状态  
ULONG_PTR Reserved;  
}SP_DEVICE_INTERFACE_DATA,*PSP_DEVICE_INTERFACE_DATA;  
4)SetupDiGetDeviceInterfaceDetail() 
long Result=SetupDiGetDeviceInterfaceDetail(  
hDevInfo,          //设备信息集句柄  
&strInterfaceData, //设备接口信息  
NULL,              //设备路径  
0,                 //输出缓冲区大小  
&Length,  
NULL);  

再次调用为了得到strInterfaceDetailData
long Result=SetupDiGetDeviceInterfaceDetail(  
hDevInfo,  
&strInterfaceData,  
strInterfaceDetailData,  
Length,  
&Required,  
NULL  
);  

2、与USB设备交换数据

在Windows中,读写端口与读写文件都是调用同样的API函数,打开或创建端口用CreateFile,从端口读数据用ReadFile,用WriteFile向端口写数据

1)设备的打开与关闭

用API函数CreateFile来打开或创建设备:

HANDLE hCom=CreateFile(   
strInterfaceDetailData->DevicePath, //指定打开设备名   
GENERIC_READ|GENERIC_WRITE,         //允许读写   
0,                                  //独占方式   
NULL,                               //安全模式   
OPEN_EXISTING,                      //打开   
FILE_ATTRIBUTE_NORNAL,              //文件属性   
NULL,                               //临时文件的句柄   
);  

如果调用成功,函数返回文件的句柄,如果调用失败,则返回INVALID_HANDLE_VALUE,在打开通信设备时,应该以独占方式打开, 不再使用设备句柄时,应该调用CloseHandle(hCom)函数关闭它。

2)Bool Result = HidD_GetAttributes (hCom, &strAttrib);
其中hCom是对应于选定设备的句柄,strAttribute则是指向HIDD_ATTRIBUTES类型的指针,当函数返回时即得到了指定设备的属性

typedef struct _HIDD_ATTRIBUTES {   
ULONG Size;        // = sizeof (struct _HIDD_ATTRIBUTES)   
USHORT VendorID;   // Vendor ids of this hid device USHORT ProductID;   
USHORT VersionNumber;   
// Additional fields will be added to the end of this structure.   
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;  
3)设备的读写操作,读写通信设备可用同步方式执行
HANDLE hCom;  
void *pBuffer;  
DWORD iLength;  
DWORD pReadFact;  
Bool ReadFile(hCom,pBuffer,iLength,&pReadFact,NULL);  

读到的数据放在内存pBuffer里,pBuffer要先申请内存空间,iLength为需要读的数据长度,pReadFact存放实际的数据长度。

需要注意的是在读写设备之前,应先调用ClearCommError函数清除错误标志,此函数负责报告指定的错误的设备的当前状态,调用PrugeComm函数可以更改正在进行的读写操作方式


3、函数模块实例

1)打开设备

四个模块中,打开设备是最复杂的。因为它需要通过其设备类来枚举设备树上的所有设备,从而得到相匹配的设备,进而得到设备名

BOOL DeviceOpen(HANDLE&handle, WORD wVID, WORD wPID)  
{  
    BOOL bRet = FALSE;  
    GUID hidGuid;  
    HDEVINFO hardwareDeviceInfo;  
    SP_INTERFACE_DEVICE_DATA deviceInfoData;  
    PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL;  
    ULONG predictedLength = 0;  
    ULONG requiredLength = 0;  
    CloseHandle(handle);  
    handle = INVALID_HANDLE_VALUE;  
    deviceInfoData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);  
    HidD_GetHidGuid(&hidGuid);  
    hardwareDeviceInfo = SetupDiGetClassDevs(&hidGuid, NULL,NULL, (DIGCF_PRESENT|DIGCF_DEVICEINTERFACE));  
    for (int i=0; i<128; i++)  
    {  
       if (!SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 0,&hidGuid, i, &deviceInfoData)) continue;  
       SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo, &deviceInfoData,NULL, 0, &requiredLength, NULL);  
       predictedLength = requiredLength;  
       functionClassDeviceData =(PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(predictedLength);  
       if (!functionClassDeviceData) continue;  
       functionClassDeviceData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);  
       if (!SetupDiGetDeviceInterfaceDetail (hardwareDeviceInfo,&deviceInfoData, functionClassDeviceData, predictedLength,&requiredLength, NULL)) break;  
       handle = CreateFile(functionClassDeviceData->DevicePath,GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING, 0, NULL);  
       if (handle != INVALID_HANDLE_VALUE)  
       {  
           HIDD_ATTRIBUTES attri;  
           HidD_GetAttributes(handle, &attri);  
           if ((attri.VendorID == wVID) && (attri.ProductID == wPID))  
           {  
              bRet = TRUE;  
              break;  
           }  
           CloseHandle(handle);  
           handle = INVALID_HANDLE_VALUE;  
       }  
    }  
    SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);  
    return bRet;  
}  

2)关闭设备

关闭设备比较简单,只需要直接使用函数CloseHandle即可


void DeviceClose(HANDLE&handle)  
{  
    CloseHandle(handle);  
    handle = INVALID_HANDLE_VALUE;  
}  

3)写数据

假设HID的Report大小为8字节,且第一字节为ID

BOOL DeviceWrite(HANDLEhandle, LPCVOID lpBuffer, DWORD dwSize)  
{  
    BYTE wBuffer[8] = {0};  
    DWORD dwRet;  
    BOOL bRet;  
    wBuffer[0] = 0x01;  
    wBuffer[1] = 0x00;  
    memcpy(&wBuffer[2], lpBuffer, min(6, dwSize));  
    bRet = WriteFile(handle, wBuffer, 8, &dwRet, NULL);  
    return bRet;  
}  
4)读数据
BOOL DeviceRead(HANDLEhandle, LPVOID lpBuffer, DWORD dwSize)  
{  
    BYTE rBuffer[8] = {0};  
    DWORD dwRet;  
    BOOL bRet;  
    rBuffer[0] = 0x01;  
    rBuffer[1] = 0xff;  
    bRet = WriteFile(handle, rBuffer, 8, &dwRet, NULL);  
    bRet &= ReadFile(handle, rBuffer, 8, &dwRet, NULL);  
    memcpy(lpBuffer, &rBuffer[1], min(7, dwSize));  
    return bRet;  
}  



猜你喜欢

转载自blog.csdn.net/Angle0306/article/details/75046484
今日推荐