用VC++編寫USB介面通訊程式
摘要:詳細介紹Visual C++ 6.0環境下利用Windows API(Application Program Interface)函數來實現與符合HID裝置類的USB介面通訊,並給出了通訊程式的部分代碼。
關鍵詞:通用序列匯流排 人際介面裝置 API VisualC++
1 引言
在USB出現之前,電腦的典型介面有並行口(列印口)、串列口、滑鼠口、鍵盤口、顯示器口、遊戲口及各種卡式介面(如音效卡、網卡)等,與這些介面對應的有各種不同的電纜。在傳輸速度方面,這些介面都存在速度偏低的問題;在技術方面,這種設計容易產生I/O衝突。中斷(IRQ)不夠用,以及對於每一種新的外設都必須設計新的介面卡等缺點。當今的電腦外部裝置,都在追求高速度和高通用型。USB介面適應了這種要求,並以其速度快,使用方便,成本低等優點,迅速得到了眾多PC廠商和半導體廠商的大力支援,外設向USB過渡稱為必然的趨勢。
但如果主機PC不知道如何與USB外設通訊,那麼這個USB外設一點用處都沒有,人機介面裝置(HID)類是Windows完全支援的第一批USB裝置類型中的一種。在運行Windows98或更高版本的PC機上,應用程式可以使用作業系統內建的驅動與HID通訊,但與HID通訊不像開啟一個連接埠,設定幾個參數,然後就可以讀寫資料那麼簡單。在應用程式能與HID交換資料之前,它先要找到裝置,擷取有關它的報告資訊,為做到這些,應用程式必須通過訪問通訊API函數,使位於上層的應用程式與位於下層的裝置驅動程式進行資料交換。應用程式可以使用任何能訪問API函數的程式設計語言,C++是一種能訪問API函數的功能強大的語言,本文將在VisualC++6.0環境下編寫與USB裝置通訊的Windows程式。
2 USB簡介
USB是由Intel,Compaq,Digital,IBM,Microsoft,NEC,Northern Telecom等七家世界著名的電腦和通訊公司共同推出的新一代介面標準,全稱為Universal Serial Bus(通用序列匯流排)。它是為瞭解決日益增加的PC外設與有限的主板插槽和連接埠之間的矛盾而制定的一種串列通訊標準,尤其當傳輸速率高達480Mbit/s的USB2.0規範面世後,USB應用更加廣泛,它具有下屬優點:
(1)適用於多種外設,使它不需要為不同的外設準備不同的介面和協議;
(2)Windows能自動檢測到USB裝置的熱插拔,並自動設定;
(3)PC機上的IRQ線非常緊缺,而USB裝置並不需要設定連接埠和IRQ,故無論從使用者使用的方便性,或從對資源的佔用方面看,USB都很有優秀;
(4)當接入一個USB裝置時,全速USB介面可達12Mbit/s,考慮到狀態,控制和出錯資訊,最大理論速度仍可達9.6Mbit/s,這是其他串列介面協議所不能比擬的,且USB也支援1.5Mbit/s的低速傳輸;、
但是USB同樣有缺點,諸如:協議複雜,編寫裝置驅動程式要考慮很多細節,以保證USB某些特性的透明性,但通過調用Win32的API函數與裝置通訊,或者說與內建的驅動程式通訊,便沒有必要去逐條理解複雜的協議。
3 VC++實現與USB介面通訊的執行個體
Windows下,與USB外設的任何通訊需通過裝置驅動,該驅動知道如何與系統的USB驅動和訪問裝置的應用程式通訊,Windows包含應用程式與HID通訊需要的各種資訊,不需要再安裝裝置驅動。Win32的應用程式介面(API)函數,使得裝置驅動能與應用程式之間相互連信,應用程式也不需要為了和USB裝置通訊去瞭解複雜的USB協議。
下面用VisualC++編寫應用程式調用API函數,從而簡化了與硬體通訊的過程。
3.1 建立工程
操作步驟如下:
(1)在VC++6.0工作平台中開啟File 菜單,選擇New功能表命令,在對話方塊中選擇Project選項,在左邊列表框選擇MFC AppWizard(exe),在Project name 文本編輯框中輸入項目名USBPort,在Location文本編輯框中輸入項目路徑,單擊OK按鈕,進入MFC AppWizard。
(2)在MFC AppWizard-Step1視窗中,選擇Dialog based選項,不該變其他選項的預設值
(3)在MFC AppWizard-Step2 of 4視窗中,選擇About box和3D controls複選框
(4)在MFC AppWizard-Step3 of 4到Step4 of 4不改變各個選項的預設設定
(5)進入New Project Information視窗,如果檢查完全正確後,單擊OK按鈕即產生應用程式所需要的全部檔案。
通過上述操作便產生了基於對話方塊的工程USBPort。
3.2 尋找USB裝置
在應用程式能與HID交換資料之前,它先要找到裝置,擷取有關它的報告資訊。首先找到串連到系統的HID是什麼,然後檢索資訊,知道滿足要求的屬性。
(1)新增成員函數。單擊ClassView標籤,選定CUSBPortDlg類,右擊添加OnSearch訊息響應函數,並增加私人類型成員變數,即字串型變數strPath和strLog以及布爾類型變數bFoundDevice。
(2)OnSearch函數調用API函數,HID類裝置是通過GUID類型值作標識的,調用函數HidD_GetHidGuid顆獲得HID裝置的標識:Hidd_GetHidGuid(&guidHID);
其中guidHID是指向GUID類型的指標,當函數返回後,它指向的內容就是HID類的GUID標識,GUID是16位元組大小的結構,用來標識通訊介面及類對象,它的定義為:
typedef struct _GUID
{
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
}GUID;
調用函數HidD_GetHidGuid獲得特定的HID裝置屬性
BOOL HidD_GetAttributes(hCom,&strAttrib);
其中hCom是對應與選定裝置的控制代碼,根據這個控制代碼定所關心的裝置,strAttrib則是指向HIDD_ATTRIBUTES類型的指標,當函數返回時即得到了指定裝置的屬性。
HIDD_ATTRIBUTES結構定義為:
typedef struct _HIDD_ATTRIBUTES
{
ULONG size;//這個HIDD_ATTRIBUTES變數大小,以位元組為單位
USHORT vendorID;//致命HID裝置的供應商標識
USHORT ProductID;//致命HID裝置的產品標識
USHORT VersionNumber;//HID裝置的版本號碼
}HIDD_ATTRIBUTES,*PHIDD_ATTRIBUTES;
OnSearch函數中還調用了其他與硬體相關的API函數,這些函數都在Setupapi.h中定義。調用SetupDiGetClassDevs函數用來獲得一類硬體裝置的資訊:
HDEVINFO hDevInfo = SetupDiGetClassDevs(
&guidHID,//這類裝置配置或介面類GUID
NULL,//特定的字串,用來選擇合格裝置
0,//與獲得資訊相關的頂層表單(Top_Level Window)控制代碼
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE//給出了設定資訊集的方式
);
調用SetupDiEnumDeviceInterfaces函數得到一個裝置介面資訊反覆調用得到所有裝置介面資訊。若要找到特定裝置,可在迴圈語句內調用該函數,直到找到預期裝置或函數返回FALSE值。
定義該函數:
BOOL bSuccess = SetupDiEnumDeviceInterface(
hDevInfo,//感興趣的介面控制代碼
NULL,//指向SP_DEVINFO_DATA類型結構的指標,該結構限定了特定介面
&gudiHID,//確定了介面的GUID標識
0,//所關心的索引號,以0為起點
&strInterfaceData,//指向SP_DEVINFO_INTERFACE_DATA類型的指標,
它所指向的內容就是調用函數的目的所在,當函數返回時,strInterfaceData
指向的結構就存在相關介面的資訊。
);
其中結構SP_DEVINFO_DATA定義為:
typedef struct _SP_DEVINFO_DATA{
DWORD cbsize;//指定結構的大小
GUID classGuid;//裝置的GUID標識
DWORD DevInst;//用來訪問裝置的控制代碼
ULONG_PTR Reserved;
}SP_DEVINFO_DATA,*PSP_DEVINFO_DATA;
結構SP_DEVICE_INTERFACE_DATA定義為
typedef struct _SP_DEVICE_INTERFACE_DATA{