前言
市场上有很多的 HID 设备,包括键盘,鼠标,游戏手柄等。我也做过几个这方面的项目,但是感觉一知半解,仅仅做到了会使用完成了项目,但是遇到一些问题需要很长的时间来解决。因此在这里狠狠的总结了下,写的有问题的地方也希望大家指正,一起进步。
摘要
HID 全称 Human Interface Device,翻译为人类接口设备,该术语由微软 Mike Van Flandern 提出,USB 委员会创建了一个工作组(Human Input Device) 后该工作组更名为 Human Interface Device。所以可以在 USB 委员会查找协议相关的资料, 例如 HID Usage Tables 和 Device Class Definition for HID 1.11。在蓝牙中有了 BLE HID,但是BLE HID 是在 USB HID 规范的基础上又做了一个规范,设备的驱动还是使用 USB HID,仅仅是在蓝牙数据通信上做的规范,如遇到蓝牙部分比较深入的问题可以参考蓝牙规范
USB HID 简介
USB 角色
- Host 主机角色。例如: 电脑,手机,PDA 等属于 host 角色
- Device 设备角色。例如: 键盘,鼠标,游戏手柄等与人交互属于 device 角色
HID Channel
- HID 有两个 Channel
-
Control Channel
-
Interrupt Channel
- 在这两个通道上传输数据也有两个不同的名称
- report 在 Control Channel 传输 称为 synchronous reports
- report 在 Interrupt Channel 传输 称为 asynchronous reports
注意: Feature report 仅仅使用 synchronous reports
HID Reports
BLE 的 HID Reports 支持 3 种,以 Host 角色来看分为 3 种 Input,Output,Feature
USB HID 协议传输数据的两种方式
- 中断方式
Device(键盘)按键按下后,发送数据给 Host(电脑)
Host(电脑)产生中断,收到数据,解析数据后执行按键的动作
- 查询方式
Host(电脑) 发送控制传输(set report)给 Device(键盘)请求发送数据
Device(键盘)收到后发送自己的状态给 Host(电脑)
Host(电脑) 收到后回复一个状态给 Device(键盘) [0 字节表示成功]
HID Report Modes
hid report modes 有两种 boot protocol 和 report protocol
为什么有这两种模式那?
这要从 USB HID 中的这两种特殊的设备,鼠标和键盘
在电脑进入 BIOS 时,鼠标和键盘也要有作用,此时不用解析设备描述符,以固定的格式发送数据保证鼠标键盘正常使用,此时使用 boot protocol 来上报数据
电脑正常启动后会解析鼠标键盘的设备描述符,此时使用 report protocol 的方式来上报数据
注意: report protocol 方式比 boot protocol 多出更多的按键
代码中具体体现
设备描述符介绍
设备描述符主要用于 Host 一端解析 Device 端发送数据
分为两类:short items 和 long items
long items 不常使用,了解也不深。等到后续使用到了再补充,本文仅介绍 short items
下文截图采用了hid1_11.pdf 第六章节部分
short items
- 长度 1-5bytes 包含 1 或 0byte 可选数据
- 结构
这里注意 bSize 的长度只有 0,1,2,4Bytes
根据 bType 又分为 3 类,由于文档过长这里不做截图,做了一个表格方便大家查阅
item | 功能 |
---|---|
Main Items | 用于定义或分组特定类型的数据字段报告的描述符。分两种类型 注意:由于 bSize 不确定所以最后两字节不确定这里用 ? 来表示 二进制用 n 来表示 1. 数据类型 eg: input,output,feature 2. 非数据类型 collection ,end collection Input: 0x8? 1000 00nn 比如按键 Output:0x9? 1001 00nn 比如键盘灯 Feature:0xb? 1011 00nn 描述可以发送到设备的配置信息 Collection: 0xa? 1010 00nn 和 End Collection 是一个组合比如把键盘的 input 和 output 设备描述符作为一个集合 End Collection: 0xc? 1100 00nn |
Global Items | 用来选择用途页,定义数据域的长度、数量、报告 ID 等 Global Items 出现后对所有的 Mian Items 都有效,除非遇到另外一个 Global Items 来改变他 Usage Page: 0x0? 0000 01nn 用途页 Logical Minimum:0x1? 0001 01nn 逻辑最小值 Logical Maximum:0x2? 0010 01nn 逻辑最大值 Physical Minimum: 0x3? 0011 01nn 物理最小值 Physical Maxmum: 0x4? 0100 01nn 物理最大值 Unit Exponent: 0x5? 0101 01nn 以 10 为单位指数值 Unit : 0x6? 0110 01nn unit 值 Report Size: 0x7? 0111 01nn 上报数据的大小单位 bit Report ID : 0x8? 1000 01nn 上报数据的 ID 比如鼠标键盘一起可以用 ID 区分上报的为鼠标还是键盘 Report Count: 0x9? 1001 01nn 上报数据的个数,和 report 组合可得出上报的字节大小 Push: 0xa? 1010 01nn 将 global items 放在堆栈上 Pop: 0xb? 1011 01nn 用堆栈顶部数据替换 items |
Local Items | 定义了控件的特征,只在局部有效,遇到下一个 Main Items 有效结束 Usage: 0x0? 0000 10nn 用途 Usage Minimum: 0x1? 0001 10nn 用途最小值 Usage Maximum: 0x2? 0010 10nn 用途最大值 Designator Index: 0x3? 0011 10nn Designator Minimum: 0x4? 0100 10nn Designator Maximum: 0x5? 0101 10nn String Index: 0x7?: 0111 10nn String Minimum: 0x8? 1000 10nn String Maxmum: 0x9? 1001 10nn Delimiter: 0xa? 1010 10nn |
-
main items 参数介绍
Input Reports 表示 Device 上报给 Host 比如键盘按下的动作
Onput Reports 表示 Host 发送数据给 Device
比如键盘灯在小写状态下按下 Caps Lock 后会发送按键消息给 Host
Host 收到后启用大写功能,发送控制键盘 LED 点亮给到键盘
键盘收到后点亮 LED,并回复设备状态给 Host [0 字节表示成功]Feature Reports 表示 Host 专属双向数据通道
USB 中只能通过 Host 控制 Device 来双向传输数据
1.通过 get_report 来获取 Device 数据
2.通过 set_report 来发送数据到 DeviceCollection 集合的开始相当于 (
End Collection 集合结束相当于 )
-
main items 值介绍
具体参见 hid1_11.pdf 30页
bit | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|
0 | 此 bit 为 0 用一个字节表示 bit file | non-volatile | no null position | preferred state | linera | no wrap | absolute | array(可以同时被触发) | data(可变值) |
1 | 此 bit 为 1 用2个字节表示 buffered bytes | volatile | null state | no perferred | no nlinera | wrap | relative | variable(只能触发一个) | constant(不可变值) |
0: 表示上面的属性,1:表示下面的属性
eg: 0x81 0x02
0x02 表示的属性为 data,variable,absolute, no wrap,linera, perferred state,no null position, non-volatile
long items
todo
HID 实例设备描述符解析
-
Nordic sdk17.1.0
-
ble_app_hids_keyboard 历程
-
设备描述符代码片段截取
static uint8_t report_map_data[] =
{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
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)
0x81, 0x01, // Input (Constant) reserved byte(1)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (Page# for LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x91, 0x02, // Output (Data, Variable, Absolute), Led report
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output (Data, Variable, Absolute), Led report padding
0x95, 0x06, // Report Count (6)
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(6 bytes)
0x09, 0x05, // Usage (Vendor Defined)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8 bit)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data, Variable, Absolute)
0xC0 // End Collection (Application)
};
-
设备描述符详解
下表查询文档为 hut1_4.pdf 用 文档1 表示 hid1_11.pdf 用 文档2 表示源数据 条目二进制格式解析 内容解析 说明 0x05,0x01 0000 0101
bTag: 0000 Usage Page 用途页
bType: 01 Global 类型
bSize: 01 数据长度 10x01: 查文档1第三部分(17页) Usage Pages
为 Generic Desktop Page条目类型: Global
用途页:Usage Pages
具体用途: Generic Desktop Page 桌面应用0x09, 0x06 0000 1001
bTag: 0000 Usage 描述用途
bType: 10 Local 类型
bSize: 01 数据长度 10x06: 查文档1第四部分(32页) Generic Desktop Page
为 Keyboard条目类型: Local
用途页:Usage
具体用途: Generic Desktop Page 桌面应用
的 Keyboard 应用0xA1, 0x01 1010 0001
bTag: 1010 Collection
bType: 00 Main 类型
bSize: 01 数据长度 101: 查文档2 33页 和文档1 3.4.3 (26页)
为 Application条目类型: Main
开集合:Collection
具体用途: Application0x05, 0x07 0000 0101
bTag: 0000 Usage Page 用途页
bType: 01 Global 类型
bSize: 01 数据长度 10x07: 查文档1第三部分(17页) Usage Pages
为 Keyboard/Keypad Page条目类型: Global
用途页:Usage Pages
具体用途: Keyboard/Keypad Page0x19, 0xe0 0001 1001
bTag: 0001 Usage Minimum 用途最小值
bType: 10 Local 类型
bSize: 01 数据长度 10xE0: 查文档1第十部分(94页) Keyboard LeftControl
为键盘功能按键 左 CTRL条目类型: Local
用途最小值 :Usage Minimum
具体用途: 从 Keyboard LeftControl
开始,往往配合 Usage Maximum 用途最大值规定用途的范围0x29, 0xe7 0010 1001
bTag: 0010 Usage Maximum 用途最小值
bType: 10 Local 类型
bSize: 01 数据长度 10xE7: 查文档1第十部分(95页) Keyboard Right GUI 条目类型: Local
用途最小值 :Usage Maximum
具体用途: 到 Keyboard Right GUI
结束,往往配合 Usage Minimum 用途最小值规定用途的范围0x15, 0x00 0001 0101
bTag: 0001 Logical Minimum 逻辑最小值
bType: 01 Global 类型
bSize: 01 数据长度 10x00: 逻辑最小值 0
查文档2 35页条目类型: Global
逻辑最小值 :Logical Minimum
值: 00x25, 0x01 0010 0101
bTag: 0010 Logical Maximum 逻辑最大值
bType: 01 Global 类型
bSize: 01 数据长度 10x01: 逻辑最大值 1
查文档2 35页条目类型: Global
逻辑最大值 :Logical Maximum
值: 10x75, 0x01 0111 0101
bTag: 0111 Report Size
bType: 01 Global 类型
bSize: 01 数据长度 10x01: 上报数据的大小(单位 bit)
查文档2 35页条目类型: Global
上报数据大小 :Report Size
值: 1
这里可以解释为 1bit 代表一个按键
结合上文为 1 bit 表示 0xE0 - 0xE7
8 个功能按键中的一个按顺序来0x95, 0x08 1001 0101
bTag: 1001 Report Count
bType: 01 Global 类型
bSize: 01 数据长度 10x08: 上报数据的个数 8
查文档2 36页条目类型: Global
上报数据个数 :Report Count
值: 80x81, 0x02 1001 0001
bTag: 1001 Input
bType: 01 Main 类型
bSize: 01 数据长度 10x02: 这里太多了就不一一注明
查文档2 30页条目类型: Main
Input
值: 0x02
根据 main items 值介绍 章节部分详细解释 3 bit
data:数值可变
variable: 一次只能触发一个
absolute: 绝对数值
根据前面的描述符可以解释为
键盘的功能键 0xE0 - 0xE7 8 个功能键,一次只能触发一个,数值是可变的数据补齐 0x95, 0x01 1001 0101
bTag: 1001 Report Count
bType: 01 Global 类型
bSize: 01 数据长度 10x01: 上报数据的个数 1
查文档2 36页条目类型: Global
上报数据个数 :Report Count
值: 10x75, 0x08 0111 0101
bTag: 0111 Report Size
bType: 01 Global 类型
bSize: 01 数据长度 10x08: 上报数据的大小(单位 bit)
查文档2 35页条目类型: Global
上报数据大小 :Report Size
值: 80x81, 0x01 1000 0001
bTag: 1000 Input
bType: 01 Main 类型
bSize: 01 数据长度 10x01: 属性
查文档2 30页条目类型: Main
Input
值: 0x01
constant 值不可变字节补齐用,无实际意义键盘 led 0x95, 0x05 1001 0101
bTag: 1001 Report Count
bType: 01 Global 类型
bSize: 01 数据长度 10x05: 上报数据的个数 5
查文档2 36页条目类型: Global
上报数据个数 :Report Count
值: 50x75, 0x01 0111 0101
bTag: 0111 Report Size
bType: 01 Global 类型
bSize: 01 数据长度 10x01: 上报数据的大小(单位 bit)
查文档2 35页条目类型: Global
上报数据大小 :Report Size
值: 10x05, 0x08 0000 0101
bTag: 0000 Usage Page 用途页
bType: 01 Global 类型
bSize: 01 数据长度 10x08: 查文档1第三部分(17页) Usage Pages
为 LED Page条目类型: Global
用途页:Usage Pages
具体用途: LED Page0x19, 0x01 0001 1001
bTag: 0001 Usage Minimum
bType: 10 Local 类型
bSize: 01 数据长度 10x01: 用途最小值 1
Num Lock
查文档1 97 页条目类型: Local
用途:Usage Minimum
最小值: 10x29, 0x05 0010 1001
bTag: 0001 Usage Maximum
bType: 10 Local 类型
bSize: 01 数据长度 10x05: 用途最大值 5
Compose
查文档1 97 页条目类型: Local
用途:Usage Maximum
最大值: 50x91, 0x02 1001 0001
bTag: 1001 Output
bType: 10 Main 类型
bSize: 01 数据长度 10x02: 查文档2 30页 条目类型: Main
Output
值: 0x02 led 部分属性描述0x95, 0x01 1001 0101
bTag: 1001 Report Count
bType: 01 Global 类型
bSize: 01 数据长度 10x01: 上报数据的个数 1
查文档2 36页条目类型: Global
上报数据个数 :Report Count
值: 10x75, 0x03 0111 0101
bTag: 0111 Report Size
bType: 01 Global 类型
bSize: 01 数据长度 10x03: 上报数据的大小(单位 bit)
查文档2 35页条目类型: Global
上报数据大小 :Report Size
值: 30x91, 0x01 1001 0001
bTag: 1001 Output
bType: 10 Main 类型
bSize: 01 数据长度 10x01: 查文档2 30页 条目类型: Main
Output
值: 0x01 属性描述和前面键盘灯 5bit + 此处 3bit 字节补齐使用无实际意义0x95, 0x06 001 0101
bTag: 1001 Report Count
bType: 01 Global 类型
bSize: 01 数据长度 10x06: 上报数据的个数 6
查文档2 36页条目类型: Global
上报数据个数 :Report Count
值: 60x75, 0x08 0111 0101
bTag: 0111 Report Size
bType: 01 Global 类型
bSize: 01 数据长度 10x08: 上报数据的大小(单位 bit)
查文档2 35页条目类型: Global
上报数据大小 :Report Size
值: 80x15, 0x00 0001 0101
bTag: 0001 Logical Minimum 逻辑最小值
bType: 01 Global 类型
bSize: 01 数据长度 10x00: 逻辑最小值 0
查文档2 35页条目类型: Global
逻辑最小值 :Logical Minimum
值: 00x25, 0x65 0010 0101
bTag: 0010 Logical Maximum 逻辑最大值
bType: 01 Global 类型
bSize: 01 数据长度 10x65: 逻辑最大值 101
查文档2 35页条目类型: Global
逻辑最大值 :Logical Maximum
值: 1010x05, 0x07 0000 0101
bTag: 0000 Usage Page 用途页
bType: 01 Global 类型
bSize: 01 数据长度 10x07: 查文档1第三部分(17页) Usage Pages
为 Keyboard/Keypad Page条目类型: Global
用途页:Usage Pages
具体用途: Keyboard/Keypad Page0x19, 0x00 0001 1001
bTag: 0001 Usage Minimum
bType: 10 Local 类型
bSize: 01 数据长度 10x00: 用途最小值 0
查文档1第十部分(89页)
未定义,条目类型: Local
用途:Usage Minimum
最小值: 00x29, 0x65 0010 1001
bTag: 0001 Usage Maximum
bType: 10 Local 类型
bSize: 01 数据长度 10x65: 用途最大值 101
查文档1第十部分(89页) 数字表示的按键含义条目类型: Local
用途:Usage Maximum
最大值: 1010x81, 0x00 1000 0001
bTag: 1000 Input
bType: 01 Main 类型
bSize: 01 数据长度 10x00: 属性
查文档2 30页条目类型: Main
Input
值: 0x00
array 方式可组合按键0x09, 0x05 0000 1001
bTag: 0000 Usage 用途
bType: 10 Local 类型
bSize: 01 数据长度 10x05: 查文档1第十部分(95页) keyboard b 和 B 按键
这部分不理解 Nordic 为什么这么做条目类型: Local
Usage
值: 0x050x15, 0x00 0001 0101
bTag: 0001 Logical Minimum 逻辑最小值
bType: 01 Global 类型
bSize: 01 数据长度 10x00: 逻辑最小值 0
查文档2 35页条目类型: Global
逻辑最小值 :Logical Minimum
值: 00x26, 0xFF, 0x00 010 0110
bTag: 0010 Logical Maximum 逻辑最大值
bType: 01 Global 类型
bSize: 10 数据长度 20x00FF: 逻辑最大值 255
查文档2 35页条目类型: Global
逻辑最大值 :Logical Maximum
值: 2560x75, 0x08 0111 0101
bTag: 0111 Report Size
bType: 01 Global 类型
bSize: 01 数据长度 10x08: 上报数据的大小(单位 bit)
查文档2 35页条目类型: Global
上报数据大小 :Report Size
值: 80x95, 0x02 1001 0101
bTag: 1001 Report Count
bType: 01 Global 类型
bSize: 01 数据长度 10x02: 上报数据的个数 2
查文档2 36页条目类型: Global
上报数据个数 :Report Count
值: 20xB1, 0x02 1011 0001
bTag: 1011 Feature
bType: 00 Main类型
bSize: 01 数据长度 10x02: 属性
查文档2 30页条目类型: Main
Feature
值: 0x020xC0 1100 0000
bTag: 1100 End Collection
bType: 00 Main类型
bSize: 00 数据长度 0条目类型: Main
End Collection
集合结束 -
usb 实用小工具 hid-descriptor-tool
demo 工程
- nordic sdk17.1.0 裁剪后的 ble_uart 历程
- 编译环境 armgcc
- 工程链接
- 注意事项
- 广播中需要添加 hid 设备信息的内容
- 需要添加配对绑定才能和手机系统蓝牙连接
- 设备描述符中 input 和 output report 的个数按照 Byte 计算
- 发送数据的格式按照设备描述符的格式来