2009年3月5日星期四

基于HID类的USB人机接口设计

基于HID类的USB人机接口设计
http://www.robotsky.com 来源:RobotSky编辑采编  时间:2008-03-05 评论 0 条
 (访问论坛) RobotSky恭候您的投稿>>
  摘要 USB设备及其驱动程序的复杂性给开发人员带来了很大的开发难度。本文给出一种采用C8051F120微控制器和PDIUSBD12 USB控制器结合的方式实现人机接口设备(Human Interface Devices)的方法,以及如何编写应用程序来对它进行访问。此设备无需编写USB设备驱动程序,Windows系统能够自动识别。

  关键词 HID 人机接口设备 USB 描述符 中断传输 报告

  目前市场上USB设备的种类繁多,但是这些设备会有一些共同的特性,根据这些特性可以把USB设备划分为不同的类,如显示设备、通信设备、音频设备、大容量存储设备、人机接口设备(HID)。这里介绍如何实现HID类设备,以及如何在应用程序中对HID类设备进行访问。从WINDOWS98操作系统开始,为HID类设备提供了通用的驱动程序,所以只要按照HID设备类的规范编写设备的固件程序,就能够让Windows系统自动识别设备,省去了复杂的驱动程序编写过程。

  1 HID协议简介

  人机接口设备(HID)主要是指一些人与计算机进行交互的设备,如键盘、鼠标、游戏杆等;但是HID设备不一定非要是这些人机交互设备,只要符合HID设备级定义规范要求的都可以认为是HID设备。HID设备有以下主要特点:

  ① 交换的数据存储在报告的结构内,设备必须支持HID报告格式。

  ② 每笔事务可以携带小量或中量的数据。低速设备每笔事务最大为8字节,全速设备每笔最大为64字节,高速设备最大为1 024字节;

  ③ 有最大传输速度的限制。低速设备最快10 ms一笔事务,最高速度为800 B/s;全速设备最快1 ms一笔事务,最高速度为64 KB/s;高速设备最快125 μs一笔事务,最高速度为24.576 MB/s。

  ④ 没有传输速度的保证。

  当插入USB设备后,主机会向设备请求各种描述符来识别设备。为了把一个设备识别为HID类别,设备在定义描述符的时候必须遵守HID规范。图1显示了 HID各种描述符之间的关系。事实上,每个设备可以有多个接口描述符来实现多接口设备,而且每个接口描述符下应该有多个端点描述符。

  

  图1 HID各种描述符之间的关系

  从图1中可以看出,除了USB标准定义的一些描述符外,HID设备还必须定义HID描述符。另外设备和主机的通信是通过报告的形式来实现的,所以还必须定义报告描述符;而物理描述符不是必需的。还有就是HID描述符是关联于接口(而不是端点)的,所以设备不需要为每个端点都提供一个HID描述符。

  USB设备有4种传输方式与主机进行通信: 控制方式、中断方式、批量方式和同步方式。每种方式都有它的应用领域。HID只支持控制和中断传输方式。如图2所示,HID设备必须要有默认的控制管道和一个中断输入端点;中断输出端点是可选的。

  

  图2 HID类设备使用控制和中断传输方式

  中断输出传输是USB1.1规范才有的内容,且必须获得Windows系统的支持。从WINDOWS98 SE版本开始才支持中断输出传输方式,所以如果需要中断输出传输方式的设备应该选择相应的操作系统。表1列出了传输类型和相关情况。

  表1 HID类设备支持的传输方式传输

  

  USB协议定义了11种请求命令,通过这些请求来获得设备的信息及对设备进行设置。HID类设备除了要支持这11种标准的请求外,还要实现以下6种特定请求:

  ① Get_Report——主机用控制传输从设备接收数据,所有HID类设备都要支持这个请求;

  ② Set_Report——设备用控制传输接收主机的数据,设备可以不支持此请求;

  ③ Get_Idle——主机读取设备当前的空闲速率,设备可以不支持此请求;

  ④ Set_Idle——设置闲置状态,设备可不支持此请求;

  ⑤ Get_Protocol——主机获得设备的当前活动是引导协议还是报告协议;

  ⑥ Set_Protocol——在引导协议和报告协议间切换,设备如果支持系统引导(如键盘和鼠标),就必须支持Get_Protocol和Set_Protocol请求。

  2 HID接口固件设计与实现

  该设备采用C8051F120微控制器和PDIUSBD12芯片来实现,如图3所示。

  

  图3 HID系统结构框图

  因为PDIUSBD12的主端点(Endpoint2)具有64字节的双缓冲,能够提供比较高的速度,所以在端点描述符里把它配置为中断传输方式,而Endpoint1没有使用。PDIUSBD12通过中断触发CPU来响应主机的各种请求。

  此系统采用的USB协议版本是1.1,所以能够支持中断输出传输。为了让主机把设备识别为HID类别,定义设备接口描述符时类别这一字段的值必须设置为 0x03(HID类别),这样主机就会继续请求获得设备的HID描述符和报告描述符。在主机Get_Descriptor请求中,当值字段的高位字节为 0x21时,表示主机要求获得HID描述符;当值字段高字节为0x22时,就是主机要求获得报告描述符。对于报告描述符,可以参考HID Usage Tables规范。HID Descriptor Tool工具可以帮助建立和测试编写的报告描述符。这里定义了一个输入和输出64字节数据的报告描述符。

  code unsigned char szReport[] = {

  0x06,0xA0,0xFF,//用法页(FFA0h, vendor defined)

  0x09, 0x01,//用法(vendor defined)

  0xA1, 0x01,//集合(Application)

  0x09, 0x02 ,//用法(vendor defined)

  0xA1, 0x00,//集合(Physical)

  0x06,0xA1,0xFF,//用法页(vendor defined)

  //输入报告

  0x09, 0x03 ,//用法(vendor defined)

  0x09, 0x04,//用法(vendor defined)

  0x15, 0x80,//逻辑最小值(0x80 or -128)

  0x25, 0x7F,//逻辑最大值(0x7F or 127)

  0x35, 0x00,//物理最小值(0)

  0x45,0xFF,//物理最大值(255)

  0x75, 0x08,//报告长度Report size (8位)

  0x95, 0x40,//报告数值(64 fields)

  0x81, 0x02,//输入(data, variable, absolute)

  //输出报告

  0x09, 0x05,//用法(vendor defined)

  0x09, 0x06,//用法(vendor defined)

  0x15, 0x80,//逻辑最小值(0x80 or -128)

  0x25, 0x7F,//逻辑最大值(0x7F or 127)

  0x35, 0x00,//物理最小值(0)

  0x45,0xFF,//物理最大值(255)

  0x75,0x08,//报告长度(8位)

  0x95, 0x40,//报告数值(64 fields)

  0x91, 0x02,//输出(data, variable, absolute)

  0xC0,//集合结束(Physical)

  0xC0//集合结束(Application)

  };

  这样,后面数据的输入和输出都必须满足报告的格式才能够进行传输。

  

  图4 应用程序枚举HID设备流程

  3 应用程序设计实现

  Windows为应用程序访问HID设备提供了强大的支持,有一整套对HID设备进行访问的API。应用程序要访问设备就必须先枚举到设备,图4为应用程序枚举HID设备流程。

  枚举成功后根据返回的设备句柄,就可以用ReadFile和WriteFile来读写设备的数据了。这里采用异步方式来读写数据,这样不会发生读写时阻塞,提高了程序的效率。以下是异步方式读写设备的要点:

  ① 为了实现异步访问设备,在CreateFile打开设备时必须使用FILE_FLAG_OVERLAPPED标志。

  ② 打开设备成功后,使用CreateThread建立1个读设备线程。

  ③ 在这个线程中首先建立1个OVERLAPPED结构,并用CreateEvent函数初始化它的hEvent成员,这样就创建了1个事件对象。

  ④ 调用ReadFile函数,并传入这个结构。

  ⑤ 调用ReadFile后会立即返回,必须调用GetLastError获得出错码。 如果为ERROR_IO_PENDING, 说明此操作是在等待完成的;否则,说明调用出错。

  ⑥ 调用WaitForSingleObject等待hEvent事件的通知,并使此线程进入休眠状态。如果有数据发送到主机,读线程就会被激活。

  WriteFile的使用也同样要求异步操作,与ReadFile的使用差不多。

  这里要注意的是,在每次读写数据前都要先接收和发送1字节的PID标志,所以每次读写数据的时候都要多一个字节。比如,这里每次读写的是64字节数据,但是在这64字节之前必须放1字节的PID数据,所以是65字节。一般这个字节的值为0。

  4 小结

  充分利用PDIUSBD12主端口的双缓冲特性后,测试设备与PC间传输速度能达到8 KB/s以上,对于一些传输数据量不大,速度要求不高,而又必须在短时间内做出响应的场合基本能够满足要求。在此基础上只要生成不同的报告描述符,就能开发出各种不同的嵌入式设备;而且这样的设备无需驱动,在插入PC后就能立刻开始工作,省去了安装驱动程序的过程,方便使用。

2009年3月4日星期三

FW: USB HID报告及报告描述符简介

USB HID报告及报告描述符简介


USB HID报告及报告描述符简介

在USB中,USB HOST是通过各种描述符来识别设备的,有设备描述符,

配置描述符,接口描述符,端点描述符,字符串描述符,报告描述符等等。

USB报告描述符(Report Descriptor)是HID设备中的一个描述符,它是比较

复杂的一个描述符。

USB HID设备是通过报告来给传送数据的,报告有输入报告和输出报告。

输入报告是USB设备发送给主机的,例如USB鼠标将鼠标移动和鼠标点击等

信息返回给电脑,键盘将按键数据数据返回给电脑等;输出报告是主机发送

给USB设备的,例如键盘上的数字键盘锁定灯和大写字母锁定灯等。报告是

一个数据包,里面包含的是所要传送的数据。输入报告是通过中断输入端点

输入的,而输出报告有点区别,当没有中断输出端点时,可以通过控制输出

端点0发送,当有中断输出端点时,通过中断输出端点发出。

而报告描述符,是描述一个报告以及报告里面的数据是用来干什么用的。

通过它,USB HOST可以分析出报告里面的数据所表示的意思。它通过控制输入

端点0返回,主机使用获取报告描述符命令来获取报告描述符,注意这个请求

是发送到接口的,而不是到设备。一个报告描述符可以描述多个报告,不同的

报告通过报告ID来识别,报告ID在报告最前面,即第一个字节。当报告描述符中

没有规定报告ID时,报告中就没有ID字段,开始就是数据。更详细的说明请参看

USB HID协议,该协议可从Http://www.usb.org下载。

USB报告描述符可以通过使用HID Descriptor tool来生成,这个工具可以

Http://www.usb.org下载,为了方便大家,我顺便上传了一份。

http://www.ednchina.com /Upload/Blog/2007/4/2/af7c3443-ad61-4465-adc7- a74d28bbc322.zipuploadfile-/2007-4/42672233.zip下面通过由HID Descriptor tool生成的USB鼠标和USB键盘来说明一下报告

描述符和报告。

code char KeyBoardReportDescriptor[63] = {

//表示用途页为通用桌面设备

0x05, 0x01,// USAGE_PAGE (Generic Desktop)

//表示用途为键盘

0x09, 0x06,// USAGE (Keyboard)

//表示应用集合,必须要以END_COLLECTION来结束它,见最后的END_COLLECTION

0xa1, 0x01,// COLLECTION (Application)

//表示用途页为按键

0x05, 0x07,//USAGE_PAGE (Keyboard)

//用途最小值,这里为左ctrl键

0x19, 0xe0,//USAGE_MINIMUM (Keyboard LeftControl)

//用途最大值,这里为右GUI键,即window键

0x29, 0xe7,//USAGE_MAXIMUM (Keyboard Right GUI)

//逻辑最小值为0

0x15, 0x00,//LOGICAL_MINIMUM (0)

//逻辑最大值为1

0x25, 0x01,//LOGICAL_MAXIMUM (1)

//报告大小(即这个字段的宽度)为1bit,所以前面的逻辑最小值为0,逻辑最大值为1

0x75, 0x01,//REPORT_SIZE (1)

//报告的个数为8,即总共有8个bits

0x95, 0x08,//REPORT_COUNT (8)

//输入用,变量,值,绝对值。像键盘这类一般报告绝对值,

//而鼠标移动这样的则报告相对值,表示鼠标移动多少

0x81, 0x02,//INPUT (Data,Var,Abs)

//上面这这几项描述了一个输入用的字段,总共为8个bits,每个bit表示一个按键

//分别从左ctrl键到右GUI键。这8个bits刚好构成一个字节,它位于报告的第一个字节。

//它的最低位,即bit-0对应着左ctrl键,如果返回的数据该位为1,则表示左ctrl键被按下,

//否则,左ctrl键没有按下。最高位,即bit-7表示右GUI键的按下情况。中间的几个位,

//需要根据HID协议中规定的用途页表(HID Usage Tables)来确定。这里通常用来表示

//特殊键,例如ctrl,shift,del键等

//这样的数据段个数为1

0x95, 0x01,//REPORT_COUNT (1)

//每个段长度为8bits

0x75, 0x08,//REPORT_SIZE (8)

//输入用,常量,值,绝对值

0x81, 0x03,//INPUT (Cnst,Var,Abs)

//上面这8个bit是常量,设备必须返回0

//这样的数据段个数为5

0x95, 0x05,//REPORT_COUNT (5)

//每个段大小为1bit

0x75, 0x01,//REPORT_SIZE (1)

//用途是LED,即用来控制键盘上的LED用的,因此下面会说明它是输出用

0x05, 0x08,//USAGE_PAGE (LEDs)

//用途最小值是Num Lock,即数字键锁定灯

0x19, 0x01,//USAGE_MINIMUM (Num Lock)

//用途最大值是Kana,这个是什么灯我也不清楚^_^

0x29, 0x05,//USAGE_MAXIMUM (Kana)

//如前面所说,这个字段是输出用的,用来控制LED。变量,值,绝对值。

//1表示灯亮,0表示灯灭

0x91, 0x02,//OUTPUT (Data,Var,Abs)

//这样的数据段个数为1

0x95, 0x01,//REPORT_COUNT (1)

//每个段大小为3bits

0x75, 0x03,//REPORT_SIZE (3)

//输出用,常量,值,绝对

0x91, 0x03,//OUTPUT (Cnst,Var,Abs)

//由于要按字节对齐,而前面控制LED的只用了5个bit,

//所以后面需要附加3个不用bit,设置为常量。

//报告个数为6

0x95, 0x06,//REPORT_COUNT (6)

//每个段大小为8bits

0x75, 0x08,//REPORT_SIZE (8)

//逻辑最小值0

0x15, 0x00,//LOGICAL_MINIMUM (0)

//逻辑最大值255

0x25, 0xFF,//LOGICAL_MAXIMUM (255)

//用途页为按键

0x05, 0x07,//USAGE_PAGE (Keyboard)

//使用最小值为0

0x19, 0x00,//USAGE_MINIMUM (Reserved (no event indicated))

//使用最小值为0x65

0x29, 0x65,//USAGE_MAXIMUM (Keyboard Application)

//输入用,变量,数组,绝对值

0x81, 0x00,//INPUT (Data,Ary,Abs)

//以上定义了6个8bit宽的数组,每个8bit(即一个字节)用来表示一个按键,所以可以同时

//有6个按键按下。没有按键按下时,全部返回0。如果按下的键太多,导致键盘扫描系统

//无法区分按键时,则全部返回0x01,即6个0x01。如果有一个键按下,则这6个字节中的第一

//个字节为相应的键值(具体的值参看HID Usage Tables),如果两个键按下,则第1、2两个

//字节分别为相应的键值,以次类推。

//关集合,跟上面的对应

0xc0// END_COLLECTION

};

通过上面的分析,我们知道这个报告中只有一个报告,所以没有报告ID,

因此返回的都是实际使用的数据。总共有8字节输入,1字节输出。其中输入的

第一字节用来表示特殊按键,第二字节保留,后面的六字节为普通按键。如果

只有左ctrl键按下,则返回01 00 00 00 00 00 00 00(十六进制),如果

只有数字键1 按下,则返回00 00 59 00 00 00 00 00,如果数字

键1 和2 同时按下,则返回00 00 59 5A 00 00 00 00,如果

再按下左shift 键,则返回02 00 59 5A 00 00 00 00,

然后再释放1键,则返回02 00 5A 00 00 00 00 00,

然后全部按键释放,则返回00 00 00 00 00 00 00 00。

这些数据(即报告)都是通过中断端点返回的。当按下Num Lock键时,PC会发送

输出报告,从报告描述符中我们知道,Num Lock的LED对应着输出报告的最低位,

当数字小键盘打开时,输出xxxxxxx1(二进制,打x的由其它的LED状态决定);

当数字小键盘关闭时,输出xxxxxxx0(同前)。取出最低位就可以控制数字键锁定LED了。

下面这个报告描述符是USB鼠标报告描述符,比起键盘的来说要简单些。

它描述了4个字节,第一个字节表示按键,第二个字节表示x轴(即鼠标左右移动,

0表示不动,正值表示往右移,负值表示往左移),第三个字节表示y轴(即鼠标

上下移动,0表示不动,正值表示往下移动,负值表示往上移动),第四个字节

表示鼠标滚轮(正值为往上滚动,负值为往下滚动)。

code char MouseReportDescriptor[52] = {

//通用桌面设备

0x05, 0x01,// USAGE_PAGE (Generic Desktop)

//鼠标

0x09, 0x02,// USAGE (Mouse)

//集合

0xa1, 0x01,// COLLECTION (Application)

//指针设备

0x09, 0x01,//USAGE (Pointer)

//集合

0xa1, 0x00,//COLLECTION (Physical)

//按键

0x05, 0x09,//USAGE_PAGE (Button)

//使用最小值1

0x19, 0x01,//USAGE_MINIMUM (Button 1)

//使用最小值3。1表示左键,2表示右键,3表示中键

0x29, 0x03,//USAGE_MAXIMUM (Button 3)

//逻辑最小值0

0x15, 0x00,//LOGICAL_MINIMUM (0)

//逻辑最大值1

0x25, 0x01,//LOGICAL_MAXIMUM (1)

//数量为3

0x95, 0x03,//REPORT_COUNT (3)

//大小为1bit

0x75, 0x01,//REPORT_SIZE (1)

//输入,变量,数值,绝对值

//以上3个bit分别表示鼠标的三个按键情况,最低位(bit-0)为左键

//bit-1为右键,bit-2为中键,按下时对应的位值为1,释放时对应的值为0

0x81, 0x02,//INPUT (Data,Var,Abs)

//填充5个bit,补足一个字节

0x95, 0x01,//REPORT_COUNT (1)

0x75, 0x05,//REPORT_SIZE (5)

0x81, 0x03,//INPUT (Cnst,Var,Abs)

//用途页为通用桌面

0x05, 0x01,//USAGE_PAGE (Generic Desktop)

//用途为X

0x09, 0x30,//USAGE (X)

//用途为Y

0x09, 0x31,//USAGE (Y)

//用途为滚轮

0x09, 0x38,//USAGE (Wheel)

//逻辑最小值为-127

0x15, 0x81,//LOGICAL_MINIMUM (-127)

//逻辑最大值为+127

0x25, 0x7f,//LOGICAL_MAXIMUM (127)

//大小为8个bits

0x75, 0x08,//REPORT_SIZE (8)

//数量为3个,即分别代表x,y,滚轮

0x95, 0x03,//REPORT_COUNT (3)

//输入,变量,值,相对值

0x81, 0x06,//INPUT (Data,Var,Rel)

//关集合

0xc0,//END_COLLECTION

0xc0// END_COLLECTION

};

通过对上面的报告分析,我们知道报告返回4个字节,没有报告ID。如果鼠标左键按下,

则返回01 00 00 00(十六进制值),如果右键按下,则返回02 00 00 00,如果中键按下,

则返回04 00 00 00,如果三个键同时按下,则返回07 00 00 00。如果鼠标往右移动则

第二字节返回正值,值越大移动速度越快。其它的类推。

这里只对报告描述符做一个简单的介绍,更详细的资料请参看USB HID协议以及HID Usage Tables,

可以从Http://www.usb.org下载。

根据这个实际设计的USB键盘和USB鼠标:

USB键盘:

user1/2198/archives/2007/36484.html

http://www.ednchina.com/blog/computer00/20134/message.aspx

USB鼠标:

user1/2198/archives/2007/36520.html

http://www.ednchina.com/blog/computer00/20135/message.aspx


作者:computer00 2007-5-18 11:19:00
回复1 谢谢Computer00的精彩解释673437

补充一点,HID的报告中有输入报告和输出报告,这些Computer00已经解释得很清楚了,除此之外还有一种功能报告,功能报告是用来传递一些设备状态的。

举个例子,一般键盘上都有三个锁定灯,大小写锁定、数字锁定和滚动锁定,(顺便说一下,00提到的Kana锁定灯是用于输入日文的);在00的例子中这些灯是用输出报告设置的,它们也可以用功能报告设置,这样可以实现上电时预置某些状态,比如要求开机时数字锁定(Num Lock);这样的特性用输出报告只能通过PC端的软件实现了,而用功能报告时,可以在键盘端用硬件设置好(如使用拨动开关或跳线)。


作者:平常人 2007-5-19 18:47:00
回复2 呵呵673438

我找的就是他啊,顶,另外:报告描述符软件还不知道怎么用呢?

oo


作者:isbit 2007-5-19 19:27:00
回复3 呵呵,报告描述符软件自己去摸索一下吧, 我都是乱摸索出来的673439

呵呵,报告描述符软件自己去摸索一下吧, 我都是乱摸索出来的
作者:computer00 2007-5-19 21:23:00
回复4 不会,凑个份子673440

不会,凑个份子
作者:conwh 2007-5-19 21:42:00
回复5 HID里面的机打延时报告描述符怎么设置呀?673441

楼主的对报告描述符的讲解太精辟了.不过我还有个问题,比如说一般键盘都有设置机打延时的,但是在上面的USB报告描述符中只有对LED的设置报告,如果在USB键盘中也要设置机打延时的话,那报告描述符又是怎样的呢?谢谢!


作者:ranjohn 2007-6-6 22:10:00
回复6 HID的报告描述符中没有这个参数673442

一般这个延时是在HOST端解决的。

如果你一定要在设备端解决,唯一的办法就是设备端不断地发报告了。


作者:平常人 2007-6-6 22:14:00
回复7 明了673443

说得很有道理,可能是在主机端根据控制面板里面键盘的设置自已设置好了的。


作者:ranjohn 2007-6-7 22:13:00
回复8 在主机端解决的!673444

经过验证,确实是在主机端解决的。