天天看点

VC++ USB接口程序函数

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

继续阅读