观前提示
警告 版权声明
转载此文内容时,请如实标明作者与网页来源,对于层叠转载的情形,请标明初始网址。创作不易,有部分内容使用了HTML代码进行排版与呈现,用于优化阅读体验并提高阅读理解效率,请尊重作者的意愿并保护知识成果。
- 通过截取为图片或保存为网页副本/PDF文件的形式获取内容均视作“转载”。
- 禁止在转载期间添加阅读限制条件,如“必须关注转载者方可观看”。
- 同时禁止源自转载平台施加的阅读限制条件,如“需要登陆转载平台账户方可观看全文”。
- 禁止商用:转载者或转载平台要求用户支付“会员费/赞助费”方可阅读全文的情形等效于“商业用途”,此页的编辑历史亦可作为有效举证材料。
此处记录本人学习HID期间总结出来的知识点,这些知识点可以作为快速对照的手册或解答学习期间疑惑的辅助文本。
必需文档链接
- 对于电脑浏览器,建议新建窗口并以分屏形式打开方便学习;对于手机浏览器,建议直接下载到本地存储器。
综述
此处文档均描述1.11版本的HID接口。开发HID接口所需的基础知识点如下:
- Report_Descriptor(数据报表格式描述符,简称“报表描述符”)
- 设备项目/事件编码的对照表(HID Usage Table)
- 数据端点组态与数据报表格式
- HID专项USB请求(控制指令)
HID接口的特点就是包罗万象,上到鼠标键盘游戏手柄、下到医疗设施键盘、盲文阅读器、FIDO联机身份验证器都可以直接使用这个接口进行人机互动。
HID设备用于功能实现的基础是“数据报表”,数据报表需要设备在配置期间声明“数据报表格式”,配置结束后,就需要按照所声明的格式,将对应数据通过“数据报表”与主机进行交换。
数据报表格式
序言
在USB主机枚举Enumerate,主要软件对目标设备或程序进行枚举时,目标设备或程序会将需要使用的功能与可能用上的数据做成一个“执行列表”,随后将“执行列表”发送至主要软件中,让主要软件根据“执行列表”进行有序处理。一个HID设备时,设备需要在传输配置描述符之后,单独根据USB请求传输“数据报表格式描述符”(亦称“报告描述符”)。
数据报表格式描述符的作用看起来很简单但实施起来困难,具体作用如下:
- 声明人机互动设备可能响应或接受的事件编码
- 声明对应事件的数据组态(配置)格式
- 声明一系列数据在报表上的分布
- 玩懂了数据报表格式描述符,才算玩懂了HID接口协议。拿别人的设计图纸重复造轮子很简单,轮到自己做出最佳空气力学性能的轮子很难,但最为适配的轮子造出来,才能证明你是造轮子的大师。
报头格式
数据报表格式描述符的基本组成格式为“报表格式项目”,报表项目有“长项目”和“短项目”两种数据格式,但由于HID1.11从不使用“长项目”,所以只能学习“短项目”数据格式,且报表项目统一由“报头”和“报文”构成。
短项目数据格式
(此处示例为2字节报文)
| 数据报送顺序先报→后报 |
| 报头 | 报文 |
| 报头编码 | 报头分类 | 数据长度 | 报文第1字节 | 报文第0字节 |
| b7第7位 | b6第6位 | b5第5位 | b4第4位 | b3第3位 | b2第2位 | b1第1位 | b0第0位 | 01H | 00H |
按照现实生活类比,“报头”表示特性类别,“报文”表示对应内容。
“报头”内存行里的内容,不仅会决定报头对应的属性类别,还会决定报文的数据长度。
| “内存行”名词注解 |
|
学习HID乃至USB之前,必须精通数电基础。
“内存”一般指DRAM缓存,“内存”所存放的数据“内存行”是其构建的基础单位。
“内存行”表示存放对应数据的一组逻辑位,如无特殊说明,一行内存为1字节,占用8个逻辑位。- 在单片机中,“寄存器”是“内存行”的一种类型。
- 一个逻辑位只有高电平和低电平两种状态,各种电气功能以及数据处理都要根据逻辑位的状态发生改变。
- 一组逻辑位组成的“内存行”可以表示很多可能,比如一组8位的字节可以表示256种状态。
- 不在对应内存行的数据或者超出有效状态的数值均为无效内容。
- 错误管理内存行会导致“程序跑飞”,程序跑飞时必须紧急停机,否则会导致系统结构解体。
- Windows电脑突然呈现的“蓝屏”(常见Windows版本)或者“绿屏”(测试版Windows,WIP),就是一种紧急停机的表现。
|
报文可能出现的格式
| 报文类型 | 原始数据 | 解析结果 |
| 空报文 | 0xC0, | END_COLLECTION |
| 1字节报文 | 0x05, 0x01, | USAGE_PAGE(Generic Desktop) |
| 2字节报文 | 0x26, 0x7F, 0xFF, | LOGICAL_MAXIMUM(32767) |
| 4字节报文 | 0x0B, 0x20, 0x00, 0xD0, 0xF1, | USAGE(0xF1D0:FIDO_Alliance, 0x0020:Input_Report_Data) |
数据长度效果
| 观前提示 |
- 除了固定数据格式,单元内容必须从BE字节序转为LE字节序,也就是说,字节顺序必须逆向排布。
- 此处表格为转换完成后的最终字节顺序。
|
0字节 void | |
1字节 BYTE |
| 数据报送顺序先报→后报 |
| 报头 | 报文第0字节 |
| 00H | 01H |
|
2字节 WORD |
| 数据报送顺序先报→后报 |
| 报头 | 报文第1字节 | 报文第0字节 |
| 00H | 02H | 01H |
|
4字节 DWORD |
| 数据报送顺序先报→后报 |
| 报头 | 报文第3字节 | 报文第2字节 | 报文第1字节 | 报文第0字节 |
| 00H | 04H | 03H | 02H | 01H |
|
“报头”由3个字段构成,总大小为1字节。4个逻辑位的“报头声明”、2个逻辑位的“报头分类”和2个逻辑位的“数据长度”,且“数据长度”会影响“报文”的数据大小。
原始数据对应释义
| 原始数据 | 报头分类(翻译)1 | 报文长度 | 注意事项 |
| 0x00 | MAIN/基础功能 | 0字节 |
- 放置在不同位置的原始数据会出现不同释义,如“0x01”放在“报头分类”区域会编译为“GLOBAL类项目”,“0x01”放在“报文长度”会表示“1字节报文”。
- “短项目”报头声明会受到“报头分类”的影响而受到不同制约。
- 若报头分类为0x03时,请使用长项目数据格式,不能继续使用“短项目”数据格式。
|
| 0x01 | GLOBAL/广义声明 | 1字节 |
| 0x02 | LOCAL/狭义声明 | 2字节 |
| 0x03 | LONG/长项目修饰符2 | 3字节 |
报头类别
报头类型与编码
(有效数据掩码以字节形式表示数据的有效范围,对应逻辑位为1时表示有效位,为0时表示无关位。为0xF0)
| 原始数据 | 报头名称 | 使用方法 |
MAIN / 基础功能 控制HID解析器执行报表制成或分组 |
| 0x80 | INPUT 打包数据入表 |
- 3个报头的报文大小均为1/2字节报文,组合内存行。
- 前述两个报头均为必需报头,一个数据报表格式描述符需要至少一个这样的报文,且三种报头至少需要其一。
报表打包:根据修饰属性和注册的用途清单,创建对应内存行/寄存器。
- 数据输入:将设备报告已触发的事件在HID技术文档中称作“Usage”,直译为“用途”。上传至主机。必须有一个数据入点USB数据端点,传输方向为“输入”,即“主机←设备”。以传输实时变化的数据报表。
- 数据输出:将主机报告已触发的事件下载至设备。可分配一个数据出点USB数据端点,传输方向为“输出”,即“主机→设备”。以传输实时变化的数据报表,否则只通过控制端点USB数据端点,端点编号为0号,只能通过USB请求传输数据的SET_REPORT请求主机单独下载数据报表到设备传输数据报表。
- 设备特性:在主机与设备的传输中交换设备特性,只能通过控制端点交换数据。
- 如果需要定义为“BUFFERED_BYTES”字节填充,必须使用2字节报文。
- 对应操作结束后会清空狭义声明。
|
| 0x90 | OUTPUT 打包数据出表 |
| 0xB0 | FEATURE 打包设备特性表 |
| 0xA0 | COLLECTION 集合起始域 |
- COLLECTION报头为1字节报文,无符号整数,END_COLLECTION报头为无数据报头。
- 前述两个报头均为必需报头,一个数据报表格式描述符需要至少一组这样成对的报文。
集合或分组:创建报文分组,提高HID解析器和电脑软件的解析效率。
- 一个数据报表格式描述符需要至少一个最高级“APPLICATION”应用领域集合。同时具备多个HID标准类型的单个设备视作“多个应用领域”。
- 如果开发的1个非标设备不按现有标准制成的设备,有时又被称作“自定义设备”或“客制化设备”。只有一个应用领域,可以不再对数据报表格式描述符进行分组。
- COLLECTION报头和END_COLLECTION报头必须成对出现,但是允许嵌套。
|
| 0xC0 | END_COLLECTION 集合结束域 |
- 基础功能所生成的项目会受到广义声明和狭义声明的调整。
|
GLOBAL / 广义声明 修饰一个数据报表的扩展属性 |
| 0x00 | USAGE_PAGE 定位用途类别 |
- 1字节报文,部分用途类别需要2字节;不论字节大小均为无符号整数。
- 可选报头1,根据USAGE报头决定是否为必需报头2。
用途注册:向HID解析器添加可能会用到的用途。其中此报头用于定位用途所在的类别表格编号。
- 此报头可节省用途声明所需的代码,比如注册同一类别表格下的零散用途。
- 对于工作在低速模式下的设备而言,由于控制端点字长为恒定8字节,此报头为必需报头。
- 如果数据报表格式描述符集合中,第一个USAGE_PAGE前面有USAGE报头,HID解析器会将集合起始域后的第一个USAGE中的低16字节用作USAGE_PAGE.
|
| 0x10 | LOGICAL_MINIMUM 数字域最小值 |
- 4个报头的报文大小可使用1/2/4字节的报文,并且均为有符号整数在单个字节的最高位(MSD)表示数字前面的符号,若该位为1,表示该数值为负数,负数情况下,取数字域最小值(负数最大值)加上剩余位表示的数值算出最终结果。
以下例子介绍了1字节有符号整数对应的有效数据:- “0x7F”→“+127”
- “0x80”→“-128”
- “0x81”→“-127”
- ……
- “0xFF”→“-1”
实际算法: “0xFF”↓ “-1”↓ “(-128)+127=-1”。
- “数字域”相关报头均为必需报头1,一个数据报表格式描述符需要至少一个对应类别的报文2;“实体域”报头为可选报头3。
数据极值:划定有效数据的最大值/最小值范围。
- “实体域数值”指的是设备在实际应用中对应的有效数据;“数字域数值”指的是数字信号传输过程中对应的等效数值;“极值”包含“最大值”和“最小值”。
- 未声明极值时,对应的极值将取缺省值“0x00”;“数字域最大值”不能小于或等于“数字域最小值”。
- 极值以外的数值均视作NULL值无意义数值,一般用作“数据不可用”;“-2147483648(0x8000 0000)”为恒定NULL值,禁止用作极值。
- “实体域”极值不被用作数据报表创建,仅供应用程序读取相关内容以修改算法,比如“电子减速器”。
|
| 0x20 | LOGICAL_MAXIMUM 数字域最大值 |
| 0x30 | PHYSICAL_MINIMUM 实体域最小值 |
| 0x40 | PHYSICAL_MAXIMUM 实体域最大值 |
| 0x50 | UNIT_EXPONENT 实体域10乘倍数 |
- “UNIT_EXPONENT”仅使用1字节的低4位1,“UNIT”为7组4位(组合内存),并且均为有符号整数。
- 前述两个报头均为可选报头2。
实体域单位声明:声明数据对应的单位与10乘倍数。
“10乘倍数”算式效果为$1×10^x$。
- “UNIT”报头的数据结构很复杂,第0字节的低4位表示单位所使用的度量制(公制/英制)与坐标体系(直角坐标/极坐标),其余分组的4位内存表示各单位的导数微积分数学的重要概念,在此处用于修饰单位的导函数或积分值,对应单位会随着度量制的不同而发生改变。
- 该字节高4位没有任何意义,恒定为0x0.
- 不被用作数据报表创建,仅供应用程序读取相关内容以修改算法。
|
| 0x60 | UNIT 实体域单位与导数 |
| 0x80 | REPORT_ID 数据报表编号 |
- 1字节报文,无符号整数。“0x00”为NULL值。
多重数据报表:声明对应报表所处的编号。
- 同时只能传输一整个数据报表,需要传输的报表编号可被设备轮换或主机控制。
|
| 0x70 | REPORT_SIZE 项目占用大小 |
- 前述两个报头均为1字节无符号整数,“0x00”为NULL值。
- 前述两个报头均为必需报头,一个数据报表格式描述符需要至少一个对应类别的报文。
数据格式:声明单次注册(数据打包)所需要的项目数量与单个项目的占用大小。
- “REPORT_SIZE”的单位不是1个字节而是1个逻辑位。“REPORT_COUNT”的单位为数量。
- 被注册的内存将使用“REPORT_SIZE”דREPORT_COUNT”个逻辑位,但打包成数据传输时,剩余不满1个字节的部分需要根据以下情形填充为整个字节:
- 如果在报表打包过程中声明了“BUFFERED_BYTES”,不需要填充字节,但此情形仅用于一个特殊事件(项目),这种特殊项目占用的位大小为字节的整数倍。
- 剩余未使用的逻辑位以1个“无意义项目”的形式打包成声明“CONSTANT”的数据。
- 单个项目占用的数据长度上限为32个逻辑位。
|
| 0x90 | REPORT_COUNT 打包所需项目个数 |
| 0xA0 | PUSH 广义声明存档 |
- 前述两个报头均为无数据报头。
- 前述两个报头均为可选报头。
广义声明缓存区:用于快速复制广义声明修饰的属性
- 缓存区只有一倍大小的空间,使用范围不受“COLLECTION”类报头影响,全局有效。
- 连续使用PUSH会替换旧内存,不论多少次使用POP都只会读取当次POP前最后一次PUSH的内存。
|
| 0xB0 | POP 广义声明读档 |
- 一个广义声明报头的影响范围会从本身开始,截至下一个同类广义声明。📽️没有编译呈现效果,请先完成代码填入以编译对应效果。
|
LOCAL / 狭义声明 具有限定范围的属性修饰符 |
- 一个狭义声明报头经过声明后,其影响范围会从本身开始截至下一个“MAIN”类报头,多个狭义声明报头共存且互不干扰。
|
报头用法
数据组态
应用例
标准USB键盘