天天看點

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

繼續閱讀