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{

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通信函数,具有更大灵活性,可定制性也更强,在设置通信配置和发送错误敏感,无时间限制的数据时,该接口尤其有用。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享