为了正常的体验网站,请在浏览器设置里面开启Javascript功能!
首页 > USBHID协议中文版

USBHID协议中文版

2020-11-10 9页 doc 98KB 14阅读

用户头像 个人认证

百里登峰

暂无简介

举报
USBHID协议中文版USBHID协议中文版第8章USB接口HID设备HID(HumanInterfaceDevice,人机接口设备)是USB设备中常用的设备类型,是直接与人交互的USB设备,例如键盘、鼠标与游戏杆等。在USB设备中,HID设备的成本较低。另外,HID设备并不一定要有人机交互功能,只要符合HID类别规范的设备都是HID设备。Wndows操作系统最先支持的HID设备。在windows98以及后来的版本中内置有HID设备的驱动程序,应用程序可以直接使用这些驱动程序来与设备通信。在设计一个USB接口的计算机外部设备时,如果HID类型的设备...
USBHID协议中文版
USBHID协议中文版第8章USB接口HID设备HID(HumanInterfaceDevice,人机接口设备)是USB设备中常用的设备类型,是直接与人交互的USB设备,例如键盘、鼠标与游戏杆等。在USB设备中,HID设备的成本较低。另外,HID设备并不一定要有人机交互功能,只要符合HID类别的设备都是HID设备。Wndows操作系统最先支持的HID设备。在windows98以及后来的版本中内置有HID设备的驱动程序,应用程序可以直接使用这些驱动程序来与设备通信。在设计一个USB接口的计算机外部设备时,如果HID类型的设备可以满足需要,可以将其设计为HID类型设备,这样可以省去比较复杂的USB驱动程序的编写,直接利用Windows操作系统对的HID类型USB设备的支持O8.1HID设备简介8.1.1HID设备的特点交换的数据储存在称为报表(Report)的结构内,设备的固件必须支持HlD报表的格式。主机通过控制和中断传输中的传送和请求报表来传送和接收数据。报表的格式非常灵活。每一笔事务可以携带小量或中量的数据。低速设备每一笔事务最大是8B,全速设备每一笔事务最大是64B,高速设备每一笔事务最大是1024B。一个报表可以使用多笔事务。设备可以在未预期的时间传送信息给主机,例如键盘的按键或是鼠标的移动。 所以主机会定时轮询设备,以取得最新的数据。HID设备的最大传输速度有限制。主机可以保证低速的中断端点每10ms内最多1笔事务,每一秒最多是800B。保证全速端点每lms一笔事务,每一秒最多是64000B。保证高速端点每125us三笔事务,每一秒最多是24.576MB。HID设备没有保证的传输速率。如果设备是设置在10ms的时距,事务之间的时间可能等于或小于10ms。除非设备是设置在全速时在每个帧传输数据,或是在高速时在每个微帧传输数据。这是最快的轮询速率,所-以端点可以保证有正确的带宽可供使用。HID设备除了传送数据给主机外,它也会从主机接收数据。只要能够符合 HlD类别规范的设备都可以是HID设备。设备除了HlD接口之外,它可能同时还包含有其他的USB接口。例如影像显示设备可能使用HID接口来做亮度、对比度的软件控制,而使用传统的影像接口来传送要显示的数据。USB扩音器可以使用实时传输来播放语音,同时使用HID接口来控制音量、低音等。HID类别设备的规范文件主要是以下两份:DeviceClassDefinitionforHumaninterfaceDevicesHIDUsageTables其中前者是HID的基本规范文件,后者可以是前者的附件,为开发人员提供实际的控制类型的描述。文件是用来定义让主机了解以及使用HID数据的数值。这两份文件是由 USBDeviceWorkingGroup制定的,可以在网址http://www.usb.org/developers/hidpage/#ClassDefinition下载。8.1.2HID设备的硬件要求HID接口必须符合DeviceClassDefinitionforHumaninterfaceDevices规范内所定义的HID类别的需求。在此文件内描述了所需的描述符、传输的频率以及传输的类型等。为了符合规范,HID接口的端点与描述符都必须符合数个要求。所有的HID传输都是使用默认控制管道或是一个中断管道,HID设备必须有一个中断输入端点来传送数据到主机,中断输出端点则不是必需的。 HID类 默认控» ► HID类 别表 中断管8-1HID传输的传输类型8-1HID设备的传输类型 传输类型 数据来源 数据类型 是否需要管道 设备(输入) 没有严格时间限制的数据 是 控制 主机(输出) 没有严格时间限制的数据或是没有中断输出管道时的任何数据 中断 设备(输入) 定时或低延迟的数据 是 主机(输出) 定时或低延迟的数据 是主机与设备之间所交换的数据,可以分成两种类型:低延迟的数据,必须尽快地到达目的;配置或其他的数据,没有严格时间限制的需求。中断管道是控制管道之外的另一种数据交换的方式,特别适合使用在接收端需要定时或是尽可能及时收到数据的时候。中断输入管道携带数据到主机,中断输出管道则是携带数据到设备。在总线忙的时候,控制管道可能会被延迟,而中断管道保证会有可得到的带宽。HID不需要一定有中断输出管道。如果没有中断输出管道,主机会在控制管道上使用HID设备特有的Set_Report请求来传送所有的报表。8.1.3HID固件的要求主机的驱动程序要与HID设备通信,设备的固件必须符合下列需求:设备的描述符必须识别该设备包含有HID接口。除了默认控制管道外,固件必须另外支持一个中断输入管道。固件必须包含一个报表描述符来定义要传送与接收的设备数据。如果要传送数据,固件必须支持Get_Report控制传输与中断输入传输。如果要接收数据,固件必须支持Set_Report控制传输与选择性的中断输出传输。所有的HID数据都必须使用定义过的报表格式来定义报表中数据的大小与。设备可以支持一个或多个报表。在固件中的一个报表描述符用来描述此报表,以及如何使用报表数据的信息。在每一个报表中的一个数值,定义此报表是一个输入(Input)、输出(Output)或是特征(Feature)报表。主机在输入报表中接收数据,在输出报表中传送数据,特征报表可以在任何方向传递。Windows98以及后来版本的HID驱动程序使用中断传输来传递输入报表。输出报表的传输类型要根据设备支持的端点与Windows的版本而定。Windows98Gold只符合HID1.0规范,它的HID驱动程序使用控制传输来传递输出报表。 Windows98SE、Wndows2000符合HID1.1规范,HID驱动程序在有中断输出端点时使用中断传输,否则使用控制传输来传递输出报表。特征报表都是使用控制传输。8.2HID设备描述符HID设备连接到USB主机后,主机通过发送Get_Descriptor请求读取HID设备的描述符,亍解描述符对了解USB设备是至关重要的。8.2.1HID设备的描述符HID设备除了支持USB设备的5种标准描述符之外,还支持HID设备特有的3种描述符。这些描述符是:USB标准描述符:设备、配置、接口、端点和字符串描述符。HID特有的描述符:HID、报表(Report)和实体(Physical)描述符。从描述符的关联关系看,HID描述符是关联于接口。所以如果一个HID设备有2个端点,设备不需要每个端点有一个HID描述符。从前面的USB描述符可以看出一个规律,描述符的第一、二字节分别是描述符的长度和类型,描述符的类型字段(bDescriptorType)表明描述符的种类,下表列出了不同描述符的类型字段数值。表8-2HID的描述符 类型 描述符 应用 数值 标准 设备Device 所有设备必须有,只能一个 01 配置Configuration 「所有设备必须有,至少一个 02 字符串String 可选择 03 接口Interface 每一个接口一个 04 端点Endpoint :除端点0之外的每个端点一个 05 设备限定 Device_Qualiffier 同时支持全速与高速的设备必须有_个 06 OtherSpeedConfiguration 07 Interfacepower 08 类别 HID HID设备必须有 21 「Hub 29 HID 报表 Report HID设备必须有 22 特定 实体 Physical 可选择的 23对于一个HID设备,设备描述符与配置描述符没有HID特定的信息。其设备描述符的bDeviceClass和bDeviceSubClass字段的值为0,接口描述符的blnterfaceClass字段值为03,表示设备的该接口是HID类别。在接口描述符中其他包含HID特定信息的字段还有子类别码(blnterfaceSubClass)与协议码(blnterfaceProtocol字段)。在接口描述符中子类别码字段等于 1表示此设备支持启动接口(BootInterface)。如果设备有启动接口,即便主机的HID没有加载驱动程序,设备也可以使用。这种情形可能发生在计算机是由DOS直接启动,在启动时观看系统设置画面或使用 Wndows的安全模式时。含有启动接口的键盘或鼠标可以使用BIOS或许多主机支持的默认简单协议。HID规范定义了键盘与鼠标的启动接口协议。如果设备没有启动接口,并且接口描述符中协议码字段是1,表示设备支持键盘接口,协议码字段是2,表示支持鼠标接口。接口描述符中协议码字段是0,表示设备不支持启动协议。在HIDUsageTables规范中定义了键盘与鼠标的启动描述符(BootDescriptor)。BIOS不需要从设备中读取描述符,因为它知道启动协议,并且假设设备支持启动协议。所以要启动的设备不需要在固件内包含启动接口描述符,它只要在主机尚未要求在报表描述符中的定义协议时支持启动协议即可。在操作系统加载 HlD驱动程序后会使用Set_Protocol请求,将设备由启动协议转换成报表协议。8.2.2HID描述符HID描述符的主要作用是用来识别HID通信所使用的额外描述符。下表是HID描述符结构。表8-3HID描述符结构 偏移量 字段 字节数 数值类型 说明 0 bLength 1 Numeric 描述符字节数 1 bDescriptorType 1 Constant 0x21=HID描述符 2 bcdHID 2 Numeric HID规范版本号(BCD) 4 bCountryCode 1 Numeric 硬件设备所在国家的国家代码 5 bNumDescriptors 1 Numeric 类别描述符数目(至少有一个报表描述符) 6 bDescriptorType 1 Constant 类别描述符的类型 7 wDescriptorLength 2 Numeric 报表描述符的总长度 9 [bDescriptorType]... 1 Constant 附加的描述符的类型,可选的 10 [wDescriptorLength]... 2 Numeric 附加的描述符的总长度,可选的bcdHID:设备与其描述符所遵循的HID规范的版本号码,此数值是4个16进制的BCD格式字符。例如版本1.1的bcdHID是0110h。(2bytes)bCountryCode:硬件目的国家的识别码。如果不说明,该字段为0。bDescriptorType:HID描述符附属的描述符的类型(报表或实体)。每一个HID都必须至少支持一个报表描述符。一个接口可以支持多个报表描述符,以及一个或多个实体描述符。HID描述符的偏移量为9和10的bDescriptorType和wDescriptorLength可以重复存在多个。1.报表描述符报表描述符定义了执行设备功能的数据格式和使用方法。报表描述符和USB的其他描述符是不一样的,它不是一个简单的表格, 报表描述符是USB所有描述符中最复杂的。报表描述符非常复杂而有弹性,因为它需要处理各种用途的设备。报表的数据必须以简洁的格式来储存,这样才不会浪费设备内的储存空间以及数据传输时的总线时间。实际上可以这样理解,报表内容的简洁,是通过报表描述符全面的、复杂的数据描述实现的。报表描述符必须先描述数据的大小与内容。报表描述符的内容与大小因设备的不同而不同,在进行报表传输之前,主机必须先请求设备的报表描述符,只有得到了报表描述符才可正确解析报表的数据。报表描述符是报表描述项目(Item)的集合,每一个描述项目都有相对统一的数据结构,项目很多,通过编码实现。(D项目报表描述符由描述HID设备的数据项目(Item)组成。项目的第一个字节(项目前缀)由三部分构成:项目标志(itemTag):说明项目的功能,项目类型(itemType):说明项目的数据类型,项目长度(itemSize):说明项目的数据部分的长度。HID的项目有短项目和长项目两种,其中短项目的格式如下图。Data(可以是0、1」bTaglbTbSi位序字段图8-3HID报表短项目格式短项目的数据字节数由bSize的值定义,bSize为0、1、2、3时Data部分的字节数分别为0、1、2、4个字节。(nn为数据长度)短项目的项目类型由bType定义,bType为0、1、2时分别为Main、Global和Local类型。(见后面的表8-4HID项目列表)长项目可以携带较多的数据,其格式如下图。位序字段字节数Data 'bLQng「bDataS1111图8-4HID报表长项目格式项目中的第一个字节为上图中的特定值时表明该项目是一个长项目。长项目中的bDataSize说明Data部分的字节数,bLongItemTag在HID规范中没有定义。下面是通过汇编实现的一个简单的报表描述符,描述符的每一行是一个项目,该描述符描述了一个从设备接收2个字节的输入报表和发送2个字节到设备的输出报表。 HID_Report_desc_table:db06h,A0h,FFhdb09h,A5hdbA1h,01h ;UsagePage(Vendordefined);Usage(VendorDefined);Collection(Application) 定义设备功能定义用法开一个集合 db09H,A6h ; Usage(Vendordefined) 定义用法 ; 输入报表 db09h,A7h ; Usgae(Vendordefined) 定义用法 db15h,80h ; LogicalMinimum 定义输入最小值=-128 db25h,7Fh ; LogicalMaximum 定义输入最大值=+127 db75h,08h ReportSize 定义报表数据项大小=8 db95h,02h ; ReportCount 定义报表数据项个数=2 db81h,02h ;Input(Data,Variable,Absolute) 输入项目 ; 输出报表 db09h,A9h ; Usgae(Vendordefined) 定义用法 db15h,80h ; LogicalMinimum 定义输入最小值=-128 db25h,7Fh ; LogicalMaximum 定义输入最大值=+27 db75h,08h ; ReportSize 定义报表数据项大小=8 db95h,02h ; ReportCount 定义报表数据向个数=2 db91h,02h ;Output(Data,Variable,Absolute) 输出项目 dbC0h ;EndCollection 关闭集合(2)项目的分类报表的项目有Main、Global和Local三大类,每一类都有多个不同的项目,实现不同的描述。Main类项目用于定义报表描述符中的数据项。也可以组合其中的若干数据项成为一个集合。Main项目可以分为带数据的Main项目和不带数据的Main项目。带数据项的Main用于生成报表中的数据项,包括Input、Output和Feature项目。不带数据的Main项目不生成报表中的数据项,包括Collection和EndCollection项目。Global类项目实现对数据的描述,用来识别报表并且描述报表内的数据,包括数据的功能、最大与最小允许值以及数据项的大小与数目等。改变由Main类项目生成的项目状态表。Global类项目描述对后续的所有项目有效,除非遇到有新的Global类项目。Local类项目定义控制的特征,这一类项目的作用域不超过下一个Main项目,所以在每一Main项目之前可能有多个Local项目。Local项目用于描述后面的Input、Output和Feature项目。下表列出的是全部的项目的前缀字和简要功能说明。表8-4HID项目列表 项目类型 项目标志(Tag) 项目前缀,nn为数据长度 功能说明 Main类项目(00) Input 100000nn 定义输入报表,主机利用该信息解析设备提供的数据。主机向控制端口发送Get_Report实现输入 Output 100100nn 创建输出报表,通过向设备发送 Set_Report实现输出 Feature 101100nn 定义送往设备的设置信息 Collection 101000nn 定义2个以上数据(Input、Output和Feature)的关系为集合,Collection开始一个集合,之后的EndCollection 结束集合。Collection项目的数据部分说明Collection的类型 EndCollection 110000nn Global类项目(01) UsagePage 000001nn 指定设备的功能 (06h,A0h,FFh)另外由于Usages目有32位数据值,UsagePage项目用于为Usage项目在报表描述符中占居存储空间。用于存放后续的Usages目的局16位。 LogicalMinimum 000101nn 宋义变量或数绢项目的谬辑最小佰和最大佰 LogicalMaximum 001001nn (-128,+127) (15h,80h)(25h,7Fh) PhysicalMinimum 001101nn 定义变量或数组项目的物理最小值和最大值,分别和LogicalMinimum、LogicalMaximum对应 PhysicalMaximum 010001nn UnitExponent 010101nn 定义数值是基于10的指数 二 Unit 011001nn 单位 ReportSize 011101nn 指定报表数据区域所包含的位数 (=8) ReportID 100001nn 报表ID,该项目在报表中插入一个字节的报表 ID ReportCount 100101nn 报表中数据域的数目 (=2) Push 101001nn 将Global项目状态表送入堆栈 Pop 101101nn 从堆栈恢复Global项目状态表 110001nn111101nn 保留 Local类项目(10) Usage 000010nn 用法索引值,表示对项目或集合建议的用法,用于当一个项目描述多个控制,对每一个变量和数组元素都有建议的用法(09h,A7h) UsageMinimum 000110nn 定义阵列或位图中控制操作的第一个和最后一个用法 UsageMaximum 001010nn DesignatorIndex 001110nn 确定用于控制的实体,指向物理描述符中的目标 DesignatorMinimum 010010nn 定义阵列或位图目标的起始和终止索引值 DesignatorMaximum 010110nn StringIndex 011110nn 确定字符串描述符中的索引值 StringMinimum 100010nn 定义用于阵列或位图控制中字符串序列索引值的最小值和最大值 StringMaximum 100110nn Delimiter 101010nn 定义一组Local项目的开始和结束,1=开始,0=结束 101010nn111110nn 保留在这些项目中,UsagePage用来指定设备的功能,而Usage项目用来指定个别报表的功能。UsagePage项目相当于是HID的子集合,Usage相当于是UsagePage的子集合。2.报表描述符的项目(1)Input、Outpot和Feature项目这3个项目用来定义报表中的数据字段。Input项目可以应用到任何控制、计数器读数或其他设备传给主机的信息。一个输入报表包含一个或多个Input项目,主机使用中断输入传输来请求输入报表。Ouput项目用来定义主机传送给设备的信息。一个输出报表包含一个或多个 Outpot项目。输出报表包含控制状态的数据。如果有中断输出管道,HID1.1兼容主机使用中断输出传输来传送输出报表,否则使用Set_Report控制请求。Feature项目应用到主机传送给设备的信息,或是主机从设备读取Feature项目。一个特征报表包含一个或多个Feature项目,Feature项目通常是包合影响设备与其组件整体行为的配置。特征报表通常是控制可以使用实际的控制面板调整的设置,例如主机可以使用虚拟控制面板来让用户选择控制特征。主机使用Set_Report与Get_Report请求来传送与接收特征报表。在每一个Input、Output和Feature项目的前缀字之后是32位描述数据,目前最多定义了9个位,余的位则是保留。位0~8的定义中只有位7不能应用于Input项目,除此之外其他的位定义都适应于Input、Output和Feature项目。表8-5Input、Output和Feature项目的数据项说明 数据字段 含义说明 位 值 名称 0 0 Data 数据:表示项目的内容是可更改的(读/写)。 1 Constant 常数:表示项目的内容是不可更改的(只读) 。 1 0 Array 数组:全部控制的状态。如在键盘报表中每一个键在报表中占一位,报表传输全部键的状态,可以同时按下任意多个键。 1 Variable 变量:报告作用中的控制。如在键盘报表中只报告按下的键的编号,可以同时按下的键的数目等于报表的计数( Global类项目ReportCount) 2 0 Absolute 绝对:表示数值以一个固定值为基准。游戏杆通常是报告绝对数据(游戏杆目前的位置)。 1 Relative 相对:表示数据的改变以上一个读数为基准。鼠标通常是报告相对数据(鼠标的移动位置)。 3① 0 NoWrap 如果设置为1表示回转,当数值超过最小值到最大值的范围时将回转,如果最小值是0而最大值是10,超过最大值的下一个数值是0。 1 Wrap 4① 0 Linear 线形:表示测量的数据与报表的数据有线性的关系。 1 Non-Linear 非线性:表示测量的数据与报表的数据没有线性的关系。 5① 0 Preferred 优选状态:表示控制在没有用户交互时会回到一个特定的状态。如按钮就有优选状态,在无操作时保持未按下的状态。 1 Non-Preferred 非优选状态:它维持在上一个用户选择的状态。如交替的开关就没有优选状态。 6① 0 NoNullPosition 无空状态位置:表示控制永远在传送有效的数据。 1 NullState 空状态:表示控制支持一个没有传送有效数据的状态。如操纵杆可能具有一个多方向的按钮开关,在没有按下时在空状态,这时控制将传送一个在LogicalMinimum与LogicalMaximum范围之外的数值来表示它在空状态。 7② 0 Non-Volatile 不可变的:表示设备只有在主机请求时才改变数值。当主机传送一个报表并且不要改变不可变项目时,如果该项目是定义成相对(Relative)的,数值0表示不改变数据,如果不可变项目是定义成绝对(Absolute)的,超出范围外的数值则表示不改变数据。 1 Volatile 可变的:表示设备可以自己改变数值,并不是必须主机传送报表要求给设备来改变数值。例如设备控制面板可以由主机软件传送一个报表给设备,也可以由用户自己按设备上的实际按钮。 8① 0 BitField 位字段:表示每一个位或是一个字节内的一组位可以代表一份数据。 1 BufferedBytes 缓冲字节:表示信息包含一个或多个字节,缓冲字节的报表大小必须是8o 9~31位 保留注:①:该位不能应用到数组。②:只应用于Output和Feature项目,对于Input项目该位保留。(2)Collection和EndCollection项目所有的报表类型都可以使用 Collection与EndCollection项目来将相关的Main类型项目组成群组。这两个项目分别用于打开和关闭集合。所有在Collection与EndCollection项目之间的Main类型项目都是Collection 的一部分。Collection有3种类型:Application、Physical与Logical,其项目的数据项的值分别为1、0和2。厂商也可以自己定义Collection类型,数据项的值为80h~FFh保留给厂商定义。EndCollection项目无数据项。ApplicationCollection包含有共同用途的项目或执行单一功能的项目。例如键盘的开机描述符将键盘的按键与LED指示灯数据集合成一个ApplicationCollection。所有的报表必须在一个ApplicationCollection内。PhysicalCollection 包含在一个单一几何点上的数据项目,可以将每个位置的数据集合成一个PhysicalCollection。在设备报告多个传感器的位置的时候,使用PhysicalCollection 指明不同的数据来自不同的传感器。LogicalCollection 形成一个数据结构,包含由Collection所连结的不同类型的项目。例如数据缓冲区的内容以及缓冲区内字节数目的计数。(3)UsagePage和Usage项目Usagepage项目的数据部分为1~2个字节,目前的定义全部都是一个字节。UsagePage定义了常用的设备功能,关于UsagePage(以及其他项目)的具体定义内容,可以查阅HIDUsage tables(http://www.usb.org/developers/hidpage/#Class_Definition),下表是来自HIDUsagetables的UsagePage定义。表8-6UsagePage定义 PageID PageName 00 Undefined 01 GenericDesktopControls 02 SimulationControls 03 VRControls 04 SportControls 05 GameControls 06 GenericDeviceControls 07 Keyboard/Keypad 08 LEDs 09 Button 0A Ordinal 0B Telephony 0C Consumer 0D Digitizer 0E Reserved 0F PIDPage 10 Unicode 11-13 Reserved 14 AlphanumericDisplay 15-3f Reserved 40 MedicalInstruments 41-7F Reserved 80-83 Monitorpages 84-87 Powerpages 88-8B Reserved 8C BarCodeScannerpage 8D Scalepage 8E MagneticStripeReading(MSR)Devices 8F ReservedPointofSalepages 90 CameraControlPage 91 ArcadePage 92-FEFF Reserved FF00-FFFF Vendor-defined关于UsagePage的每一个有效定义项,都有一个相应的下一级定义,如 UsagePage的数据项数值为1,则设备定义为GenericDesktopControls,关于该类设备的具体功能可以在HIDUsageTables中查到具体的定义。下表是HIDUsageTables中对GenericDesktopControls设备的功能定表8-7GenericDesktopControls 用法定义 UsageID UsageName UsageType 参阅HIDUsageTables中的相关章节 00 Undefined 01 Pointer CP 4.1 02 Mouse CA 03 Reserved 04 Joystick CA 4.1 05 GamePad CA 06 Keyboard CA 07 Keypad CA 08 Multi-axisController CA 09 TabletPCSystemControls CA 0A-2F Reserved 30 X DV 4.2 31 Y DV 32 Z DV 33 Rx DV 34 Ry DV 35 rRZ- DV 36 Slider DV 37 「Dial DV 4.3 38 Wheel DV 39 Hatswitch DV 3A CountedBuffer CL 4.6 3B ByteCount DV 3C MotionWakeup OSC 3D Start OOC 4.3 3E PSelect OOC 3F Reserved 40 Vx DV 41 Vy DV 42 Vz DV 43 Vbrx DV 4.3.1 44 Vbry DV 45 Vbrz DV 46 Vno DV 47 FeatureNotification DV,DF 4.8 48 ResolutionMultiplier DV 49-7F Reserved 80 SystemControl CA 4.5 81 SystemPowerDown OSC 82 SystemSleep OSC 4.5.1 83 SystemWakeUp OSC 84 SystemContextMenu OSC 85 SystemMainMenu OSC 86 SystemAppMenu OSC 87 SystemMenuHelp OSC 88 「SystemMenuExit OSC 4.5 89 SystemMenuSelect OSC 8A SystemMenuRight RTC 8B SystemMenuLeft RTC 8C SystemMenuUp RTC 8D SystemMenuDown RTC 8E ,SystemColdRestart OSC 4.5.1 8F 「SystemWarmRestartOSC 90 D-padUp OOC 91 D-padDown OOC 4.7 92 D-padRight OOC 93 D-padLeft OOC 94-9F Reserved A0 ,SystemDock OSC A1 ,SystemUndock OSC 4.5.1 A2 SystemSetup OSC A3 SystemBreak OSC A4 SystemDebuggerBreak OSC 4.9 A5 ApplicationBreak OSC A6 ApplicationDebuggerBreak OSC A7 ,SystemSpeakerMute OSC 4.5.1 A8 ,SystemHibernate OSC A9-AF Reserved B0 SystemDisplayInvert OSC B1 .SystemDisplayInternal OSC 4.10 B2 SystemDisplayExternal OSC B3 SystemDisplayBoth OSC B4 SystemDisplayDual OSC B5 SystemDisplayToggleInt/Ext OSC B6 SystemDisplaySwapPrimary/Secondary OSC B7 SystemDisplayLCDAutoscale OSC B8-FFFF Reserved 用法(Usage)定义了各种各样设备特性,对于UsagePage的每一项都定义了常用的各种用法。用法说明了3种信息,即控制、集合和数据。控制说明设备的状态,如on/off、Enable/Disable等。集合说明控制和数据的组合关系。上表中的用法类型(UsageType)描述了应用程序如何处理由Main类型项目生成的数据,具体的定义和详细说明请参阅 HIDUsageTables。(4)ReportID项目ReportID放在信息包中报表数据之前,设备可以支持多个相同类型的报表,每一个报表包含不同的数据与其特有的ID。在报表描述符中,ReportID项目作用于其后续所有的项目,直到遇到下一个ReportID为止。如果报表描述符中没有ReportID项目,默认的ID值是0,描述符不能定义一个为0的ReportID,输入报表、输出报表与特征报表可以分享同一个ReportID。在Set_Report和Get_Report请求传输中,主机在设置事务的wValue字段的低字节中指定一个ReportID。在中断传输中如果接口支持一个以上的ReportID,ReportID必须是传送报表中的第一个字节。如果接口只支持数值为0的默认ReportID,此ReportID不应该在中断传输中随着报表一起传送。(5)LogicalMinimum和LogicalMaximum项目LogicalMinimum与LogicalMaximum项目定义报表的变量( Variable)或阵列(Array)数据的限制范围,此限制范围以逻辑单位来表示。例如设备报表的一个电流值读数是500mA,而一个单位是 2mA,则LogicalMaximum值等于250。负数值以2的补码来表示。如果LogicalMinimum与LogicalMaximum都是正数,就不需要有正负号位。不管LogicalMinimum与LogicalMaximum是以有正负号或是无正负号的数值来表示,设备都可以正确地传输数据。数据的接收者必须知道数据是否可以是负值。(5)PhysicalMinimum和PhysicalMaximum项目PhysicalMinimum和PhysicalMaximum项目定义数值的限制范围,该限制范围使用Unit项目定义的单位来表示。上例中设备报表的一个电流值读数是500mA,单位是2mA,LogicalMaximum值等于250,而PhysicalMaximum值是500。LogicalMinimum与LogicalMaximum值说明了设备返回数值的边界,可以根据PhysicalMinimum和PhysicalMaximum值对数据进行偏移和比例变换。(6)UnitExponent项目UnitExponent项目定义了在使用逻辑范围和实际范围将设备的返回数值转换成实际数值时,使用10的多少次方对数值进行定标。UnitExponent的值的编码为4位补码,代表10的指数范围是-8~+7。表8-8UnitExponent数值表 代码 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0FH 数值 0 1 2 3 4 5 6 7 -8 -7 -6 -5 -4 -3 -2 -1根据以上5个项目的值可以换算出报表传送数据(逻辑数据)与物理数据的转换关系。物理数据值=逻辑数据值+分辨率分辨率=(LogicalMaximum-LogicalMinimum)+((PhysicalMaximum-PhysicalMinimum)x10UnitExponent)(7) Unit项目Unit项目指定报表数据在使用 Physical与UnitExponent项目转换后使用什么度量单位,以及单位的藉指数值。Unit的数值部分可以长达4字节,按照4位为一段分段,可以分为8个半字节段,由高到低分别为半字节7、半字节6、…、半字节0。每一个半字节对应不同的基本单位,其数值表示单位的指数值,采用4位2的补码表示,取值范围是-8~"7之间。从半字节0~6由下表给出了具体的定义,其中半字节0表示测量系统,半字节7保留。例如在半字节0数值为1(表示米用线性公制测量系统)的条件下,半字节1表示长度(单位为厘米),如果其数值为1表示厘米,数值为2表示(厘米)2,成为面积单位。半字节3表示时间(单位为秒),如果其数值为-2,表示(秒)-2。表8-9Unit单位的定义 半字节序号 测量项目 数值含义 0 1 2 3 4 0 测量系统 无 线性、公制 角度、公制 线性、英制 角度、英制 1 长度 厘米 半径 英寸 度 2 质量 克 石拉(slug) 3 时间 秒 4 温度 开式度(Kelvin) 华式度 5 电流 安培 6 亮度 烛光 7 保留 虽然表中只是定义了有限的基本单位,但可以通过这些基本单位的组合派生出大多数其它的常用单位。例如报表使用一个字节传送一个从 -20到110华氏度温度值,可以定义以下报表描述项目:LogicalMinimum=-128LogicalMaximum=127PhysicalMinimum=-20PhysicalMaximum=110UnitExponent=0Unit=30003hUnit的半字节0=3选择英制线性测量系统,半字节4=3选择华氏温度单位。130(110+20)华氏度的数值范围线性分布到了256和有效数值区域,每一位相当于0.51华氏度,这样就提高了分辨率。(8)ReportSize和ReportCount项目ReportSize项目指定Input、Output与Feature项目字段的大小,以位为单位。ReportCount项目指定Input、Output与Feature项目包含的字段数目。例如两个8位的字段,ReportSize等于8,而ReportCount等于2。8个1位的字段,ReportSize等于1,而ReportCount等于8。Input、Output与Feature项目报表可以有多个项目,每一个项目可以有自己的ReportSize和ReportCount项目。(9)Push和Pop项目Push项目将一个Global项目状态表格的副本压入CPU的堆栈内。GIobal项目状态表格包含所有之前定义的Gobal项目的目前设置。Pop项目恢复之前压入堆栈的Global项目状态的储存状态。(10)Usage、UsageMinimum和UsageMaximum项目这3个项目输入Local类型项目。Usage项目和Global类型的UsagePage项目协同描述项目或集合的功能。一个报表可以指定一个Usage给许多个控制,或是指定不同的Usage给每一个控制。如果一个报表项目之前有一个 Usage,此Usage应用到该项目的所有控制。如果一个报表项目之前有一个以上的 Usage,每一个Usage应用到一个控制,Usage与控制是按顺序结合的O例如下面报表描述符的一个局部,报表含有2个输入字节,第一个字节的用法是x,第2个字节是V。ReportSize(8)ReportCount(2)Usage(x)Usage(y)Input(Data,Variable,Absolute)如果一个报表项目之前有一个以上的Usage,而且控制的数目多于Usage的数目,每一个Usage与一个控制对应,最后一个Usage则应用到所有剩余的控制。例如在下面报表包含16个字节输入数据,第一个字节对应用法x,第2个字节对应用法V,剩余的14个字节对应厂商定义的用法。Usage(x)Usage(y)Usage(Vendordefined)ReportSize(8)ReportCount(16)Input(Data,Variable,Absolute) UsageMinimum和UsageMaximum可以指定一个Usage给多个控制或是数组项目。将从UsageMinimum至UUsgaeMaximun定义的用法顺序对应到多个控制例如在一个键盘描述符中定义的标准键盘的左、右修饰键的输入项目中,使用一个字节的8位分别输入键盘的左、右Ctrl键、Shift键、Alt键和GUI键,从HIDUsagetables文档中的第10节可以查到关于键盘用法的定义,其中上述8个修饰键的用法定义值为224到231。以下是报表描述符的修饰键部分描述。UsagePage(1)Usage(6)Collection(1)UsagePage(7)UsageMinimum(224)UsageMaximum(231)LogicalMinimum(0)LogicalMaximum(1)ReportSize(1)ReportCount(8)Input(Data,Variable,Absolute);1=GenericDesktopControls;6=Keyboard;1=Application;7=Keyboard/Keypad8.3USB接口的键盘描述符范例下面作为一个例子,介绍一个USB接口的101键盘的全部描述符。该键盘固件部分由一个微处理器实现全部控制功能,下面列出的代码为微处理器汇编实现描述符定义。8.3.1设备的描述符设备描述符的代码如下 ; =;Devicedescriptor —设备描述符 ; =DEVICE_DESC_DATA: — DB0x12 bLength=18 ,该描述符长度为18字节 DB0x01 bDescriptorType=01 ,表明是设备描述符 DB0x10,0x01 bcdUSB,USB设备版本号=1.1 DB0x00 DeviceClass ,设备类码,HID设备为0,类别在接口描述符中定义 DB0X00 DeviceSubClass ,设备子类码,DeviceClass 为0时该字段必须为0 DB0X00 bDevicePortocol ,协议码,DeviceClass 为0时该字段必须为0 DB0x08 bMaxPacketSize0 ,端点0的最大包尺寸 DB0xFF,0xFF bVendor,厂商ID,由USB实现者论坛确定的 DB0x01,0x00 bProduct,产品ID DB0x00,0x01 bcdDevice ,设备版本号为1.00 DB0x04 iManufacturer ,厂商字符串的索引值,见字符串描述符 DB0x0E iProduct ,产品字符串的索引值,见字符串描述符 DB0x30 iSerialNumber ,产品序列号字符串的索引值,见字符串描述符 DB0X01 bNumConfigurations ,配置数目只有1个8.3.2配置描述符配置描述符的代码如下;Configurationdescriptor 配置才苗述符 CONFIG_DESC_DB0x09DB0x02DB0x3B,0x00DB0x02DB0x01DB0x00DB0XA0 _DATA:;bLength=9 ,该描述符长度为9字节;bDescriptorType=02 ,表明是配置描述符;wTotalLength=59 ,配置、接口、端点和HID描述符的总和字节数;bNumInterfaces=2 ,本配置支持的接口数目为 2个;bConfigurationValue =1,本配置描述符的标识符;iCongfiguration=0 ,配置描述符说明字符串的索引值, 0表示无;bmAttributes ,电源及唤醒设置,USB1.1版中D7=1,D6=0表示总线供电; D5=1 表示支持远程唤醒 DB0X32 ;MaxPower=50 ,本设备最大耗电为50X2mA=100mA需要说明的是wTotalLength的值,该数值为配置描述符长度(9)加上后续的键盘的接口描述符长度(9)、端点描述符长度(7)、HID描述符长度(9),以及该配置下的鼠标的接口描述符长度(9)、端点描述符长度(7)、HID描述符长度(9),共59个字节。关于鼠标的相关描述符在下面的叙述中省略了。8.3.3接口描述符接口描述符的代码如下 ; =;Interfacedescriptor —接口描述符— ; =InterfaceDescriptor0:DB0x09 ;bLength=9 ,该描述符长度 DB0x04 ;bDescriptorType=4 ,表明是接口描述符 DB0x00 ;bInterfaceNumber=0 ,此接口的识别标识符 DB0x00 ;bAlternateSetting=0 ,表示此接口无替代设置值 DB0x01 ;bNumEndpoints=1 ,本接口的端点数目,HID设备使用端点1 DB0x03 ;bInterfaceClass=3 ,表示该设备是HID类别 DB0x01 ;bInterfaceSubClass=1 ,表示支持启动接口 DB0x01 ;bInterfaceProtocol=1 ,表示支持键盘协议 DB0x00 ;iInterface=0 , 接口描述符说明字符串的索引值, 0表示无字符串8.3.4HID描述符HID描述符的代码如下 ; ;HIDdescriptorHID —描述符 ; HIDDescriptor0: — DB0x09 ;bLength=9 ,该描述符长度 DB0x21 ;bDescriptorType=21h ,表明是HID描述符 DB0x00,0x01 ;bcdHID=0100 ,HID规范版本为1.00 DB0x00 ;bCountryCode=0 ,硬件设备所在国家的国家代码, 0表示未指明 DB0x01 ;nNumDescriptors=1 ,表示支持的描述符有1个,即一个报表描述符 DB0x22 ;bDescriptorType=22h ,描述符类别,表示支持的描述符是报表描述符 DB0x3F,0x00 ;wDescriptorLength=63 ,表示支持的报表描述符的长度8.3.5端点描述符端点描述符的代码如下 ; ;EndPointdescriptor —端点描述符 ; EndpointDescriptor0: — DB0x07 ;bLength=7 ,该描述符长度 DB0x05 ;bDescriptorType=5 ,表明是端点描述符 DB0x81 ;bEndpointAddress=10000001b ,表示1号输入端点 DB0X03 ;bmAttributes=00000011b ,表示中断类型端点 DB0x08,0x00 ;wMaxPacketSize=8,端点发送和接收的最大包尺寸为 8 DB10 ;blnterval=10 ,表示中断端点轮询时间间隔为 10ms8.3.6字符串描述符字符串描述符的代码如下;Stringdescriptor 字符串描述符;=========================================StringDescriptor0:DB0x04 ;bLength=4 ,字符串描述符0的长度为4DB0x03 ;bDescriptorType=3 ,表明是字符串描述符DB0x09,0x00 ;wLANGID=0009h ,表明是英语DB0x0A ;bLength=10 ,字符串描述符的长度为 10DB0x03 ;bDescriptorType=3 ,表明是字符串描述符DB0x41,0x00,0x43,0x00,0x4D,0x00,0x45,0x00;bString= ACME:",Unicode编码的字符串DB0x22 ;bLength=34 ,字符串描述符的长度为 34DB0x03 ;bDescriptorType=3 ,表明是字符串描述符DB0x4C,0x00,0x6F,0x00,0x63,0x00,0x61,0x00DB0x74,0x00,0x6F,0x00,0x72,0x00,0x20,0x00DB0x4B,0x00,0x65,0x00,0x79,0x00,0x62,0x00DB0x6F,0x00,0x61,0x00,0x72,0x00,0x64,0x00;bString=LocatorKeyboard:Unicode编码的字符串DB0x0E ;bLength=14 ,字符串描述符的长度为 14DB0x03 ;bDescriptorType=3 ,表明是字符串描述符DB0x41,0x00,0x42,0x00,0x43,0x00,0x21,0x00DB0x22,0x00,0x23,0x00;bString=ABC123';Unicode编码的字符串8.3.7报表描述符报表描述符的代码如下;HIDReportsDescriptor 报表描述符 DB0x05,1 UsagePage(1:GenericDesktop) DB0x09,6 Usage(6:Keyboard) 表示报表定义的是HID键盘 DB0xA1,1 Collection(1:Application) 集合开始 以下定义了键盘的修饰键输入报表,共有 8个键,组成一个字节 用法见HIDUsageTable 中的第10节中的键盘用法定义 DB0x05,7 Usagepage(7:KeyCodes) DB0x19,224 UsageMinimum(224) DB0x29,231 UsageMaximum(231) DB0x15,0 LogicalMinimum(0) DB0x25,1 LogicalMaximum(1) DB0x75,1 ReportSize(1) DB0x95,8 ReportCount(8) DB0x81,2 Input(Data,Variable,Absolute) 以下定义了一个保留字节的输入报表 DB0x95,1 ReportCount(1) DB0x75,8 ReportSize(8), DB0x81,1 Input(Constant)=ReservedByte 以下定义了键盘的LED指示灯输出报表项目,共有5个指示灯 用法见HIDUsageTable 中的第11节中的LED用法定义 DB0x95,5 ReportCount(5) DB0x75,1 ReportSize(1) DB0x05,8 UsagePage(Page#forLEDs) DB0x19,1 UsageMinimum(1) DB0x29,5 UsageMaximum(5) DB0x91,2 Output(Data,Variable,Absolute) 以下定义了3个填充位,与前面的5个LED指示灯数据组成一个完整的字节 DB0x95,1 ReportCount(1) DB0x75,3 ReportSize(3) DB0x91,1 Output(Constant) 以下定义了键盘的按键值输入报表项目, 共6个字节,存放键编号(0~101) 用法见HIDUsageTable 中的第10节中的键盘用法定义 ; 这样的设计可以允许一次输入 6个按键的键值 DB0x95,6 ReportCount(6) DB0x75,8 ReportSize(8) DB0x15,0 LogicalMinimum(0) DB0x25,101 LogicalMaximum(101) DB0x05,7 UsagePage(7:KeyCodes) DB0x19,0 UsageMinimum(0) DB0x29,101 UsageMaximum(101) DB0x81,0 Input(Data,Array) DB0xC0 End_Collection 集合结束通过上面的报表描述符的定义,确定了键盘的输入报表和输出报表的数据格式。其中输入报表共8个字节,输出报表只有1个字 键值6 键值5 键值4 键值3 键值2 键值1 保留 修饰键图8-4 键盘的输入报表格式字节1字节0字节7字节6字节5字节4字节3字节28.4HID的特定请求除了USB设备的11个标准请求外,HID规范另外还定义了6个HID特定控制请求。所有的HID设备都必须支持Get_Report请求)同时支持启动的设备必须支持Get_Protocol请求和Set_Protocol请求,其他的请求是可选择的。如果设备没有中断-输出端点,此设备需要支持Get_Report请求来从主机读取数据。在控制传输的设置阶段的数据包中的8个字节中的第一字节bmRequestType的编码含义参阅第6章中的USB标准请求。第2个字节bRequest定义请求的内容。wValue因请求的不同而不同。wIndex指明HID所在的接口。 表8-10HID特定的请求 bmRequestType bRequest(值) wValue wIndex wLength 数据阶段 10100001b Get_Report(1) 报表类型,报表ID 接口 报表长度 报表 00100001b Set_Report(9) 报表类型,报表ID 接口 报表长度 报表 10100001b Get_Idle(2) 0,报表ID 接口 1 闲置时间 00100001b Set_Idle(10) 闲置时间,报表ID 接口 0 无 10100001b Get_Protocol(3) 0 接口 1 0:启动协议1:报表协议 00100001b Set_Protocol(11) 0:启动协议1:报表协议 接口 0 无8.4.1Get_Report请求Get_Report的作用是启用主机使用控制传输,来从设备读取数据。在使用时wValue字段的高字节是报表类型,1表示Input报表,2表示Output报表,3表示Feature报表。wValue的低字节是报表的ReportID,如果没有定义ReportID,该字节为设0。在携带请求的控制传输的数据阶段, HID设备回传指定的报表内容。HlD规范不建议使用该请求获得未经定时的数据,这样的数据建议使用中断输入管道获得。该请求用来取得在主机初始化设备时的特征项目状态和其他信息。使用开机协议的主机可以使用此请求来获得按键或鼠标数据。8.4.2Set_Report请求Set_Report请求的参数含义和Get_Report一样,但Set_Report请求的数据方向与Get_Report相及,在后面的数据阶段,主机传亲报表到HID设备,这样的输出报表可以用于复位设备的控制,复位产生的效果取决于对应的控制的类型是相对(Reletive)的还是绝对(Absolute)的。8.4.3Set_Idle请求Set_Idle请求的作用是静默一个在中断输入管浦的特定的报表,直到一个发生一个相关的事件或过去了规定的时间,当数据从上一个报表后没有改变时,可以通过限制中断输入端点的报表频率来节省传输带宽。 HID设备不是必需支持此请求。wValue字段的高字节是设置的闲置时间,是报表之间的最大间隔时间。该字节为 0表示闲置时间为无限长,在这种情况下,设备只有在报表数据有改变时才传送报表,否则设备传回一个NAK。wValue字段的低字节指示此请求应用的报表的ReportID。如果低字节是0,此请求应用到设备的所有输入报表。闲置时间以4ms为单位,范围在4ms〜1020ms之间。如果报表的数据自从上一次报表后有改变,或是接收到一个请求,设备会传送一个报表。如果报表的数据没有改变,而且从上一次报表后过去的时间自尚未达到规定的闲置时间,设备会传回一个NAK。如果报表的数据没有改变,而且持续时间已经达到的闲置时间,设备会传送一个报表。闲置时间设置为0表示无限长的闲置时间,设备只有在报表的数据有改变时才会传送一个报表,对于其他的中断输入请求则是传回NAK。在检测HID设备时,Wndows的HID驱动程序会试图将闲置时间设置成0。如果HID设备不支持此请求,主机会收到传回的Stall<8.4.4Get_Idle请求Get_Idle请求的作用是过的设备的当前闲置时间,在数据阶段,HID设备回传一个字节的闲置时间值。8.4.5Get_Protocol请求Get_Protocol 请求的作用是主机获取设备目前作用的是启动协议还是报表协议。在数据阶段中设备回传的1个字节信息包中的数据值为0表示启动协议,为1表示报表协议。启动设备必需支持该请求。8.4.6Set_Protocol请求Set_Protocol的作用是主机指定设备使用启动协议或报表协议。在数据阶段中主机传送的1个字节信息包中的数据值为0表示指定启动协议,为1表示指定报表协议。启动设备必需支持该请求。8.5HID程序设计HID设备是Windows系统提供了完善支持的一类,应用程序可以通过标准的 API函数调用,实现与HID设备的通信。Windows系统提供了几千个API函数,作为应用程序与操作系统的接口,与HID相关的API函数被封装在hid.dll、setupapi.dll和kernal32.dll文件中。8.5.1HID访问使用的API函数文件hid.dll中提供了很多个API,因为与HID设备通信有一定的复杂性。首先,在应用程序与HID传输数据之前,应用程序必须先识别该设备并且读取它的报表信息,这些动作需要调用多个API函数才可以实现。应用程序需要寻找连接到系统上的是哪些 HID设备,然后读取每个设备的信息直到查找到所需的属性。如果是客户化的设备,应用程序可以寻找特定的厂商与产品ID,或者应用程序可以寻找特定类型的设备,例如键盘或鼠标。表8-11用于HID设备的API函数 用于了解HID设备情况的API函数(hid.dll) HidD_GetAttributes 请求获得HID设备的厂商ID、产品ID和版本号 HidD_FreePreparsedData 释放函数HidD_GetPreparsedData所使用的资源 HidD_GetHidGuid 请求获得HID设备的GUID HidD_GetIndexString 请求获律由索引识别的字符串 HidD_GetManufactureString 请求获得设备制造商字符串 HidD_GetPhysicalDescriptor 请求获得设备实体字符串 HidD_GetPreparsedData 请求获得与设备能力信息相关的缓冲区的代号 HidD_GetProductString 请求获得产品字符串 HidD_GetSerialNumberString 请求获得产品序列号字符串 HidP_GetButtonCaps 请求获得HID报表中所有按钮的能力 HidP_GetCaps 请求获得用于描述设备能力的结构的指针 HidP_GetLinkCollectionNotes 请求获得描述在顶层集合中的连接集合 (LinkCollection)关系的结构的数组 HidP_GetSpecificButtonCaps 请求获得报表中按钮的能力,该请求可以设定一个 UsagePage、Usage或是LinkCollection HidP_GetSpecificValueCaps 请求获得报表中数值的能力,该请求可以设定一个 UsagePage、Usage或是LinkCollection HidP_GetValueCaps 请求获得HID报表中所有数值的能力 HidP_MaxUsageListLength 请求获得HID报表中可以回传的按钮的最大数目,该请求可以设定一个UsagePage HidP_UsageListDifference 比较两个按钮列表,并且求出在一个列表中设定而在另一个列表中没有设定的按钮 用于从设备读取、向设备传送报表的 API函数(hid.dll) HidD_GetFeature 从设备读取一个特征报表 HidD_SetFeature 向设备传送一个特征报表 HidP_GetButtons 从设备读取包含每个按下的按钮的用法(Usage)的缓冲区的指针,该请求可以设定一个 UsagePage HidP_GetButtonEx 从设备读取包含每个按下的按钮的 Usage和UsagePage的缓冲区的指针 HidPGetScaledUsageValue 从设备读取一个已经经过比例因子调整的有符号数值 HidP_GetUsageValue 从设备读取一个指向数值的指针 HidP_GetUsageValueArray 从设备读取包含多个数据项的 Usage的数据 HidP_SetButtons 向设备传送设置按钮的数据 HidP_SetScaledUsageValue 将一个实际数值转换成设备使用的逻辑数值, 并将其插入到报表中 HidP_SetUsageValue 向设备传送数据 HidP_SetUsageValueArray 向设备传送包含多个数据项的 Usage的数据 HidD_FlushQueue 清空输入缓冲区 HidD_GetNumInputBuffer 获得驱动程序用于存储输入报表的环形缓冲区的大小, 默认值是8 HidD_SetNumInputBuffer 设置驱动程序用于存储输入报表的环形缓冲区的大小 用于查找和识别设备的API函数(setupapi.dll) SetupDiGetClassDevs 获得HID的信息,针对已安装的设备,回传一个指向其信息集的代码 SetupDiEnumDeviceInterfaces 请求获得设备信息群内的一个设备的信息 SetupDiGetDeviceInterfaceDetail 请求获得设备的路径 SetupDiDestroyDeviceInfoList 释放SetupDiGetClassDevs使用的资源 用于打开、关闭设备和实现数据传送的 API函数(kernal32.dll) CreatFile 取得设备的路径后、调用该函数获得设备代号 WriteFile ,向设备传送输出报表 ReadFile 从设备读取输入报表 CloseHandle 关闭设备、释放CreateFile所彳吏用的资源8.5.2查找HID的过程在实现HID的访问之前,首先要查找指定(根据设备的厂商ID、产品ID和产品序列号)的HID。查找指定设备的过程如下:?调用函数HidD_GetHidGuid获得USB设备的GUID;?调用函数SetupDiGetClassDevs,获得一个包含全部HID信息的结构数组的指针,下面根据此数组逐项查找指定的HID;?调 用 函 数SetupDiEnumDeviceInterfaces,填写SP_DEVICE_INTERFACE_DATA结构的数而项,该结构用于识别一个HID设备接口;?调 用 函 数SetupDiGetDeviceInterfaceDetail,获得一个指向该设备的路径名;?调用函数CreateFile,获得设备句柄;?调用函数HidD_GetAttributes,填写HIDD_ATTRIBUTES结构的数据项,该结构包含设备的厂商ID、产品ID和产品序列号,比照这些数值确定该设备是否是查找的设备。查找HID的流程如下图下面介绍VB实现的查找过程。(1)获得GUID应用程序要与HID设备通信之前,必须先获得HID类别的GUID(GloballyUniqueIndentifer)。GUID是一个128位的数值,每个对象都有惟一的GUID。HID类别的GUID包含在hidclass.h文档内,可以接引用,或是使用HidD_GetHidGuid 函数来取得HID类别的GUID。‘函数声明PublicDeclareFunctionHidD_GetHidGuidLib"hid.dll"(ByRefHidGuidAsGUID)AsLong'GUID结构说明PublicTypeGUIDDatalAsLongData2AsIntegerData3AsIntegerData4(7)AsByteEndType'变量说明DimHidGuidasGUID'调用CallHidD_GetHidGuid(HidGuid)(2)获得HID的结构数组得到GUID后调用SetupDiGetClassDevs 函数传回所有已经连接并且检测过的HID包含其信息的结构数组的地址。‘函数声明PublicDeclareFunctionSetupDiGetClassDevsLib"setupapi.dll"Alias"SetupDiGetClassDevsA”(—ByRefClassGuidAsGUID,_ByValEnumeratorAsString,_ByValhwndParentAsLong,_ByValFlagsAsLong) AsLong'常量说明PublicConstDIGCF_PRESENT=&H2PublicConstDIGCF_DEVICEINTERFACE=&H10 '调用hDevInfoSet=SetupDiGetClassDevs(HidGuid,_ 通过HidD_GetHidGuidvbNullString,_0,_(DIFCG_PRESENTorDIFCG_DEVICEINTERFACE)_) 函数获得GUID该函数的ClassGuid参数值为通过HidD_GetHidGuid函数获得GUID。Enumerator与hwndParent参数没有用到,Flags参数是两个定义在setupapi.h文档内的系统常数。代码中的Flags常数告诉SetupDiGetClassDevs 函数只寻找目前存在(连接并且检测过)的设备接口,并且是HID类别的成员。返回值hDevlnfoSet是包含所有连接并且检测到的全部HID的信息的结构数组的地址。在程序中并不需要访问hDevInfoSet的元素,只需要将hDevlnfSet值传给SetupDiEnumDeviceInterfaces函数即可。当程序不再需要hDevInfoSet指向的数组时,应该调用SetupDiDestroyDeviceInfoList函数来释放资源。下面在hDevInfoSet指向的结构数组中查找。(3)识别HID接口接下来调用SetupDiEnumDevicelnterfaces,填写SP_DEVICE_INTERFACE_DATA结构的数 据项,该结构用于识别一个 HID设备接口。 '函数声明PublicDeclareFunctionSetupDiEnumDeviceInterfacesLib"setupapi.dll"(ByValDeviceInfoSetAsLong,_ByValDeviceInfoDataAsLong,_ByRefInterfaceClassGuidAsGUID,_ByValMemberIndexAsLong,_ByRefDeviceInterfaceDataAs"Sp_DEVICE_INTERFACE_DATA_)AsLong '结构定义PublicTypeSP_DEVICE_INTERFACE_DATAcbSizeAsLongInterfaceClassGuidAsGUIDFlagsAsLongReservedAsLongEndType '变量说明DimResultasLongDimMemberIndexasLongDimMyDeviceInterfaceDataasSP_DEVICE_INTERFACE_DATA '调用MyDeviceInterfaceData.cbSize=LenB(MyDeviceInterfaceData)MemberIndex=0Result=SetupDiEnumDeviceInterfaces(DeviceInfoSet,_ 'SetupDiGetClassDevso,_ -HidGuid,_ 通过HidD_GetHidGuidMemberIndex,_ 第一次调用设置为0MyDeviceInterfaceData_) - 的返回值函数获得的GUID参数cbSize是SP_DEVICE_INTERFACE_DATA结构的大小,以字节另:单位。在调用SetupDiEnumDevicelnterfaces 函数之前,cbSize必须储存在结构内来当做传递的参数。参数HidGuid和DeviceInfoSet是函数之前的传回值。DeviceInfoData 是SP_DEVICE_INTERFACE_DATA结构的指针,用来恒制检测特定设备的接口。MemberIndex是DeviceInfoSet数组的索引值,在遍历整个数组的循环中MemberIndex 递增。MyDeviceInterfaceData是回传的结构,用来识别HID的一个接口。(4)获得设备路径下面通过调用SetupDiGetDeviceInterfaceDetail函数用来获得另外一个结构:SP_DEVICE_INTERFACE_DETAIL_DATA。此结构与前一个函数SetupDiEnumDeviceInterfaces 所识别的设备涂口有关。结构的DevicePath成员是一个设备路径,应用程序可以用此路径来实现与该设备的通信。'函数声明:PublicDeclareFunctionSetupDiGetDevicelnterfaceDetailLib"setupapi.dll"_Alias"SetupDiGetDeviceInterfaceDetailA"(_ByValDeviceInfoSetAsLong,_ByRefDeviceInterfaceDataAsSP_DEVICE_INTERFACE_DATA,_ByValDeviceInterfaceDetailDataAsLong,_ByValDeviceInterfaceDetailDataSizeAsLong,_ByRefRequiredSizeAsLong,_ByValDeviceInfoDataAsLong_)AsLong'结构声明PublicTypeSP_DEVICE_INTERFACE_DETAIL_DATAcbSizeAsLongDevicePathAsByteEndType'变量定义DimNeededasLong,DetailDataaslongDimMyDeviceInterfaceDetailDataAsSP_DEVICE_INTERFACE_DETAIL_DATADimDetailDataBuffer()asByteDimDevicePathNameAsString'调用MyDeviceInterfaceData.cbSize=LenB(MyDeviceInterfaceData)Result=SetupDiGetDeviceInterfaceDetailA(DeviceInfoSet,_DeviceInterfaceData,_0,_ -0,_Needed,_0_一)DetailData=neededMyDeviceInterfaceDetailData.cbSize=Len(MyDeviceInterfaceDetailData)ReDimDetailDataBuffer(Needed)CallRtlMoveMemory(DetailDataBuffer(0),MyDeviceInterfaceDetailData,4)Result=SetupDiGetDeviceInterfaceDetailA(DeviceInfoSet,_DeviceInterfaceData,_VarPtr(DetailDataBuffer(0)),_DetailData,_Needed,_0_)'转换成字符串,再转换成 Unicode并从中删除4个字符得到设备路径DevicePathName=Cstr(DetailDataBuffer())DevicePathName=StrCovn(DevicePathName,vbUnicode)DevicePathName=Right$(DevicePathName,Len(DevicePathName)-4)DeviceInterfaceDetailDataSize包含DevicelnterfaceData 结构的大小,以字节为单位。但是,在调用SetupDiGetDevicelnterfaceDetail函数之前无法知道此数值的大小,如果没有DeviceInterfaceDetailDataSize函数,SetupDiGetDeviceInterfaceDetail函数不会传回所需的结构。这个问题的解决方式是两次调用SetupDiGetDeviceInterfaceDetail函数。第一次调用时GetLastError函数会传回错误信息,即:Thedataereapassedtoasystemcallistoosmall,但是RequireSize参数会包含正确的DeviceInterfaceDetailDataSize数值。再次调用时传递此传回值,函数就会执行成功。(5)获得设备句柄取得设备的路径以后,就可以准备开始与设备通信。使用CreateFile来打开一个HID设备,并且取得此设备的句柄,使用设备的句柄来与设备交换数据。当不再需要访问此设备时,应该调用CIoseHandle函数来关闭设备并释放系统资源。'函数声明:PublicDeclareFunctionCreateFileLib"kernel32"Alias"CreateFileA"(_ByValIpFileNameAsString,_ByValdwDesiredAccessAsLong,_ByValdwShareModeAsLong,_ByReflpSecurityAttributesAsLong,_ByValdwCreationDispositionAsLong,_ByValdwFlagsAndAttributesAsLong,_ByValhTemplateFileAsLong_)AsLong'常量定义PublicConstGENERIC_READ=&H80000000PublicConstGENERIC_WRITE=&H40000000PublicConstFILE_ShARe_READ=&H1PublicConstFILE_SHARE_WRITE=&H2PublicConstOPEN_EXIsTInG=3'调用HidDevice=CreateFile(_DevicePathName,_GENERIC_READOrGENERIC_WRITE,_(FILE_SHARE_READOrFILE_ShARE_WrITE),0,_open_existing,_0,_0)(6)获得设备的厂商ID、产品ID和产品序列号要识别一个设备是否是所要的设备,只要比较此设备的厂商与产品ID即可。HidD_GetAttributes 函数用来取得一个包含厂商与产品ID以及产品的版本号码的结构。‘函数声明PublicDeclareFunctionHidD_GetAttributesLib"hid.dll"(ByValHidDeviceObjectAsLong,_ByRefAttributesAsHIDD_ATTRIBUTES_)AsLong'结构说明PublicTypeHIDD_ATTRIBUTES SizeAsLongVendorlDAsIntegerProductIDAsIntegerVersionNumberAsIntegerEndType 厂商ID产品ID产品版本号'变量定义第8章USB接口HID设备231DimDeviceAttributesAsHIDD_ATTR旧UTES'调用DeviceAttributes.Size=LenB(DeviceAttributes)Result=HidD_GetAttributes(—HidDevice,_ '由CreateFile函数返回DeviceAttributes)HidDevice是由CreateFile函数所传回的设备句柄。如果CreateFile函数传回的是非零值,DeviceAttributes 结构就会填写正确值。应用程序可以由DeviceAttributes结构取得厂商ID、产品ID以及产品的版本号码。如果厂商与产品ID不是想查找的,应用程序应该使用CloseHandle函数来关闭该设备,然后移到下一个SetupDiEnumDeviceInterfaces 所检测到的下一个HlD继续查找。当应用程序检测到指定的设备或是检测完全部HID,它应该调用SetupDiDestroyDeviceInfoList函数来释放SetupDiGetClassDevs 函数所保留的资源。8.5.3获得HID的能力获得设备的能力是可以进一步了解 HID的手段,但不是必须的。如果在查找设备时,如果通过厂商ID、产品ID和产品序列号可以确定特定的设备,一般是已知设备的细节信息了。如果不通过厂商ID、产品ID和产品序列号确定设备,另一个方法是通过获得设备能力来确定设备。获得HID的能力的过程主要经过以下几个步骤:先使用HidD_GetPreparsedData函数获得一个包含设备能力信息的缓冲区的指针,调用HidP_GetCaps 获得描述HID的能力的数据结构;调用HidP_GetValueCaps 取得描述能力的数值。(1)获得描述HID能力的数据结构'函数声明PublicDeclareFunctionHidD_GetPreparsedDataLib"hid.dll"(_ByValHidDeviceObjectAsLong,_ByRefPreparsedDataAsLong)AsLong'变量定义DimPreparsedDataAsLong'调用Result=HidD_GetPreparsedData(HidDevice,_ '由CreateFile获得的设备句柄PreparsedData_) 一PreparsedData是一个包含数据的缓冲区的指针。程序并不需要访问缓冲区内的数据,只需要将缓冲区的开始地址传递给其他的API函数。当应用程序不再需要PreparsedData时,它应该调用HidD_FreePreparsedData函数来释放系统资源。接下来调用HidP_GetCaps,该函数传回一个结构,里面包含设有能力的信息,包括设备的UsagePage、Usage、报表长度以及按钮能力和数值能力等的数目。如果不使用厂商与产品ID来识另设备,HidP_GetCaps函数传回的设备能力信息可以帮助决定是否继续与此设备回巨。'函数声明PublicDeclareFunctionHidP_GetCapsLib"hid.dll"(ByValPreparsedDataAsLong,_ByRefCapabilitiesAsHIDP_CAPS)AsLong'结构定义PublicTypeHIDP_CAPSUsageAsIntegerUsagePageAsIntegerInputReportByteLengthAsIntegerOutputReportByteLengthAsIntegerFeatureReportByteLengthAsIntegerReserved(16)AsIntegerNumberLinkCollectionNodesAsIntegerNumberInputButtonCapsAsIntegerNumberInputValueCapsAsIntegerNumberInputDataIndicesAsIntegerNumberOutputButtonCapsAsIntegerNumberOutputValueCapsAsIntegerNumberOutputDataIndicesAsIntegerNumberFeatureButtonCapsAsInteger'用法'用法页'输入报表长度'输出报表长度'特征报表长度'由函数HidP_GetLinkCollectionNodes'返回的顶层集合连接数目'由函数HidP_GetButtonCaps返回的'包含输入按钮能力的结构 HIDP_BUTTON'CAPS的数目'由函数HidP_GetValueCaps返回的'包含输入数值能力的结构 HIDP_VALUE_'CAPS的数目'在输入报表中有关按键和数值的数据'指示器(DataIndices)的数目'由函数HidP_GetButtonCaps返回的'包含输出按钮能力的结构 HIDP_BUTTON'CAPS的数目'由函数HidP_GetValueCaps返回的'包含输出数值能力的结构 HIDP_VALUE_'CAPS的数目'在输出报表中有关按键和数值的数据'指示器(DataIndices)的数目'由函数HidP_GetButtonCaps返回的'包含特征按钮能力的结构 HIDPBUTTONNumberFeatureValueCapsAsIntegerNumberFeatureDatalndicesAsIntegerEndType'变量定义DimCapabilitiesAsHIDP_CAPS'CAPS的数目'由函数HidP_GetValueCaps返回的'包含特征数值能力的结构 HIDP_VALUE.'CAPS的数目'在特征报表中有关按键和数值的数据'指示器(DataIndices)的数目'调用Result=HidP_GetCaps(_PreparsedData,_Capabilities由函数HidD_GetPreparsedData定义)HidP_GetCaps函数填写C叩abilities结构中的数据项,Capabilities结构成员说明了HID的基本信息。这些信息包括:用法页和用法:UsagePage、Usage;输入、输出和特征报表长度:InputReportByteLength、OutputReportByteLength和FeatureReportByteLength;由函数HidP_GetLinkCollectionNodes返回的顶层藁合连接数目NumberLinkCollectionNodes在输入、输出和特征报表的按钮、数值和数据指示器的的数目。(2)获得描述HID数值能力的数据结构通过HidP_GetCaps获得的HID的基本能力信息不是能从设备得到全部信息,还可以调用另一个函数HidP_GetValueCap,该函数可以获得报表描述符中的数值和按钮的能力。HidP_GetValueCap返回的是包含了报表描述符中圣部数值信息的结构的指针。‘函数声明PublicDeclareFunctionHidP_GetValueCapsLib"hid.dll"(_ByValReportTypeAsInteger,_ByRefValueCapsAsByte,_ByRefValueCapsLengthAsInteger,_ByValPreparsedDataAsLong_)AsLong'ReportType常量定义PublicConstHidP_Input=0PublicConstHidP_Output=1PublicConstHidP_Feature=2PublicTypeHidP_Value_CapsUsagePageAsIntegerReportIDAsByteIsAliasAsLongBitFieldAsIntegerLinkCollectionAsIntegerLinkUsageAsIntegerLinkUsagePageAsIntegerIsRangeAsLongIsStringRangeAsLongIsDesignatorRangeAsLongIsAbsoluteAsLongHasNullAsLongReservedAsByteBitSizeAsIntegerReportCountAsIntegerReserved2AsIntegerReserved3AsIntegerReserved4AsIntegerReserved5AsIntegerReserved6AsIntegerLogicalMinAsLongLogicalMaxAsLongPhysicalMinAsLongPhysicalMaxAsLongUsageMinAsIntegerUsageMaxAsIntegerStringMinAsIntegerStringMaxAsIntegerDesignatorMinAsIntegerDesignatorMaxAsIntegerDatalndexMinAsIntegerDataIndexMaxAsIntegerEndType'变量定义DimValueCaps(1023)AsByte'调用Result=HidP_GetValueCaps(—HidP_Input,_ 一ValueCaps(0),_Capabilities.NumberInputValueCaps,_PreparsedData) ⑶输出报表到设备当应用程序取得HID设备的句柄,并且知道输出报表的字节数目后,它就可以传送输出报表给此设备。应用程序先将要传送的数据复制到一个缓冲区内,然后调用WriteFile函数。缓冲区的大小等于HidP_GetCaps函数返回的HIDP_CAPS结构的OutputReportByteLength属性值。这个大小值等于报表的字节大小,再加上一个字节的ReportID。ReportID是缓冲区的第一个字节。HlD驱动程序用来确定输出报表的传输类型,根据Windows的版本以及HID接口有无中断输出端点而定。应用程序不需要干预,低阶的驱动程序会自动处理。‘函数声明PublicDeclareFunctionWriteFileLib"kernel32"(_ByValhFileAsLong,_ByReflpBufferAsByte,_ByValnNumberOfBytesToWriteAsLong,_ByRefIpNumberOfBytesWrittenAsLong,_ByVallpOverlappedAsLong_)AsLong'变量定义DimSendBuffer()AsByteDimOutputReportData(7)AsByteDimCountasLong'调用ReDimSendBuffer(Capabilities.OutputReportByteLength-1)SendBuffer(0)=0 'ReportID'将准备的数据从OutputReportData复制到SendBufferForCount=1ToCapabilities.OutputReportByteLength-1SendBuffer(Count)=OutputReportData(Count-1)NextCountNumberOfBytesWritten=0Result=WriteFile(_HidDevice,_ '由CreateFile 函数返回的设备句柄SendBuffer(O),_ '输出报表缓存CLng(Capabilities.OutputReportByteLength),_ '要输出字节数NumberOfBytesWritten,_ '实际输出的字节数0 一)如果函数返回的Result数值不等于零,表示函数成功执行。如果接口只支持数值为0的ReportID,这个ReportID并不传送,但需要出现在应用程序传给WriteFile函数的缓冲区内。WriteFile函数在HID通信中最常发生的错误是CRCerror。此错误表示主机控制器试图要传送报表,但是没有从设备收到预期的响应。通常该错误不是发生在CRC计算时所检测到的错误,而是因为主机没有收到固件预期的响应。(4)从设备输入出报表当应用程序取得HID设备的句柄,并且知道输入报表的字节数目后,就可以从此设备读取输入报表。应用程序先声明一个缓冲区来储存数据,然后调用ReadFile函数。用来储存数据的缓冲区大小等于HidP_GetCaps函数所返回的HIDP_CAPS结构的InputReportByteLength属性值。‘函数声明PublicDeclareFunctionReadFileLib"kernel32”(_ByValhFileAsLong,_ByReflpBufferAsByte,_ByValnNumberOfBytesToReadAsLong,_ByReflpNumberOfBytesReadAsLong,_ByVallpOverlappedAsLong_)AsLong'变量定义DimCountDimNumberOfBytesReadAsLongDimReadBuffer()AsByte '输入缓冲区,字节0为ReportIDReDimReadBuffer(Capabilities.InputReportByteLength-1)'调用Result=ReadFile(_HidDevice,_ '由CreateFile 函数返回的设备句柄ReadBuffer(0),_ '输入缓冲区首地址CLng(Capabilities.InputReportByteLength),_ '要读取的字节数NumberOfBytesRead,_ '读到的字节数0 一)ReadBuffer字节数组包含报表的数据。如果函数返回的Result数值不等于零,表示函数成功执行。通过ReadFile读取的缓冲区的第一个字节是ReportID,后续是从设备读取的报表数据。如果接口只支持一个ReportID,此ReportID不在总线上传输,但会出现在ReadBuffer缓冲区内。调用ReadFile函数不会立刻开始总线上的传输,只是主机在定时的中断输入传输中读取一个报表。如果没有未读取的报表,就等待下一个传输完成。主机在检测设备后开始请求报表,当HlD驱动程序加载后,驱动程序将报表储存在环状缓冲区内。当缓冲区已经填满并有新的报表到达时,旧的报表会被覆盖。调用ReadFile函数会读取缓冲区内最旧的报表。在Windows98SE以及后来的版本中,默认的环状缓冲区尺寸是8个报表。应用程序可以使用HidD_SetNumInputBuffers 函数来设置缓冲区的大小。如果应用程序没有频繁的请求报表,有些报表就会丢失。如果不想要丢失报表,就应该改用特征报表。如果报表的数据从上一次传输后就没有改变,闲置速率决定设备是否要传送报表。在检测设备时HlD驱动程序会试图将设备的闲置速率设置为0,这表示除非报表的数据有改变否则HID不会传送报表。没有可以改变闲置速率的API函数。如果设备拒绝将闲置速率设置为0,可以传回Stall来响应Set_Idle请求来通知主机设备不支持该请求。如果设备不支持Set_Idle请求,而且应用程序只要读取一次报表,由件可以设置成只传送一次报表。在送报表后,固件可以设置端点传回NAK来响应输入令牌信息包。如果设备有新的数据要传送,固件可以设置端点来传回该数据。否则设备会在主机轮询端点时继续传送相同的报表,应用程序也会重复地读取相同的报表。上面程序中ReadFile的最后一个参数为0,表示ReadFile调用是阻塞的。当应用程序在环形缓冲区为空时调用ReadFile,应用程序将会被挂起,直到有输入报表为止,否则只能按下Ctrl+Alt+Del来关闭应用程序,或是从总线上移除设备。采用多线程方式编程可以较好的解决这个问题,在另一个线程中调用ReadFile可以避免主线程被挂起,在使用VisualBasic编写多线程应用程序会遇到困难,这是因为VisualBasic本身不支持多线程。而在VisualC++编写API方式通信程序时可以采用多线程方式,ReadFile函数调用发生在一个独立的线程,这样可以实现重叠I/O操作。解决的办法之一是保证设备永远有数据传送,可以将固件设计为输入端点永远启用并且准备响应要求。如果没有新的数据传送,设备可以传送上一次的数据,或是传回一个特定代码来指示没有新的数据。也可以采用这样的方法,在调用ReadFile之前,应用程序先调用WriteFile来传送一个报表,报表内可以包含一个特定代码来告诉固件准备传送数据,这样当应用程序调用 ReadFile时,设备的端点就会启用并且有数据准备传送。比较好的方法是使用ReadFile的重叠选项。在重叠的读取时ReadFile函数会立即返回(即使没有可读数据),然后应用程序调用WaitForSingleObject 函数来读取数据。WaitForSingleObject 函数可以设置暂停,如果数据在指定时间内尚未抵达,此函数会传回一个码来指示此情况,然后应用程序可以使用CancelIo函数来取消读取动作。要使用重叠I/O,CreateFile函数必须在dwFlagsAndAttributes 参数中传递一个重叠的结构。应用程序调用CreateEvent函数建立一个事件对象,在ReadFile完成后此事件对象会被设置成信号状态。当应用程序调用ReadFile时,它传递一个重叠结构的指针,重叠结构的hEvent参数是一个事件对象的代号。应用程序调用WaitForSingleObject 函数来传递此事件代号,以及一个以ms为单位的指定时间间隔。在读取到数据或到达该时间间隔时,此函数才返回。'函数声明PublicDeclareFunctionReadFileLib"kernel32"(_ByValhFileAsLong,_ByReflpBufferAsByte,_ByValnNumberOfBytesToReadAsLong,_ByReflpNumberOfBytesReadAsLong,_ByVallpOverlappedAsLong_)AsLongPublicDeclareFunctionCreateEventLib"kernal32.dll"AliasCreatEventA(—ByValSecurityAttributesAsLong,_ByValbManualResetAsLong,_ByValbInitialStateAsLong,_ByVallpNameAsString)AsLongPublicDeclareFunctionWaitForSingleObjectLib kernal32.dll”(_ByValhHandleasLong,_ByValdwMillisecondsasLong)asLong'重叠结构声明PublicTypeOVERLAPPEDInternalasLongInternalHighasLongOffsetasLongOffsetHighasLonghEventasLongEndType'常量定义PublicConstFILE_FLAG_OVERLAPPED=&H40000000'变量定义DimEventObjectasLongDimHIDOverlappedasOVERLAPPED'部分代码EventObject=CreatEvent(0&,True,True, “")HIDOverlapped.hEvent=EventObjectHIDOverlapped.Offset=0HIDOverlapped.OffsetHigh=0HidDevice=CreateFile(_DevicePathName,_GENERIC_READOrGENERIC_WRITE,_(FILE_SHARE_READOrFILE_SHARE_WRITE),_0,_OPEN_EXISTING,_FILEFLAGOVERLAPPED, 0) '获得HID设备句柄 Result=ReadFile(_ HidDevice,_ '由CreateFile 函数返回的设备句柄 ReadBuffer(0),_ '输入缓冲区首地址 CLng(Capabilities.InputReportByteLength),_ '要读取的字节数 NumberOfBytesRead,_ '读到的字节数 HIDOverlapped ) '读取报表,同时传送一个指针到重叠结构 Result=WaitForSingleObject(EventObject,5000) '等待ReadFile完成,超时间隔设为5 秒 (5)特征报表的传送应用程序调用HidD_SetFeature 函数传送一个特征报表到设备。‘函数声明PublicDeclareFunctionHidD_SetFeatureLib"hid.dll"(_ByValHidDeviceObjectAsLong,_ByRefReportBufferAsByte,_ByValReportBufferLengthAsLong)AsLong'调用Result=HidD_SetFeature(_HidDevice,_ '由CreateFile 函数返回的设备句柄SendBuffer(0),_ '输出缓冲区首地址CLng(Capabilities.FeatureReportByteLength) '特征报表长度字节数)API函数HidD_GetFeature用于从设备读取‘函数声明PublicDeclareFunctionHidD_GetFeatureLib"hid.dll"(_ByValHidDeviceObjectAsLong,_ByRefReportBufferAsByte,_ByValReportBufferLengthAsLong)AsLong'调用Result=HidD_GetFeature(_HidDevice,_ '由CreateFile 函数返回的设备句柄ReadBuffer(0),_ '输出缓冲区首地址CLng(Capabilities.FeatureReportByteLength) '特征报表长度字节数)特征报表,通过对该API函数的调用,主机控制器以控制传输送出Get_Feature请求,并在数据阶段,设备回传特征戒表。(6)关闭设备当结束与HID的通信,需要调用CloseHandle函数关闭通信‘函数声明PublicDeclareFunctionCloseHandleLib"kernel32”(ByValhObjectAsLong_)AsLong'调用Result=CloseHandle(HidDevice)8.6HID实验本节介绍通过高级接口实验台进行的一个HID编程实验。在USB设备软件的开发过程中,借助于一些工具软件的测试会对USB设备的信息获取和通信过程又更深入的理解。USB测试软件有很多,如USBView、BusHound等,下面针对一个具体的HID设备的API通信软件的开发,对这两个工具软件作一简单介绍。高级接口实验台中,有一个HID实验,在该实验中,实验台通过固件软件设计了一个简单的HID仿真设备,在PC的API通信软件的开发过程中,可以借助USB工具软件对实验台的HID设备进行测试。8.6.1获得描述符将高级接口实验台通过USB电缆与PC连接后,在实验台上通过菜单选择HID实验,实验台显示器显示器上会显示 HID实验界面。实验台的HID实验是一个为了学习HID编程而专门设计的一个简单的HID仿真设备,设备中设计了8个寄存器(R1~R8),可以通过USB接口与主机交换数据。其中R1和R2两个寄存器只是数据存储单元,主机可以对这两个寄存器进行附值,也可以读取寄存器的值。寄存器R3~R8构成了一个日期和时钟,6个寄存器的值分别表示年、月、日、时、分、秒。时钟在当前值的基础上运行,可以通过主机对时钟进行设置,也可以读取当前的时钟值。在实验台上设计了自动回传功能,如果自动回传打开,时钟每一秒向主机传送一次报表。实验台可以显示发送和接收的有效数据字节数。监==§睡奇信2:88888888=朋H0茁3%081R4段1R5^3R4R5R6携中mS回传:关闭,,cc.4t茨/士士4.图8-7 高级接口实验台 HID实验界面USBView是Microsoft提供的一个简单的USB测试软件,该工具软件是一个完全的绿色软件,只有一个EXE程序文将件,不需要安装,在Windows环境下直接运行。USBView其主要功能是获得USB设备的各个描述符。可以运行USBView获得实验台仿真的HID设备的描述符,运行USBView后显示以下程序界面。窗口的左侧显示全部的USB设备连接树,在其中找到一个显示“USB人体工程学设备”的分支,选中后右侧窗口显示全部的(报表描述符除外)描述符。USBView显示的高级接口实验台的HID仿真设备的描述符如下。 DeviceDescriptor: bcdUSB: 0x0110 bDeviceClass: 0x00 bDeviceSubClass: 0x00 bDeviceProtocol: 0x00 bMaxPacketSize0: 0x10(16) idVendor: 0x045E(MicrosoftCorporation) idProduct: 0x930A bcdDevice: 0x0100 iManufacturer: 0x01 iProduct: 0x02 iSerialNumber: 0x03 bNumConfigurations: 0x01 ConnectionStatus:DeviceConnectedCurrentConfigValue: 0x01 DeviceBusSpeed: Full DeviceAddress: 0x02 OpenPipes: 4 EndpointDescriptor:bEndpointAddress: 0x81 TransferType: Bulk wMaxPacketSize: 0x0010(16) bInterval: 0x0A EndpointDescriptor:bEndpointAddress: 0x01 TransferType: Bulk wMaxPacketSize: 0x0010(16) bInterval: 0x0A EndpointDescriptor:bEndpointAddress: 0x82 TransferType: Interrupt wMaxPacketSize: 0x0040(64) bInterval: 0x01 EndpointDescriptor:bEndpointAddress:TransferType: 0x02InterruptwMaxPacketSize: 0x0040(64)blnterval: 0x018.6.2设备的初始化在将高级接口实验台连接到计算机并操作实验台进入HID接口实验后,Windows系统会发现设备并读取设备的各种描述符,可以通过一个USB工具软件截取Windows和设备之间的请求应答过程。工具软件BusHound可以实现这个功能。BunHound是一个功能全面的总线仪软件,可以实现对计算机通过各种接口连接的设备的通信过程进行截取和分析。图8-9是BusHound的设备连接树(Devices)界面。当高级接口实验台与计算机通过 USB接口连接并操作实验台进入HID实验,可以在BusHound的设备连接树中找到一个“USB人体工程学设备”项目。选中该项目可以实现对设备的通信过程的截取。选中高级接口实验台的HID设备项,进入到采集(Capture)界面,先控制实验台退出HID实验,按BusHound的采集界面的Stop按钮,再按Run按钮,然后控制实验台进入HID实验界面。Windows系统发现设备并请求设备的各种描述符,然后完成对设备的必要的设置,BusHound可以采集到以上通信过程。下面分析BusHound截取的数据内容。·主机发送:8006000100001200Get_Descriptor请求,请求设备回传设备描述犯·设备发送:12011001000000105e040a93000101020301设备描述符内容:USB版本=1.1、类别/协议码=0、EP0的最大包尺寸=10、VID=045E、PID=930A、版本=1.0、厂商、产品和序列号字符串索引、配置数=1。·主机发送:8006000200000900Get_Descriptor请求,请求设备回传配置描述符。·设备发送:090237000101048032配置描述符内容:该描述符及后续描述符总长度=55支持接口数=1、配置标示符=1、总线供电、最大100mA·主机发送:8006000200003700Get_Descriptor请求,请求设备回传配置、接口、"hid和端点描述符。·设备发送:0902370001010480320904000004030000050921010100012234000705810210000a0705010210000a0705820340000107050203400001按顺序为:9字节配置描述符:含义同上。9字节接口描述符:标示符=0>支持的端点=4、类别=HID9字节HID描述符:版本=1.0<有1个报表描述符(长度为52)7字节端点描述符:1号批量输入,包尺寸=1&7字节端点描述符:1号批量输出,包尺寸=1&7字节端点描述符:2号中断输入,包尺寸=641ms轮询。7字节端点描述符:2号中断输出,包尺寸=641ms轮询。?主机发送:0009010000000000Set_Configuation请求,配置号=1。?主机发送:210a000000000000Set_Idle请求,设定间隔时间为0。?设备爰送:040000C0STALL,不支持请求。?主机发送:8106002200007400Get_Descriptor请求,请求设备回传报表描述符。'设备发送:06a0ff0901al010902al0006alff090309041580257f350045ff750895408102090509061580257f350045ff750895409102c0c0报表描述符。8.6.3HID测试程序的实现使用VisualBasic编写一个针对高级接口实验台的应用程序,实现与设备的通信。该测试程序实现以下功能:?查找设备:根据指定的VID和PID实现设备查找;?获得设备能力:调用相应的API,获得设备的能力;?发送报表:将界面输入的R1~R8的数值按照报表的指定格式传送到设备;?接收报表:将设备的界面显示的R1~R8的数值读取到应用程序;?显示API函数调用信息:显示每一个API函数的调用结果;?关闭设备。在获得能力的API函数的调用结果中,输入报表和输出报表的长度为65字节,在发送和接收报表时的长度设定都需要设为65。但在该实验中真正用到的有效数据只有8个寄存器的数据,占用其中的8个字节,报表的首字节(第0个)为0,表示ReportID。第1、2个字节分别55h、AAh为用户定义的标示字。第3、4字节为用户定义命令编码,输出报表中为01、08,输入报表中为02、从第5到第12字节顺序为R1、R2、的值。之后的数据为保留字节。08。・・・R8'HID查找、能力检测、输入报表、输出报表演示程序'修改自许永和的〈〈介面设计与实习 使用BisualBasic»OptionExplicit'变量定义******************************************************************************DimCapabilitiesAsHIDP_CAPSDimDataStringAsStringDimDetailDataAsLongDimDetailDataBuffer()AsByteDimDeviceAttributesAsHIDD_ATTRIBUTESDimDevicePathNameAsStringDimDeviceInfoSetAsLongDimErrorStringAsStringDimHidDeviceAsLongDimLastDeviceAsBooleanDimMyDeviceDetectedAsBooleanDimMyDeviceInfoDataAsSP_DEVINFO_DATADimMyDeviceInterfaceDetailDataAsSP_DEVICE_INTERFACE_DETAIL_DATADimMyDeviceInterfaceDataAsSP_DEVICE_INTERFACE_DATADimNeededAsLongDimOutputReportData(7)AsByteDimPreparsedDataAsLongDimResultAsLongDimTimeoutAsBooleanDimMyVendorIDAsLong 'VID、PIDDimMyProductIDAsLong'**************************************************************************************'查找全部的HID设备,直到找到VID、PID符合的一个HID'如果找到,MyDeviceDetected为True'**************************************************************************************FunctionFindTheHid()AsBooleanDimCountAsIntegerDimGUIDStringAsStringDimHidGuidAsGUIDDimMemberIndexAsLongLastDevice=FalseMyDeviceDetected=False调用HidD_GetHidGuid函数获得GUIDResult=HidD_GetHidGuid(HidGuid)Call_DisplayResultOfAPICall("GetHidGuid") GUIDString=Hex$(HidGuid.Data1)&"-"&Hex$(HidGuid.Data2)&"-"&Hex$(HidGuid.Data3)&"-"ForCount=0To7IfHidGuid.Data4(Count)>=&H10ThenGUIDString=GUIDString&Hex$(HidGuid.Data4(Count))&""ElseGUIDString=GUIDString&"0"&Hex$(HidGuid.Data4(Count))&""EndIfNextCountlstResults.AddItem"GUIDforsystemHIDs:"lstResults.AddItem""&GUIDString调用SetupDiGetClassDevs函数获得指向HID信息集的指针DeviceInfoSet=SetupDiGetClassDevs(HidGuid,vbNullString,0,(DIGCF_PRESENTOrDIGCF_DEVICEINTERFACE))CallDisplayResultOfAPICall("SetupDiClassDevs")DataString=GetDataString(DeviceInfoSet,32)lstResults.AddItem"DeviceInfoSet:"&DeviceInfoSet'下面循环,从MemberIndex=0开始,查找指定HIDMemberIndex=0Do'调用SetupDiEnumDeviceInterfaces函数获得SP_DEVICE_INTERFACE_DATA构指针MyDeviceInterfaceData.cbSize=LenB(MyDeviceInterfaceData)Result=SetupDiEnumDeviceInterfaces(DeviceInfoSet,0,HidGuid,MemberIndex,MyDeviceInterfaceData)CallDisplayResultOfAPICall("SetupDiEnumDeviceInterfaces")IfResult=0ThenLastDevice=True'如果调用成功IfResult<>0Then'显示获得的信息lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"DeviceInfoSetfordevice#"&CStr(MemberIndex)&":"cbSize="&CStr(MyDeviceInterfaceData.cbSize)InterfaceClassGuid.Data1="&Hex$(MyDeviceInterfaceData.InterfaceClassGuid.Data1)lstResults.AddItem"InterfaceClassGuid.Data2_="&Hex$(MyDeviceInterfaceData.InterfaceClassGuid.Data2)lstResults.AddItem"InterfaceClassGuid.Data3_="&Hex$(MyDeviceInterfaceData.InterfaceClassGuid.Data3)lstResults.AddItem"Flags="&Hex$(MyDeviceInterfaceData.Flags)'调用SetupDiGetDeviceInterfaceDetai函数,获得SP_DEVICE_INTERFACE_DETAIL_DATAJ'注意:该函数需要调用两次,最后获得设备路径MyDeviceInfoData.cbSize=Len(MyDeviceInfoData)Result=SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,MyDeviceInterfaceData,0,0,Needed,0)DetailData=NeededCallDisplayResultOfAPICall("SetupDiGetDeviceInterfaceDetail")IstResults.AddItem"(OKtosaytoosmall)"IstResults.AddItem"Requiredbuffersizeforthedata:"&Needed'存储结构的长度MyDeviceInterfaceDetailData.cbSize=Len(MyDeviceInterfaceDetailData)ReDimDetailDataBuffer(Needed)'存储结构的前4和字节,cbSizeCallRtlMoveMemory(DetailDataBuffer(0),MyDeviceInterfaceDetailData,4)'再一次调用Result=SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,_MyDeviceInterfaceData,VarPtr(DetailDataBuffer(0)),DetailData,Needed,0)CallDisplayResultOfAPICall("Resultofsecondcall:")lstResults.AddItem" MyDeviceInterfaceDetailData.cbSize: " &CStr(MyDeviceInterfaceDetailData.cbSize)DevicePathName=CStr(DetailDataBuffer())DevicePathName=StrConv(DevicePathName,vbUnicode) '转换成UnicodeDevicePathName=Right$(DevicePathName,Len(DevicePathName)-4)'删除4个字节lstResults.AddItem"Devicepathname:"lstResults.AddItem" "&DevicePathName'调用CreateFile函数,获得设备句柄:HidDeviceHidDevice=CreateFile(DevicePathName,GENERIC_READOrGENERIC_WRITE,_(FILE_SHARE_READOrFILE_SHARE_WRITE),0,OPEN_EXISTING,0,0)CallDisplayResultOfAPICall("CreateFile")lstResults.AddItem"Returnedhandle:"&Hex$(HidDevice)&"h"'调用HidD_GetAttributes获得HID的VIDPIDDeviceAttributes.Size=LenB(DeviceAttributes)Result=HidD_GetAttributes(HidDevice,DeviceAttributes)CallDisplayResultOfAPICall("HidD_GetAttributes")IfResult<>0ThenlstResults.AddItem"HIDD_ATTRIBUTESstructurefilledwithouterror."ElselstResults.AddItem"ErrorinfillingHIDD_ATTRIBUTESstructure."EndIfStructuresize:"&DeviceAttributes.SizelstResults.AddItemlstResults.AddItemlstResults.AddItemlstResults.AddItemVendorID:"&Hex$(DeviceAttributes.VendorID)ProductID:"&Hex$(DeviceAttributes.ProductID)VersionNumber:"&Hex$(DeviceAttributes.VersionNumber)'看看是不是指定的VIDPIDIf(DeviceAttributes.VendorID=MyVendorID)And(DeviceAttributes.ProductID=MyProductID)ThenlstResults.AddItem"Mydevicedetected"lstResults.AddItem" "lblHID.Caption="HIDFind"MyDeviceDetected=TruecmdGetCaps.Enabled=TruecmdClose.Enabled=TruetxtVendorID.Enabled=FalsetxtProductID.Enabled=FalseElseMyDeviceDetected=FalseResult=CloseHandle(HidDevice)DisplayResultOfAPICall("CloseHandle")EndIfEndIfMemberIndex=MemberIndex+1 '准备查找下一个LoopUntil(LastDevice=True)Or(MyDeviceDetected=True)EndFunction'**************************************************************************************'获得上一个API函数的执行信息'**************************************************************************************PrivateFunctionGetErrorString(ByValLastErrorAsLong)AsStringDimBytesAsLongDimErrorStringAsStringErrorString=String$(129,0)Bytes=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0&,LastError,0,ErrorString$,128,0)IfBytes>2Then '去掉其中的回车GetErrorString=Left$(ErrorString,Bytes-2)EndIfEndFunction'**************************************************************************************'清除数据域显示'**************************************************************************************PrivateSubcmdClear_Click()txtR1.Text=""txtR2.Text=""txtYear.Text=""txtMonth.Text=""txtDay.Text=""txtHour.Text=""txtMinute.Text=""txtSecond.Text=""EndSub'**************************************************************************************'查找HID'**************************************************************************************PrivateSubcmdFindHID_Click()CallFindTheHid一EndSub'**************************************************************************************'显示API函数的执行结果PrivateSubDisplayResultOfAPICall(FunctionNameAsString)DimErrorStringAsStringlstResults.AddItem""ErrorString=GetErrorString(Err.LastDllError)lstResults.AddItemFunctionNamelstResults.AddItem"Result="&ErrorStringEndSub'**************************************************************************************"i**************************************************************************************PrivateSubForm_Load()frmMain.ShowtmrDelay.Enabled=FalselstResults.ClearMyVendorID=&H45EMyProductID=&H930AEndSub'**************************************************************************************'获得HID的能力信息心************************************************************************************PrivateSubcmdGetCaps_click()DimppData(29)AsByteDimppDataStringAsVariant调用HidD_GetPreparsedData获得一个缓冲区指针Result=HidD_GetPreparsedData(HidDevice,PreparsedData)CallDisplayResultOfAPICall("HidD_GetPreparsedData")Result=RtlMoveMemory(ppData(0),PreparsedData,30)CallDisplayResultOfAPICall("RtlMoveMemory")ppDataString=ppData()ppDataString=StrConv(ppDataString,vbUnicode)调用HidP_GetCaps获得HID_CAPS结构数据Result=HidP_GetCaps(PreparsedData,Capabilities)CallDisplayResultOfAPICall("HidP_GetCaps")lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"lstResults.AddItem"Lasterror:"&ErrorStringUsage:"&Hex$(Capabilities.Usage)UsagePage:"&Hex$(Capabilities.UsagePage)InputReportByteLength:"&Capabilities.InputReportByteLengthOutputReportByteLength:"&Capabilities.OutputReportByteLengthFeatureReportByteLength:"&Capabilities.FeatureReportByteLengthNumberofLinkCollectionNodes:"&Capabilities.NumberLinkCollectionNodesNumberofInputButtonCaps:"&Capabilities.NumberInputButtonCapsNumberofInputValueCaps:"&Capabilities.NumberInputValueCapsNumberofInputDataIndices:"&Capabilities.NumberInputDataIndicesNumberofOutputButtonCaps:"&Capabilities.NumberOutputButtonCapsNumberofOutputValueCaps:"&Capabilities.NumberOutputValueCapsNumberofOutputDataIndices:"&Capabilities.NumberOutputDataIndicesIstResults.AddItem"NumberofFeatureButtonCaps:"&Capabilities.NumberFeatureButtonCapsIstResults.AddItem"NumberofFeatureValueCaps:"&Capabilities.NumberFeatureValueCapslstResults.AddItem"NumberofFeatureDataIndices:"&Capabilities.NumberFeatureDataIndices调用HidP_GetValueCaps获得HID能力的数值DimValueCaps(1023)AsByteResult=HidP_GetValueCaps(HidP_Input,ValueCaps(0),Capabilities.NumberInputValueCaps,PreparsedData)CallDisplayResultOfAPICall("HidP_GetValueCaps")lstResults.AddItem" "lblCaps.Caption="GetCapsOk"cmdTrans.Enabled=TruecmdReceive.Enabled=TrueEndSub'**************************************************************************************输出报表到HID'**************************************************************************************PrivateSubcmdTrans_Click()DimCountAsIntegerDimNumberOfBytesToSendAsLongDimNumberOfBytesWrittenAsLongDimSendBuffer()AsByteReDimSendBuffer(Capabilities.OutputReportByteLength-1)'填写报表数据到数组 SendBufferCount=0SendBuffer(Count)=0 '第一侗位元鲍是ReportIDCount=Count+1SendBuffer(Count)=&H55Count=Count+1SendBuffer(Count)=&HAACount=Count+1SendBuffer(Count)=&H1Count=Count+1SendBuffer(Count)=&H8Count=Count+1SendBuffer(Count)=Val("&H"+txtR1.Text)Count=Count+1SendBuffer(Count)=Val("&H"+txtR2.Text)Count=Count+1SendBuffer(Count)=Val(txtYear.Text)Count=Count+1SendBuffer(Count)=Val(txtMonth.Text)Count=Count+1SendBuffer(Count)=Val(txtDay.Text)Count=Count+1SendBuffer(Count)=Val(txtHour.Text)Count=Count+1SendBuffer(Count)=Val(txtMinute.Text)Count=Count+1SendBuffer(Count)=Val(txtSecond.Text)Count=Count+1 调用WriteFile函数,发送报表NumberOfBytesWritten=0Result=WriteFile(HidDevice,SendBuffer(O),CLng(Capabilities.OutputReportByteLength),NumberOfBytesWritten,0)CallDisplayResultOfAPICall("WriteFile")lstResults.AddItem"OutputReport"+Str(NumberOfBytesWritten)+"bytes"EndSub,**************************************************************************************'从HID读取报表'注意:以下代码为非重叠调用,必须保证 HID输出报表,**************************************************************************************PrivateSubcmdReceive_click()DimCountDimNumberOfBytesReadAsLongDimReadBuffer()AsByteReDimReadBuffer(Capabilities.InputReportByteLength-1)调用ReadFile函数,读取报表Result=ReadFile(HidDevice,ReadBuffer(O),CLng(Capabilities.InputReportByteLength),NumberOfBytesRead,0)CallDisplayResultOfAPICall("ReadFile")lstResults.AddItem"InputReport"+Str(NumberOfBytesRead)+"bytes"'将输入报表的数据填写到显示介面的相应数据域txtRl.Text=Hex$(ReadBuffer(5))txtR2.Text=Hex$(ReadBuffer(6))txtYear.Text=IIf(ReadBuffer(7)<10,"0"+Trim(Str$(ReadBuffer(7))),Trim(Str$(ReadBuffer(7))))txtMonth.Text=IIf(ReadBuffer(8)<10,"0"+Trim(Str$(ReadBuffer(8))),Trim(Str$(ReadBuffer(8))))txtDay.Text=IIf(ReadBuffer(9)<10,"0"+Trim(Str$(ReadBuffer(9))),Trim(Str$(ReadBuffer(9))))txtHour.Text=IIf(ReadBuffer(10)<10,"0"+Trim(Str$(ReadBuffer(10))),Trim(Str$(ReadBuffer(10))))txtMinute.Text=IIf(ReadBuffer(11)<10,"0"+Trim(Str$(ReadBuffer(11))),Trim(Str$(ReadBuffer(11))))txtSecond.Text=IIf(ReadBuffer(12)<10,"0"+Trim(Str$(ReadBuffer(12))),Trim(Str$(ReadBuffer(12))))EndSub,**************************************************************************************'关闭设备,释放资源,**************************************************************************************PrivateSubcmdClose_Click()调用CloseHandle关闭HIDResult=CloseHandle(HidDevice)CallDisplayResultOfAPICall("CloseHandle(HidDevice)")调用SetupDiDestroyDeviceInfoList和HidD_FreePreparsedData释放占用的资源Result=SetupDiDestroyDeviceInfoList(DeviceInfoSet)CallDisplayResultOfAPICall("DestroyDeviceInfoList")Result=HidD_FreePreparsedData(PreparsedData)CallDisplayResultOfAPICall("HidDFreePreparsedData")IstResults.ClearcmdClose.Enabled=FalsecmdGetCaps.Enabled=FalsecmdTrans.Enabled=FalsecmdReceive.Enabled=FalselblHID.Caption=""lblCaps.Caption=""txtVendorID.Enabled=TruetxtProductID.Enabled=TrueEndSub'**************************************************************************************'将当前日期和时间填写到界面的数据域'**************************************************************************************PrivateSubcmdNow_Click()txtHour.Text=IIf(Hour(Now())<10,"0"+Hour(Now()),Hour(Now()))txtMinute.Text=IIf(Minute(Now())<10,"0"+Trim(Str(Minute(Now()))),Minute(Now()))txtSecond.Text=IIf(Second(Now())<10,"0"+Trim(Str(Second(Now()))),Second(Now()))txtYear.Text=IIf((Year(Now())-2000)<10,"0"+Trim(Str(Year(Now())-2000)),Str(Year(Now())-2000))txtMonth.Text=IIf(Month(Now())<10,"0"+Trim(Str(Month(Now()))),Str(Month(Now())))txtDay.Text=IIf(Day(Now())<10,"0"+Trim(Str(Day(Now()))),Day(Now()))EndSub'**************************************************************************************'获得信息字符串'**************************************************************************************PrivateFunctionGetDataString(AddressAsLong,BytesAsLong)AsStringDimOffsetAsIntegerDimResult$DimThisByteAsByteForOffset=0ToBytes-1CallRtlMoveMemory(ByValVarPtr(ThisByte),ByValAddress+Offset,1)If(ThisByteAnd&HF0)=0ThenResult$=Result$&"0"EndIfResult$=Result$&Hex$(ThisByte)&""NextOffsetGetDataString=Result$EndFunction
/
本文档为【USBHID协议中文版】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索