在USB中USBHOST是通过各种描述符来识别设备的
USB HID报告及报告描述符简介
在USB中,USB HOST是通过各种描述符来识别设备的,有设备描述符,配置描述符,接⼝描述符,端点描述符,字符串描述符,报告描述符等等。USB报告描述符(Report Descriptor)是HID设备中的⼀个描述符,它是⽐较复杂的⼀个描述符。
USB HID设备是通过报告来给传送数据的,报告有输⼊报告和输出报告。输⼊报告是USB设备发送给主机的,例如USB⿏标将⿏标移动和⿏标点击等信息返回给电脑,键盘将按键数据数据返回给电脑等;输出报告是主机发送给USB 设备的,例如键盘上的数字键盘锁定灯和⼤写字母锁定灯等。报告是⼀个数据包,⾥⾯包含的是所要传送的数据。输⼊报告是通过中断输⼊端点输⼊的,⽽输出报告有点区别,当没有中断输出端点时,可以通过控制输出端点0发送,当有中断输出端点时,通过中断输出端点发出。
⽽报告描述符,是描述⼀个报告以及报告⾥⾯的数据是⽤来⼲什么⽤的。通过它,USB HOST可以分析出报告⾥⾯的数据所表⽰的意思。它通过控制输⼊端点0返回,主机使⽤获取报告描述符命令来获取报告描述符,注意这个请求是发送到接⼝的,⽽不是到设备。⼀个报告描述符可以描述多个报告,不同的报告通过报告ID来识别,报告ID在报告最前⾯,即第⼀个字节。当报告描述符中没有规定报告ID时,报告中就没有ID字段,开始就是数据。更详细的说明请参看USB HID协议,该协议可从下载。
USB报告描述符可以通过使⽤HID Descriptor tool来⽣成,这个⼯具可以到下载,为了⽅便⼤家,我顺便上传了⼀份。
下⾯通过由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,设置为常量。
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,
可以从下载。
根据这个实际设计的USB键盘和USB⿏标:
USB键盘:
USB⿏标:
USB⼊门系列之五—— USB设备的插⼊检测机制
USB主机是如何检测到设备的插⼊的呢?⾸先,在USB集线器的每个下游端⼝的D+和D-上,
分别接了⼀个15K欧姆的下拉电阻到地。这样,在集线器的端⼝悬空时,就被这两个下拉电阻拉到了低电平。⽽在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和⾼速设备,上拉电阻是接在D+上;⽽低速设备则是上拉电阻接在D-上。这样,当设备插⼊到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的⼀条拉⾼了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上⼀层的集线器报告给USB主控制器),这样就检测到设备的插⼊了。USB⾼速设备先是被识别为全速设备,然后通过HOS T和DEVICE两者之间的确认,再切换到⾼速模式的。在⾼速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
⼀个简单的实验:只⽤⼀个上拉电阻接在USB的+5V和D+或者D-上,WI NDOWS也会提⽰发现新硬件,但是⽆法到驱动程序。这时去设备管理器⾥⾯看,有显⽰未知USB设备,并且其VID和PID为0。根据这个,我们可以简单的判断设备是否枚举成功。如下图所⽰,分别是枚举不成功和枚举成功的图。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论