HID(human interface device)也就是人机接口设备,我们常见的鼠标键盘游戏手柄等都是HID,操作系统自带HID驱动的,有USB接口的,也有蓝牙接口的,都是相似的通信协议。因此这里参考USB接口的HID协议进行介绍。主要参考https://www.usb.org/hid网站中的两篇文档《Device Class Definition for HID 1.11 》《HID Usage Tables 1.2 》,可以直接在官网下载,也可以从本文附件中下载。
HID有自己协议,叫做报告描述符,用来描述发送的数据代表的含义(并不是发送的数据),包括是什么设备:鼠标、键盘、手柄等,和每一字节的含义。下面以键盘的报告描述符为例具体介绍。
参考例程:
nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_hids_keyboard
main.c函数中的报告描述符如下表。
报告描述符 |
简介 |
0x05, 0x01, // Usage Pg (Generic Desktop) |
这是一个global类Usage Page作用数据 |
0x09, 0x06, // Usage (Keyboard) |
表示这是一个键盘 |
0xA1, 0x01, // Collection: (Application) |
表示下面所包含的是对键盘的解释 |
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,分别代表放开和按下 |
0x75, 0x01, // Report Size (1) 0x95, 0x08, // Report Count (8) |
用 8 个 bit 分别表示 8 个按键的状态 |
0x81, 0x02, // Input: (Data, Variable,Absolute) |
将这 8 个 bit 添加到本报告中 |
0x95, 0x01, // Report Count (1) 0x75, 0x08, // Report Size (8) 0x81, 0x01, // Input: (Constant) |
另外再预留 8 个 bit 备用,暂时没用 |
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 |
0x95, 0x01, // Report Count (1) 0x75, 0x03, // Report Size (3) 0x91, 0x01, // Output: (Constant) |
再增加 3 个 bit,与上面 5 个 bit 组成一个字节 |
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 |
结束 |
表中左边每一列由一个或者两个数字组成,第一个数字是前缀,第二个是数据(可无)
例如:0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x05表示前缀,0x01为数据部分,先看前缀,0x05转换成二进制,就是00000101,按照HID类协议的定义,这个字节被分成3个部分:(参考hid1_11.pdf)
bit0~bit1代表的是这个前缀后面跟的数据长度,两位可以表示最大4字节的数据,即bsize
bit2~bit3代表的是这个前缀的类型,总共可以有三种类型:00=main,01=global,10=local,11=reserved;
bit4~bit7代表前缀的tag,这里根据bit2~bit3的不同类型有不同的定义,例如global类型下的定义如下图。(参考hid1_11.pdf)
0000就表示Usage Page。所以前缀0x05代表的含义就是:一个global类型Usage Page作用数据量为1的前缀。
那我们做一个练习题:第二行出现的0x09,二进制就是:00001001,这就是local类型Usage Page作用数据量为1的前缀(local类型具体可以查看hid1_11.pdf的Local Items章节)。
下面来看数据0x01的作用
前缀是global类型Usage Page作用,那么这就要查看Usage Page作用的具体介绍。(参考hut1_2.pdf)
0x01表示Generic Desktop,表示桌面控制类型的设备,但具体是什么设备就要看第二行。
前面介绍了第二行0x09表示local类型Usage Page作用,参数为0x06,0x06表示键盘,查表如下。
这里主要就是介绍报告描述符的查阅方法,其他的描述符同样可以按照上述方法进行查阅。
键盘描述符的后定义的是发送数据的类型与格式。如下:
翻译出来就是,该协议支持一次最多发送6个按钮(键值0~101),支持最多同时按下8个功能按键(键值224~231)。键值表详见附件。
举例
例如我想要发送向上按钮,查表得出向上的键值是0x52(82),所以发送的8Byte数据为。
Byte0 |
Byte1 |
Byte2 |
Byte3 |
Byte4 |
Byte5 |
Byte6 |
Byte7 |
0x00 |
0x00 |
0x52 |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |
例如我想发送Ctrl+A,查表得出左Ctrl的键值为0xE0(224),也就是第一字节的第一位为1,A的键值为4。所以发送的8Byte数据为。
Byte0 |
Byte1 |
Byte2 |
Byte3 |
Byte4 |
Byte5 |
Byte6 |
Byte7 |
0x80 |
0x00 |
0x04 |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |