HID报文讲解
Posted skdev
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HID报文讲解相关的知识,希望对你有一定的参考价值。
HID报文讲解
1 什么是HID?
HID全称Human Interface Device,人机接口设备,具体指键盘鼠标,蓝牙和USB都采用相同的报文协议,https://www.usb.org/sites/default/files/documents/hid1_11.pdf 协议里有详细描述。
2 什么是HID报文?
HID报文全称为HID Report Description,是一段用指令和数值来描述通信数据结构的数据组,定义了由设备发给PC或手机的数据包里的数据所表示的意思,比如哪个数据是按键,这个是什么按键。
3 报文结构
Collection的意思是集合,如上图,一个Application集用于表示一个虚拟设备,可以包含多个集合和多个报文,一个集合可以包含多个报文,报文描述的是发往PC或手里的数据的意义,也就是描述这个数据表示什么按键。
4 指令和数据
集合和报文是通过指令和数据来描述的,指令为一个字节,后面根据bSize跟着数据,指令结构如下:
Tag标签,Type类型,Size表示数据的个数。
Type值有:
0 = Main
1 = Global
2 = Local
3 = Reserved
根据Type值,在不同的表格里,找Tag的意义
Main类的Tag:
Global类的Tag:
Local类的Tag:
例子:0x05, 0x01
0x09, 0x06
0x05是指令,0x01表示数据
0x05分解开,Tag=0000,Type=01,Size=01
Type=01表示Global类,然后从Global表里找到Tag=0的意思是Usage,即用例,然后在《6 用途表》里找到Global的Usage表,在根据Usage表找到数据值0x01的意思为:Generic Desktop Controls
所以,0x05, 0x01的意思是这是一个普通桌面控制器。
0x09是指令,0x06表示数据
0x09分解开,Tag=0000,Type=10,Size=01
Type=10表示Local类,然后从Local表里找到Tag=0的意思是Usage,即用例然后在《6 用途表》里,去找上面Global指定的子表Generic Desktop表,找到数据值0x06的意思为:Keyboard
所以,0x09, 0x06的意思是这是一个键盘。
通过上面的例子可以知道,Global即父类,Local为子类,先定义父类表包含了各子类表的名称,子类表包含了具体的意思。
5 用途表
https://www.usb.org/sites/default/files/hut1_21_0.pdf
6 简单例子
这是一个只有一个按键的例子:
static uint8_t report_map_data[] =
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // id = 1
0x05, 0x07, // Usage Page (Key codes)
0x09, 0x29, // Usage Esc
0x15, 0x01, // Logical Minimum (1)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x00, // Input (Data, Array)
0xC0 // End Collection (Application)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
从父类表Usage里找到0x01,表示普通桌面控制器
从子类表Generic Desktop Page里找到0x06,表示键盘
0x05和0x09基本是配合使用的,先定位父表,再定位子表。
0xA1, 0x01, // Collection (Application)
...
0xC0 // End Collection (Application)
一个集合的开始和结束
0x85, 0x01, // id = 1
报文ID=1,当有多个集合时,每个集合需要分配不同的ID
0x05, 0x07, // Usage Page (Key codes)
0x09, 0x29, // Usage Esc
从父类表Usage里找到0x07,表示Keyboard
从子类表Keyboard/Keypad Page里找到0x29,表示键盘码0x29为Esc键
这里定义了哪些数据要传给PC或手机
0x15, 0x01, // Logical Minimum (1)
0x25, 0x01, // Logical Maximum (1)
定义传给PC或手机的数据取值范围,有两种取值方式,Array和Variable,比如定义了3个按键,如果三个按键都要发出去,那么选Variable,Logical Minimum 取0,Logical Maximum 取1,0表示没按下,1表示按下;如果只发出哪个按键按下,可以选Array,Logical Minimum取值1,Logical Maximum取值3,表示3个按键索引,发1表示第一个按键,发2表示第二个按键。
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x95和0x75用来表示用到多少字节,Size = 8,即一个键值用到8位,用1个字节来存索引,Count = 1,表示只传一个键值,如果想一次传多个键,则Count可以是2,3,4...。
0x81, 0x00, // Input (Data, Array)
0x81, 0x00用来表示数值的意义,根据Main表,值0x00,第0位是0对应的是Data,第1位是0 对应的是Array,第二位是0对应的是Absolute,所以它是一个Data、Variable、 Absolute的类型。
Array 表示上报的值是索引,也就是说,报文的数据由1个字节组成,这个字节的值如果是1,即表示Esc键按下。
从上面例子可以知道,报文描述的结构是:
定义设备类型
集合
定义报文ID
报文内容量
报文数据值
报文字节数
数据是Arrary还是Variable
Arrary 表示上报1个或几个内容,Variable表示字节数与内容量要一致,可以是位或字节量与内容量相等。
7 键盘例子
下面是一个键盘描述:
static uint8_t report_map_data[] =
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // id
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array) Key array(1 byte)
0xC0
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
定义设备类型
0xA1, 0x01, // Collection (Application)
定义设备类型
0x85, 0x01, // id
定义报文ID为1
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
用到按键码:224、225、226、227、228、229、230、231,这些按键码对应CTRL,ALT,SHIFT等按键
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
取值范围:0和1,0表示未按下,1表示按下
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
用1位表示1个按键,8位表示上面8个按键
0x81, 0x02, // Input (Data, Variable, Absolute)
一个字节表示8个按键状态,即上报全部按键,所以选用Variable数据类型。
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
用到1个字节
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
取值范围:0~101
0x05, 0x07, // Usage Page (Key codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
对应按键码0~101
0x81, 0x00, // Input (Data, Array) Key array(1 byte)
只上报1个按键,1个字节表示一个按键,0对应0按键码,101对应101按键码。
由上可知,集合里有两部分,第一部分是CTRL、SHIFT等功能键,第二部分是0~101键码等普通键,由这两部分组合成了组合键,也就是说,报文是由2个字节构成,第1字节是功能键,第2字节是普通键。
8 鼠标例子
下面是一个鼠标描述:
static uint8_t report_map_data[] =
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, //Report ID (2)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, 0xc0
上报4个字节的数据,第1个字节表示鼠标左键、右键和中键。第2个字节鼠标X偏移,第3个字节Y偏移,第4个字节中间滚轮偏移。
9 键盘和鼠标合一
键盘和鼠标合一有两种方式,1 是都合到同一个Application集合里,2 分两个Application集合。
从上面的分析可以知道,一个Application集合对应一个报文ID,相当于一个设备,所以两个Application集合就是两个设备,是USB设备的话则对应2个端点,是BLE设备的话,对应两个特征。
方式1
static uint8_t report_map_data[] =
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // id
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array) Key array(1 byte)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xC0,
0xC0
方式2
static uint8_t report_map_data[] =
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // id
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array) Key array(1 byte)
0xC0
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, //Report ID (2)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, 0xc0
以上是关于HID报文讲解的主要内容,如果未能解决你的问题,请参考以下文章