VC++编写USB接⼝通讯程序
⽤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{
enum函数
DOWRD cbsize;//是SP_DEVICE_INTERFACE_DATA结构的⼤⼩
GUID InterfaceClassGuid;//指定了接⼝的GUID标识
DWORD Flags;//接⼝所处状态
ULONG_PTR Reserved;
}SP_DEVICE_INTERFACE_DATA,*PSP_DEVICE_INTERFACE_DATA;
3.3 与USB设备交换数据
在Windows中,读写端⼝与读写⽂件都是调⽤同样的API函数,打开或创建端⼝⽤CreateFile从端⼝读数据⽤ReadFile,向端⼝数据⽤WriteFile。
(1)设备的打开和关闭,⽤API函数CreateFile来打开或创建设备:
HANDLE hCom = CreateFile(
m_strPath,//指定打开设备名
GENERIC_READ | GENERIC_WEITE,//允许读写
0,//独占⽅式
NULL,//安全模式
OPEN_EXISTING,//打开
FILE_ATTRIBUTE_NORMAL,//⽂件属性
NULL //临时⽂件的句柄
);
如果调⽤成功,该函数返回⽂件的句柄;如果调⽤失败,则返回INVALID_HANDLE_VALUE,在打开通信设备时,应该以独占⽅式打开。
不再使⽤设备句柄时,应该调⽤CloseHandle(hCom)函数关闭它。
(2)设备的读写操作,读写通信设备可⽤同步⽅式执⾏,也可⽤异步⽅式执⾏,这由CreateFile函数中是否指定FILE_FLAG_OVERLAPPED来决定;指定为异步⽅式,未指定则为同步⽅式,函数ReadFile和WriteFile的参数和返回值类型,下⾯是调⽤ReadFile函数的实例。
HANDLE hCom;
void *pBuffer;
DWORD iLength;
DWORD pReadFact;
BOOL ReadFile( hCom, pBuffer, iLength, &pReadFact, NULL );
读到的数据放在内存pBuffer⾥,pBuffer要先申请内存空间,iLength为需要读的数据长度,pReadFact存放实际读的数据长度。需要注意的是在读写设备之前,应先调⽤ClearCommError函数清除错误标志,此函数负责报告指定的错误的设备的当前状态,调⽤PrugeComm函数可以更改正在进⾏的读写操作⽅式。
4 结论
以上是调⽤Win32的API通信函数⽤VC++编写的USB设备通信程序,它实现了查符合HID类的USB设备并与之进⾏数据交换的基本功能。这种独⽴与通信⼦系统之外实现的应⽤程序,可以实现可靠的⾼速数据传输。对⾃定义设备,应⽤程序需要设备的特定供应商和产品ID,或设备的特定类型,读者只需要对上述程序作⼀定修改即可,在MFC环境下,编写32位串⼝通信程序还可采⽤ActiveX的MSComm控件,但使⽤API通信函数,具有更⼤灵活性,可定制性也更强,在设置通信配置和发送错误敏感,⽆时间限制的数据时,该接⼝尤其有⽤。