1、样例解读
下面是一个伪代码描述的样例,其中涉及到的页码可查看第 5 段用途表:
Usage Page (Generic Desktop), //定位到Generic Desktop页,这个相当于指针跳转一样的东西
Usage (Mouse), //指定Generic Desktop里的mouse,表示这是一个鼠标
Collection (Application), // Collection Application,是对Mouse的解释
Usage (Pointer), //表示指针形式
Collection (Physical), // Collection Physical,是对Pointer的解释
Report ID (0A), //id为0x0A的报告
Usage (X), Usage (Y), //上报X,Y两个数据
Logical Minimum (-127), //Report data values range from -127
Logical Maximum (127), //X,Y的取值范围是-127~127
Report Size (8), Report Count (2), //总共要上报2个字节,即x一个字节,y一个字节
Input (Data, Variable, Relative), //将X,Y这两个字节添加到0x0A的报告里,且这两个值是可写并且是相对的
Logical Minimum (0),
Logical Maximum (1), //下面Button的取值范围是0~1
Report Size (1), Report Count (3), //3个1位的数据
Usage Page (Button Page),//是一个BUTTON
Usage Minimum (1),
Usage Maximum (3),//共有 BUTTON1~BUTTON3,即总共有 3 个 BUTTON
Input (Data, Variable, Absolute),//将3个分别代表的BUTTON1,BUTTON2,BUTTON3的位添加到0x0A的报告
Report Size (5),
Input (Constant), //增加5个无效的位与上面3位凑成一个字节
End Collection,
End Collection
综上所示,上面样例所表达的意思就是下图所示的:
这里先简单介绍用图表(鼠标):具体看 HID Usage Tables 1.12(用图表).pdf ,或下一章
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
再 举 一 个 实 际 的 例 子 进 行 解 读 , 下 面 是 摘 自 TI CC2540/CC2541 SDK 里 的BLE-CC254x_v1.4.0\Projects\ble\Profiles\HIDDevKbM\hidkbmservice.c
static CONST uint8 hidReportMap[] =
{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report Id (1)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x05, 0x09, // Usage Page (Buttons)
0x19, 0x01, // Usage Minimum (01) - Button 1
0x29, 0x03, // Usage Maximum (03) - Button 3
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x03, // Report Count (3)
0x81, 0x02, // Input (Data, Variable,Absolute) - Button states
0x75, 0x05, // Report Size (5)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Constant) - Padding or Reserved bits
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, Variable, Relative) - X & Y coordinate
0xC0, // End Collection
0xC0, // End Collection
0x05, 0x01, // Usage Pg (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection: (Application)
0x85, 0x02, // Report Id (2)
0x05, 0x07, // Usage Pg (Key Codes)
0x19, 0xE0, // Usage Min (224)
0x29, 0xE7, // Usage Max (231)
0x15, 0x00, // Log Min (0)
0x25, 0x01, // Log Max (1)
// Modifier byte
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input: (Data, Variable,Absolute)
// Reserved byte
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input: (Constant)
// LED report
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Pg (LEDs)
0x19, 0x01, // Usage Min (1)
0x29, 0x05, // Usage Max (5)
0x91, 0x02, // Output: (Data, Variable,Absolute)
// LED report padding
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output: (Constant)
// Key arrays (6 bytes)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Log Min (0)
0x25, 0x65, // Log Max (101)
0x05, 0x07, // Usage Pg (Key Codes)
0x19, 0x00, // Usage Min (0)
0x29, 0x65, // Usage Max (101)
0x81, 0x00, // Input: (Data, Array)
0xC0, // End Collection
0x05, 0x0C, // Usage Pg (Consumer Devices)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report Id (3)
0x09, 0x02, // Usage (Numeric Key Pad)
0xA1, 0x02, // Collection (Logical)
0x05, 0x09, // Usage Pg (Button)
0x19, 0x01, // Usage Min (Button 1)
0x29, 0x0A, // Usage Max (Button 10)
0x15, 0x01, // Logical Min (1)
0x25, 0x0A, // Logical Max (10)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data,Ary,Abs)
0xC0, // End Collection
0x05, 0x0C, // Usage Pg (Consumer Devices)
0x09, 0x86, // Usage (Channel)
0x15, 0xFF, // Logical Min (-1)
0x25, 0x01, // Logical Max (1)
0x75, 0x02, // Report Size (2)
0x95, 0x01, // Report Count (1)
0x81, 0x46, // Input (Data, Var, Rel, Null)
0x09, 0xE9, // Usage (Volume Up)
0x09, 0xEA, // Usage (Volume Down)
0x15, 0x00, // Logical Min (0)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data, Var,Abs)
0x09, 0xE2, // Usage (Mute)
0x09, 0x30, // Usage (Power)
0x09, 0x40, // Usage (Menu)
0x09, 0xB1, // Usage (Pause)
0x09, 0xB2, // Usage (Record)
0x0a, 0x23, 0x02, // Usage (Home)
0x0a, 0x24, 0x02, // Usage (Back)
0x09, 0xB3, // Usage (Fast Forward)
0x09, 0xB4, // Usage (Rewind)
0x09, 0xB5, // Usage (Scan Next)
0x09, 0xB6, // Usage (Scan Prev)
0x09, 0xB7, // Usage (Stop)
0x15, 0x01, // Logical Min (1)
0x25, 0x0C, // Logical Max (12)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data, Ary,Abs)
0x09, 0x80, // Usage (Selection)
0xA1, 0x02, // Collection (Logical)
0x05, 0x09, // Usage Pg (Button)
0x19, 0x01, // Usage Min (Button 1)
0x29, 0x03, // Usage Max (Button 3)
0x15, 0x01, // Logical Min (1)
0x25, 0x03, // Logical Max (3)
0x75, 0x02, // Report Size (2)
0x81, 0x00, // Input (Data,Ary,Abs)
0xC0, // End Collection
0x81, 0x03, // Input (Const, Var,Abs)
0xC0 // End Collection
};
上面用红蓝绿区分出三大应用功能,分别鼠标、键盘和 Consumer,每个应用功能都是用CollectionApplication 括起来的。
我们先来解析鼠标的报告描述:
0x05, 0x01, // Usage Page (Generic Desktop)
0x04 代表是 Global 类的 Usage Page 功能,最位 2 位表示带多少个字节的数据,因为只带1 个数据,所以是 1,跟 0x04 组合起来就是 0x05 了。其他名称的意思都差不多,数值可以参照上一章的Generic Item Format
0x09, 0x02, // Usage (Mouse)
表示这是一个鼠标, Usage 是为了给对方解析数据时有个参照
0xA1, 0x01, // Collection (Application)
0xA1, 0x01 表示 CollectionApplication ; 0xA1, 0x00 表示 Collection Physical.表示下面所包含的是对 Mouse 的解释
0x85, 0x01, // Report Id (1)
该报告对应的 ID 是 1
0x09, 0x01, // Usage (Pointer)
这是个指针形式
0xA1, 0x00, // Collection (Physical)
下面所包含的是对指针的解释
0x05, 0x09, // Usage Page (Buttons)
下面定义的是按键
0x19, 0x01, // Usage Minimum (01) - Button 1
0x29, 0x03, // Usage Maximum (03) - Button 3
总共有 3 个按键
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
按键的值是 0 和 1,表示放开和按下
0x75, 0x01, // Report Size (1)
0x95, 0x03, // Report Count (3)
有 3 个 1 位,即用 3bits 分别对应三个按键
0x81, 0x02, // Input (Data, Variable,Absolute) - Button states
将这三个位加入本报告的数据中,这三位是可读写的绝对值
0x75, 0x05, // Report Size (5)
0x95, 0x01, // Report Count (1)
定义 1 个 5 位的数据
0x81, 0x01, // Input (Constant) - Padding or Reserved bits
将这个数据添加到本报告的数据中,主要是与前面 3 位组成一个字节,这 5 位是 Constant数据
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x38, // Usage (Wheel)
下面定义的是 X,Y,Wheel 三个功能
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
X,Y,Wheel 的取值范围是-127~127
0x75, 0x08, // Report Size (8)
0x95, 0x03, // Report Count (3)
用三个字节来表示 x,y,wheel
0x81, 0x06, // Input (Data, Variable, Relative) - X & Y coordinate
将这三个字节添加到本报告中
0xC0, // End Collection
0xC0, // End Collection
上面解析出来的数据格式如下:
我们来看一下,如果要发一个鼠标的坐标,该如何发:
static void hidEmuKbdSendMouseReport( uint8 buttons )
{
uint8 buf[HID_MOUSE_IN_RPT_LEN];
buf[0] = buttons; // Buttons
buf[1] = 0; // X
buf[2] = 0; // Y
buf[3] = 0; // Wheel
HidDev_Report( HID_RPT_ID_MOUSE_IN, HID_REPORT_TYPE_INPUT,
HID_MOUSE_IN_RPT_LEN, buf );
}
从上面函数可以看到,X,Y 在第 2、3 个字节,结合上面的数据格式图可以看出,正好是对应的。
我们接着解析键盘的报告描述:
0x05, 0x01, // Usage Pg (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
这是一个键盘
0xA1, 0x01, // Collection: (Application)
0x85, 0x02, // Report Id (2)
本报告的 ID 是 2
0x05, 0x07, // Usage Pg (Key Codes)
下面定义的是按键码
0x19, 0xE0, // Usage Min (224)
0x29, 0xE7, // Usage Max (231)
按键码分别是 224~231,共总有 8 个按键码
0x15, 0x00, // Log Min (0)
0x25, 0x01, // Log Max (1)
按键码的值是 0 和 1,分别代表放开和按下
// Modifier byte
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
用 8 个 bit 分别表示 8 个按键的状态
0x81, 0x02, // Input: (Data, Variable,Absolute)
将这 8 个 bit 添加到本报告中
// Reserved byte
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input: (Constant)
另外再预留 8 个 bit 备用,暂时没用
// LED report
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
定义 5 个 1bit
0x05, 0x08, // Usage Pg (LEDs)
这是 LED
0x19, 0x01, // Usage Min (1)
0x29, 0x05, // Usage Max (5)
5 个 bit 分别对应 LED1~LED5
0x91, 0x02, // Output: (Data, Variable,Absolute)
将这 5 个 bit 添加到本报告中,LED 需要作为 OUT
// LED report padding
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output: (Constant)
再增加 3 个 bit,与上面 5 个 bit 组成一个字节
// Key arrays (6 bytes)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
定义 6 个字节
0x15, 0x00, // Log Min (0)
0x25, 0x65, // Log Max (101)
每个字节的取值范围是 0~101
0x05, 0x07, // Usage Pg (Key Codes)
这个也是键盘码
0x19, 0x00, // Usage Min (0)
0x29, 0x65, // Usage Max (101)
分别是键盘码 0~键盘码 101
0x81, 0x00, // Input: (Data, Array)
将这 6 个字节添加到本报告中,表示同时可产生 6 个键值。
0xC0, // End Collection
上面解析出来的数据格式如下:
Input 和 Out 是不同的两条通道。现在我们来看一下,如果要发一个按键 K0~K101,需要怎么发,如下:
static void hidEmuKbdSendReport( uint8 keycode )
{
uint8 buf[HID_KEYBOARD_IN_RPT_LEN];
buf[0] = 0; // Modifier keys
buf[1] = 0; // Reserved
buf[2] = keycode; // Keycode 1
buf[3] = 0; // Keycode 2
buf[4] = 0; // Keycode 3
buf[5] = 0; // Keycode 4
buf[6] = 0; // Keycode 5
buf[7] = 0; // Keycode 6
HidDev_Report( HID_RPT_ID_KEY_IN, HID_REPORT_TYPE_INPUT,
HID_KEYBOARD_IN_RPT_LEN, buf );
}
上面函数可以看到,它是放在第 3 个字节,结合数据格式图可以看出,第 3 个字节开始,刚好是在 K0~K101 的按键区。
我们最后来解析 Consumer 的报告描述:
0x05, 0x0C, // Usage Pg (Consumer Devices)
0x09, 0x01, // Usage (Consumer Control)
这是个 Consumer 控制
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report Id (3)
本报告 ID 为 3
0x09, 0x02, // Usage (Numeric Key Pad)
下面定义的是数字键盘
0xA1, 0x02, // Collection (Logical)
0x05, 0x09, // Usage Pg (Button)
下面定义的是按键
0x19, 0x01, // Usage Min (Button 1)
0x29, 0x0A, // Usage Max (Button 10)
分别是 Button1~Button10
0x15, 0x01, // Logical Min (1)
0x25, 0x0A, // Logical Max (10)
每个按键的取值范围为 1~10
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
1 个 4bit 的值,来表示键值 1~10,这个值是哪个就表示哪个键按下。
0x81, 0x00, // Input (Data,Ary,Abs)
将这 4bit 添加到本报告中
0xC0, // End Collection
0x05, 0x0C, // Usage Pg (Consumer Devices)
0x09, 0x86, // Usage (Channel)
这里定义的是频道
0x15, 0xFF, // Logical Min (-1)
0x25, 0x01, // Logical Max (1)
频道值范围是-1~1,这里应该只用到-1 和 1,表示频道+和-
0x75, 0x02, // Report Size (2)
0x95, 0x01, // Report Count (1)
用一个 2bit 来表示,第 1 个 bit 表示频道+,第二个表示频道-
0x81, 0x46, // Input (Data, Var, Rel, Null)
将这个 2bit 加到本报告中
0x09, 0xE9, // Usage (Volume Up)
0x09, 0xEA, // Usage (Volume Down)
定义两个按键,音量加和音量减
0x15, 0x00, // Logical Min (0)
按键值为 0~1,这里少了 Logical Max,继承上面的 Logical Max=1
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
定义 2 个 1bit,每个 bit 代表一个键
0x81, 0x02, // Input (Data, Var,Abs)
将 2 个 1bit 添加到本报告中
0x09, 0xE2, // Usage (Mute)
0x09, 0x30, // Usage (Power)
0x09, 0x40, // Usage (Menu)
0x09, 0xB1, // Usage (Pause)
0x09, 0xB2, // Usage (Record)
0x0a, 0x23, 0x02, // Usage (Home)
0x0a, 0x24, 0x02, // Usage (Back)
0x09, 0xB3, // Usage (Fast Forward)
0x09, 0xB4, // Usage (Rewind)
0x09, 0xB5, // Usage (Scan Next)
0x09, 0xB6, // Usage (Scan Prev)
0x09, 0xB7, // Usage (Stop)
定义 12 个按键
0x15, 0x01, // Logical Min (1)
0x25, 0x0C, // Logical Max (12)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
用一个 4 位来存储 1~12,1 表示 Mute … 12 表示 Stop
0x81, 0x00, // Input (Data, Ary,Abs)
将这个 4bit 添加到报告中
0x09, 0x80, // Usage (Selection)
0xA1, 0x02, // Collection (Logical)
0x05, 0x09, // Usage Pg (Button)
这是按键
0x19, 0x01, // Usage Min (Button 1)
0x29, 0x03, // Usage Max (Button 3)
分别是 Button1~Button3
0x15, 0x01, // Logical Min (1)
0x25, 0x03, // Logical Max (3)
每个按键取值范围是 1~3
0x75, 0x02, // Report Size (2)
这里缺少了 Report Count,继承上面的 Report Count =1,也就是用 1 个 2bit 来存储 1~3
0x81, 0x00, // Input (Data,Ary,Abs)
将 1 个字节添加到本报告中
0xC0, // End Collection
0x81, 0x03, // Input (Const, Var,Abs)
再补充 2 个 bit 将上面的凑成一个字节,Report Size=2 和 Report Count =1 继承上面的。
0xC0 // End Collection
修改后,解析出来的数据格式如下:
我们来看一下,如果要发音量+/-键该怎么发:
static void hidCCSendReport( uint8 cmd, bool keyPressed )
{
// Only send the report if something meaningful to report
uint8 buf[HID_CC_IN_RPT_LEN] = { 0, 0 };
// No need to include Report Id
if ( keyPressed )
{
hidCCBuildReport( buf, cmd );
}
HidDev_Report( HID_RPT_ID_CC_IN, HID_REPORT_TYPE_INPUT,
HID_CC_IN_RPT_LEN, buf );
}
在 hidCCBuildReport 对音量加减解析出来是:
音量加是:buf[0] = 0x40, buf[1] = 0x00
音量减是:buf[0] = 0x80, buf[1] = 0x00
正好与数据格式图中第一个字节的第 6 位和第 7 位相对应。