USB学习小记-HID类键盘的报告描述符的理解
前言
断断续续的学习了将近三个月,才把USB的HID类搞明白,速度真是够慢的。利用晚上+周末的时间学习自己的东西确实是必要的,不过效率是有点低,以后要更专注一些才行,希望自己能做到吧。
在学习过程中,刚开始主要参考了周立功编写的一本《PDIUSBD12 USB 固件编程与驱动开发》,后面的学习主要参考电脑圈圈的资料包,包括里面的HID类的英文协议文档,还有一位台湾前辈几年前写的几篇文章,还有网络下有下得到的一些例程。在此感谢各位大虾前辈的分享。
一、学习流程
1,先大致看下USB1.1中文版的协议(就是网上能找到的翻译版),先了解一下USB1.1的工作流程(可能此时的你会对其中很多内容都很含糊,不过没关系,请坚持!);
2,选择一款最常用的USB芯片,比如我选择的NXP的PDIUSBD12.有很多使用该芯片的源码可以在上网搜索得到,而且周立功公司为其写了一本书,前面的章节对USB的工作流程作了一个简单而又清晰的讲解,而不致让人陷入协议的海洋里;
3,有了对USB1.1协议的大致了解,选好了开发的平台(我刚好手上有一块周立功公司的EASEARM2200的开发板,上面有D12)之后,先跑一下附带的例程(此开发板配套的例程是基于UCOS2系统的,刚开始用它来参考肯定晕)。没有能跑的例程,那就上网找一下经验证的例程,比如电脑圈圈在EDNCHINA建立的USB学习小组里有很好的几个例程,而且都是基于51+D12的,所以极具参考价值。我是参考周立功公司出的那本书来学习的,电脑圈圈的例程与此书的例子书写风格较相似,所以可以互相参考。如果也没有开发板,那可购买一套电脑圈圈他们搞活动的套件或者直接用51+D12自己搭一下,这样就可以直接用电脑圈圈写的例程了,可以避免走很多弯路。
4,有了例程的直观印象后,此时可以上BUS HOUND5.0了。此软件可以观察到USB设备与主机(PC)之间的通讯数据,特别那11个标准请求的理解,通过此软件的观察,可以更好理解其相互之间是如何完成这个握手枚举过程的。
5,好了,有了之前的准备工作,是否打算自己做一个设备了呢?嗯,如果学习是从易到难,那效率是事半功倍。USB分为好几个大类,最简单的类是HID类,即人机接口类,那可以从此入手。最常见的人机接口类设备就是鼠标键盘了。所以我们可以动手制作一个鼠标或键盘作为第一个实践例子。
6,在真正动手写程序之前,还需要加深对USB枚举过程的理解和熟悉,特别是其流程。而其中最重要的就是对11个标准请求的理解了。枚举的过程就是设备对主机发过来的这些请求进行正确响应,告诉主机,“我”是一个什么样的设备,各种属性均是什么,完成枚举后,主机才能根据“我”的属性,对“我”发送过去的数据进行正确的解释。
7,OK,那开始写程序了,我们选择做一个键盘作为第一个例子。用D12的朋友,在阅读过D12的数据手册后,参考电脑圈圈的例子,看其最底层的驱动是如何写的,先把这部分的驱动完成,再谈协议的实现。
《1》USB器件最底层的驱动编写;
《2》11个标准请求函数的编写;
《3》6个HID类请求函数的编写;(此6个函数很简单)
《4》USB中断部分的编写,可用查询或中断法,根据D12的中断寄存器的值,去调用11个标准请求函数;
《5》编写描述符;键盘要用到的描述符包括:1,设备描述符,2,配置描述符,3,接口描述符,4,HID描述符,5,端点描述符,6,报告描述符,7,字符串描述符。其中4,6为HID类专有的描述符,7是可选的。
当完成上面5个部分的函数编写后,如果编写正确,如果你对枚举流程理解正确,如果描述符没错(可直接使用网上的例子的),那么此时应该能够枚举成功了,如果不正确,请分部检查以上5个部分的函数。
8,完成第7步后,能枚举成功,可以说是成功一大半了。此时就考虑如何发数据发出去即可。对于HID类设备,特别是鼠标键盘这类的,只需你能按照报告描述符所描述的格式,把数据发送过去,那么PC端即会对你所发送的数据进行响应了,也就是你的键盘能输入数据了。
整个HID类的,对于键盘鼠标这类设备的开发即可算是结束了。虽然步骤就是那么多,不过想最后成功,需要参考大量的资料,最重要是是英文的HID协议,里面有对描述符的详细讲解,还有例子。还有周立功公司的那本书,虽然后面的看不懂也暂时用不上,不过前面的结合D12这个芯片还是很有参考价值的。其它的书可以先不看吧,觉得没看也没什么。
二、键盘的报告描述符的理解
在参考别人的例程实现了键盘跑起来的时候,你这时候应该会想问的是,为什么描述符要这样写呢?
好的,我当初也有同样的疑问,那下面来简单说说键盘的报告描述符的含义。其它的描述符含义很明显,这里就不作详细讲解。
报告描述符是HID类设备最重要的描述符,其实它相当于一个大的设备属性表,在主机端会有一个叫做Parser的东西,对在枚举阶段接收到的报告描述符进行解释,以完成对该HID设备的属性的了解。
由于电脑圈圈前辈已经对这部分有过较详细的讲解了,我这只作为补充,供各位参考。
键盘的报告描述符:
] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
//(1)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
//(2)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
//(3)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
//(4)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
//(5)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
};
在这为了容易表达,把上面键盘的报告描述符除开头与尾分成五部分
(1)这部分实际上为键盘的八个控制键,包括:左/右CTL,在/右ALT,在/右SHIFT,左/右WIN键盘,所以其范围为如下所示(HID Usage Tables.pdf从54页开始,展示了所有的keyborad page)
1>
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
2> 八个键一个键对应于一个位所以:
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
report size单位为bit,report count为8,所以1*8共占用一个字节;
3>由于按键的值要么为1(按下),要么为0(松开),所以逻辑最大值为1,最小值为0
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
4>由于按键为输入(对主机来说),所以为INPUT,并且为数据(Data),变量(var),绝对值(Abs)
0x81, 0x02, // INPUT (Data,Var,Abs)
(2)这部分由于键盘数据的八个字节的第二个字节是保留的(第一个字节就是上面所描述的控制键部分),所以
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
1bit*8 = 1 byte ,前且为常量。
(3)该部分LED输出,即如键盘上的大小灯,数字锁定灯等,只用了五个,所以report count为5.
(4)由于(3)部分只用了5个bits,但发送肯定是一字节一字节地发送,所以要把不用的3个bits也要凑起来,但其又是没有实际意义的,所以定义为常量。
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
(5)这部分主要就是六个字节的键盘键值了(一个字节表示一个键),所以一次最多可以发送六个键值,即六个按键按下(当然有没有效,操作系统说了算,一般三个键同时按下系统就报错),所以这一整个报告描述符,包括一组输入的键,一组输出的LED。键一共有八个字节,即一起发送要发八个字节的数据,第1个字节是八个控制键,第2个字节是保留,第三至第八个字节为普通按键键值,没有固定位置,只需要往上填上HID Usage Tables上的键值系统即会确认为该键按下。输出的LED只有一个字节,一个位对应一个LED灯,只使用了五个位。
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
report size为8bits,有6个,所以刚好是六个字节。
其它的跟上面介绍一样的意思,不再多说。
简单解释后,我们来看看上一篇博文里附带的例程中的键值发送部分:
:
break;
:
break;
:input_buf[] = 0x53;//NUM LOCK
break;
:input_buf[] = 0x58;//回车
break;
:input_buf[] = 0x59;//1 (以下均为小键盘数字,需按下NUM LOCK)
break;
:input_buf[] =
break;
:input_buf[] =
break;
:input_buf[] =
break;
:input_buf[] =
break;
:input_buf[] =
break;
:input_buf[] =
break;
:input_buf[]=
break;
:input_buf[] =
break;
:input_buf[] = 0x55;//*
break;
:input_buf[] = 0x56;//-
break;
:input_buf[] = 0x53;//NUM LOCK
break;
default:
break;
其中,input_buf为八个字节大小的数组,发送时直接发送这个数组即可完成键值传送。input_buf[0]没用上,记得是一位对一个控制键即可,自己根据情况加上。
input_buf[1]为保留字节,所以不使用。其余的,从input_buf[2]-input_buf[7]可以直接给键值,而且不限位置,所以一个101键的标准键盘可以使用这六个字节全部表达完毕,确实是很巧妙的写法,不得不佩服制定协议的前辈们。
好了,报告描述符的介绍就说到这,鼠标的也是同样的分析方法。另外在电脑圈圈的例程里,还有HID英文协议里,USB之人性化介面装置的报告描述元(1)(2)(3)三篇(林锡宽)这些文章也很详细讲解了报告描述符,建议在看这篇博文前,先把刚刚提到的这几篇文章,例程,英文协议的相关部分过目理解一遍后再看,才可起到辅助的作用。


USB学习小记-HID类键盘的报告描述符的理解的更多相关文章
- USB HID设备报告描述符详解(转)
转自:http://group.ednchina.com/93/198.aspx. 参考:USB HID usage table 概述: 报告在这里意思是数据传输(data transfer),而 ...
- usb的hid鼠标键盘报告描述符(五)
title: usb的hid鼠标键盘报告描述符 tags: linux date: 2018/12/20/ 18:05:08 toc: true --- usb的hid鼠标键盘报告描述符 https: ...
- USB HID报告及报告描述符简介
在USB中,USB HOST是通过各种描述符来识别设备的,有设备描述符,配置描述符,接口描述符,端点描述符,字符串描述符,报告描述符等等.USB报告描述符(Report Descriptor)是HID ...
- USB HID Report Descriptor 报告描述符详解
Report descriptors are composed of pieces of information. Each piece of information is called an Ite ...
- HID 报告描述符精细说明.
1,报告描述符概述 1.1) 报表描述符 报表描述符和USB的其他描述符是不一样的,它不是一个简单的表格,报表描述符是USB所有描述符中最复杂的.报表描述符非常复杂而有弹性,因为它 ...
- 浅析USB HID ReportDesc (HID报告描述符)
在USB中,USB Host是通过各种描述符来识别识别设备的,一般在设备枚举的过程将会获取有设备描述符/配置描述符/接口描述符/端点描述符/字符串描述符等 现在我们来介绍一下HID ReportDes ...
- Python笔记(4)类__属性与描述符
部分参考自:http://www.geekfan.net/7862/ 新式类与经典类 2和3不一样,3都是新式类. 新式类和经典类的区别: class A: #classic class " ...
- Python属性、方法和类管理系列之----描述符类
什么是描述符类? 根据鸭子模型理论,只要具有__get__方法的类就是描述符类. 如果一个类中具有__get__和__set__两个方法,那么就是数据描述符,. 如果一个类中只有__get__方法,那 ...
- python小知识-属性查询优先级(如果有同名类属性、数据描述符、实例属性存在的话,实例>类>数据描述符)
https://www.cnblogs.com/Jimmy1988/p/6808237.html https://segmentfault.com/a/1190000006660339 https:/ ...
随机推荐
- performance_schema 变量
performance_schema 这个变量来控制performance_schema库的禁用和启用 1.performance_schema的值说明 performance_schema=1 表示 ...
- wordpress教程之函数site_url()、home_url()、bloginfo(‘url’)的区别
在wordpress插件和主题开发中经常需要获取各种URL路径,wordpress提供了以下集中方法获得URL路径: plugins_url() — 插件目录的 URL (例如:http://www. ...
- g++ error: expected ‘)’ before ‘*’ token
原本*号前面的类型是我用typedef自定义的类型的,MyType* const p: 发生这样的错误是,编译器根本不知道MyType是什么东西,这是我在C++多重继承中遇到的.MyType是我在基类 ...
- You don't seem to have 'make' or 'gmake' in your PATH
标题尚的错误,不是原因,因为which make的时候是有显示/usr/bin/make的,echo $PATH的时候也是有make的路径的. 编译Qt ./configure的时候出现标题上错误, ...
- python list列表 方法总结
深入链表(most on lists) The list data type has some more methods. Here are all of the methods of list ob ...
- 一个C#多线程的工作队列
多线程添加元素到队列中,队列根据绑定 的事件进行自动处理,可以设置WorkSequential属性来实现对队列处理的单线程(严格顺序处理)或者多线程处理(循序出队,但是 多线程处理,不保证对队列元素的 ...
- Best Time to Buy and Sell Stock III 解答
Question Say you have an array for which the ith element is the price of a given stock on day i. Des ...
- cenos 安装 phpredis 扩展
1. php -m 可以查看 php 所有的已经安装的扩展
- 总结FormsAuthentication的使用
一.先看一下使用FormsAuthentication做登录认证的用法 用法一: FormsAuthentication.SetAuthCookie(username, isPersistent); ...
- HBase--DependentColumnFilter(参考例过滤器 )详解
DependentColumnFilter是一种允许用户指定一个参考列或引用列来过滤其他列的过滤器,过滤的原则是基于参考列的时间戳来进行筛选 . 官方说明: 大意:此过滤器提供两个参数--列族和列限定 ...