首页 / 资讯中心 / 文章详情

USB HID使用表1.3:Usage Page与Usage ID解析

USB HID使用表1.3:Usage Page与Usage ID解析 ★ FEATURED ARTICLE
简介《USB HID Usage Tables 1.3》是USB Implementers’ Forum发布的HID标准使用表文档面向USB硬件工程师、嵌入式开发者、驱动程序员与系统集成人员用于解决键盘、鼠标、游戏控制器、触摸屏等HID设备在Usage ID定义、Report Descriptor描述及跨平台兼容性上的参考需求。资源包共1个PDF文件大小约4.09MB内容完整呈现1.3版规范便于离线查阅与团队传阅。已有558人学习说明其在USB HID开发群体中具有一定参考价值。文档系统梳理Usage Page与Usage ID分类、设备类定义、协议传输及向后兼容要求并附贡献者与修订历史能帮助读者理解标准用法标识符的上下文核对报告描述符结构排查设备互操作问题也可作为产品设计与标准化评审时的案头资料。1. 到底什么是 USB HID 使用表 1.3一本让外设“自报家门”的功能字典把鼠标插到电脑上电脑怎么知道左键是“点击”而不是“音量加”答案藏在一本设备功能字典里也就是 USB HID 使用表Usage Tables1.3 版。它把键盘、鼠标、游戏手柄、触摸屏等设备上的功能用一套分页编号登记下来。你可以把它想成餐厅菜单Usage Page 是菜系Usage ID 是具体菜名主机拿到编号就能对上语义。搞懂它才能明白为什么有些外设插上就灵、有些却要装专用驱动也才能看懂 HID 描述符里那些十六进制数字到底在说什么。2. 为什么需要 Usage Page 与 Usage ID从“插上就能用”到跨系统互通2.1 设备只发字节主机需要一本“语义字典”一个 HID 设备插上主机后最原始的动作并不是“告诉系统我按了 A 键”而是发出一串按位排列的字节。键盘发的是报告鼠标发的也是报告游戏手柄发的还是报告字节本身没有语义0x04 可以表示某个按键也可以表示某个厂商自定义状态。主机如果只看字节就像收到一张只写了菜价、没写菜名的账单能看见数字却不知道自己在为什么买单。USB HID 使用表解决的正是“账单上写菜名”的问题。它把人类常见的输入输出功能拆成一个个标准用法键盘上的字母键、鼠标的左右键、桌面坐标的 X 轴和 Y 轴、游戏手柄的方向帽、电话设备的挂断键、LED 指示灯的点亮状态。厂商在设备描述符里声明自己用到哪些用法主机按同一张表去解释双方不用私底下对暗号。这样一来设备换一个系统、换一台机器只要双方都认这张表基本就能做到插上就能用。这里的关键不是“字节长什么样”而是“这串字节代表什么”。使用表管语义报告描述符管布局两者配合才让一个外设从“会发数据的黑匣子”变成“主机看得懂的人机交互设备”。如果没有统一的使用表每个厂商都可以说自己的 0x04 是确认键主机就得为每个品牌写一套私有解释外设行业会退回到“一设备一驱动”的麻烦状态。2.2 Usage Page 是领域Usage ID 是具体功能Usage Page 可以理解成“功能领域”Usage ID 是这个领域里的“具体条目”。只看 Usage ID 是不够的因为不同领域里可能出现相同的编号。比如键盘页里有一个编号表示某个按键按钮页里也有编号表示 1 号按钮桌面页里还有编号表示 X 轴它们各自处在不同页里含义完全不同。页号加编号才构成一个完整的语义坐标。通俗地说Usage Page 像图书馆的分区Usage ID 像书架上的书号。你只说“第 4 本”管理员不知道是哪一区的第 4 本你说“键盘区第 4 号”对方才能准确取书。HID 使用表把这种“分区加书号”的规则固定下来常见页包括通用桌面页、模拟控制页、游戏控制页、键盘键区页、LED 页、按钮页、电话设备页等。每个页下面再列具体用法形成一张可以查、可以扩展、可以向后兼容的编号表。这也解释了为什么读 HID 描述符时不能只盯着一个十六进制数。看到0x07和0x04放在一起熟悉表的人会先判断0x07是键盘键区页再去找0x04对应的字母键如果只看到0x04就很容易把键盘按键和别的东西混为一谈。页号是上下文编号是内容脱离上下文的编号没有稳定意义。2.3 1.3 版为什么还在更新新设备形态与旧设备兼容从修订历史看这份表从 1996 年一路修到 2022 年1.3 版并不是把旧内容推倒重来而是把多轮审查请求、若干设备使用表和相关规范里的用法合并进来。设备形态在变早期主要是键盘、鼠标、摇杆后来出现触摸屏、触控笔、游戏手柄、运动模拟设备、电话控制、电源控制、无线控制、传感器控制等。新功能需要新编号旧编号又不能随便改否则老设备会突然“变键位”。所以这类标准文档的更新逻辑很像城市门牌号新街区要加新号码老街区不能因为规划调整就把门牌全换掉。1.3 版增加或修正用法时要尽量保持已有用法的含义稳定让旧设备继续工作。对开发者来说这意味着不能看到版本号变了就假设所有编号都变了更稳的做法是查表确认具体条目再看自己的描述符有没有用到新增或调整过的部分。版本更新还带来一个现实好处不同设备类别之间的边界更清楚。比如某个旋钮到底算通用桌面页里的轴还是算某个专用设备页里的控制表里会给出倾向。开发者按表选编号主机按表解释用户侧少装一个私有驱动跨系统互通的可能性就高一分。2.4 未知用法不是错误保留处理的兼容逻辑HID 使用表并不假设主机认识表里的每一个编号。新设备可能用了较新的用法老主机可能只认识旧版条目这时如果主机一遇到未知编号就报错设备就很容易翻车。标准里专门提到未知用法的处理思路遇到不认识的用法不要瞎猜语义按保留或通用方式处理保证设备还能基本工作。这条规则很像收银员遇到菜单上的新菜名不认识可以先标记“待确认”不能把这道菜直接算成另一道菜。对开发者来说描述符里写了新用法不代表所有主机都能完整解释对主机来说看到未知用法也不应该把整个设备判死刑。保留处理是兼容性的后悔药让新旧设备在同一套体系里有个缓冲带。理解这一点就不会把“使用表”当成一张永远完备的清单。它更像一份不断维护的公共词典常用词很稳定新词会补充生僻词有保留区。设备开发者要做的是尽量选表中已有且语义贴切的用法主机开发者要做的是对未知用法留出安全分支。双方都守这个边界HID 设备才不容易在某台机器上突然失灵。3. 易混概念辨析Usage Page、Usage ID、Report Descriptor 到底谁管谁3.1 易混点一Usage Page 不是设备分类而是功能领域很多人第一次看表会把 Usage Page 当成“设备类型”键盘页就是键盘设备鼠标页就是鼠标设备。这个理解很容易混因为键盘确实常出现在键盘页鼠标坐标也确实常出现在通用桌面页。但 Usage Page 描述的是功能领域不是整台设备的身份。一个游戏手柄可能同时用到按钮页、通用桌面页和游戏控制页一个带快捷键的键盘也可能同时用到键盘页、LED 页和消费类控制页。为什么容易混因为设备厂商在描述产品时习惯说“这是一个键盘”“这是一个手柄”而标准文档是按功能条目组织的二者视角不同。怎么区分看它描述的是“整机类别”还是“某个具体功能”。设备类别回答“我是什么设备”Usage Page 回答“我这个功能属于哪个领域”。读描述符时先看页再看条目不要一看到键盘页就以为整份描述符只服务键盘。3.2 易混点二Usage 不是 Report Descriptor一个是语义一个是布局Usage 和 Report Descriptor 经常被放在同一段话里初学者容易以为它们是一回事。其实 Usage 负责“这个字段叫什么”Report Descriptor 负责“这个字段在报告里的哪几位、多少位、取值范围怎么定”。Usage 是语义标签Report Descriptor 是数据布局说明书。键盘按下 A 键Usage 说明这是键盘页里的 A 键Report Descriptor 说明报告里第几个字节的哪一位承载这个状态。为什么混因为两者都写在 HID 描述符体系里调试时又常常一起出现。设备发给主机的内容里既有描述数据结构的描述符也有按结构发出的报告。怎么区分可以记一句Usage 回答“是什么”Report Descriptor 回答“怎么排”。只改 Usage 不改布局主机可能知道字段含义却读错位置只改布局不改 Usage主机可能读对位置却解释成别的功能。3.3 易混点三Usage ID 不是 Report ID别把“功能编号”和“报告通道”混在一起Usage ID 是功能条目的编号Report ID 是报告通道的编号。一个设备可能把键盘报告、媒体控制报告、厂商自定义报告分到不同 Report ID 下主机先看 Report ID 知道“这是哪一路报告”再看描述符知道这一路里各字段的 Usage。Usage ID 不负责分通道它负责说明某个字段代表什么功能。两者都可能以十六进制出现所以特别容易看花眼。为什么混因为调试工具里常把 Report ID 和 Usage 一起打印数字又都是0x开头。怎么区分看它在描述符里的位置和用途Report ID 用来区分多份报告通常出现在报告开头Usage 用来标注字段语义跟着输入、输出或特征字段走。把 Report ID 当成 Usage就会把“这是第几路报告”误读成“这是哪个按键”后面整张表都会对不上。3.4 易混点四Usage Type 不是数据类型它描述交互性质使用表里有一组 Usage Type比如线性控制、开关控制、瞬时控制、单次触发、重触发以及选择器、静态值、静态标志、动态标志、动态值等。名字看起来像编程里的数据类型但它们主要描述交互性质这个控制是连续变化的还是按一下开、再按一下关是按住有效还是触发一次就结束是让用户在几个选项里选一个还是只读状态。为什么混因为“类型”这个词太宽容易让人联想到整数、布尔、枚举。怎么区分看它回答的是“用户怎么操作这个功能”不是“这个字段在内存里占几字节”。一个瞬时按钮的 Usage Type 是瞬时控制但它在报告里可能只占 1 位一个线性轴的 Usage Type 是线性控制但它在报告里可能是 8 位或 16 位。交互语义和数据宽度是两层事不能混着用。3.5 易混点五Boot Protocol 与 Report Protocol 不是同一层的东西Boot Protocol 和 Report Protocol 也常被混在一起说。Boot Protocol 更像一套老式兼容通道让基础键盘、鼠标在早期环境里也能工作Report Protocol 则按设备自己的报告描述符来解释数据能表达更多按键、更多轴、更多自定义布局。它们不是两种 Usage Page也不是两套 Usage ID而是设备与主机沟通报告时的不同模式。为什么混因为两者都影响“主机怎么读报告”调试时切换协议又可能看到不同的数据长度。怎么区分看问题是“用哪套简化布局”还是“按设备自述布局”。Boot Protocol 的字段位置相对固定适合最基础的输入Report Protocol 依赖描述符表达能力强。设备声明支持哪种协议、主机当前用哪种协议要分开确认不能把协议模式当成某个 Usage 条目。4. 这份表怎么读从键盘 A 键到游戏手柄按钮的映射逻辑4.1 先看页号再看编号键盘、按钮、桌面坐标三组例子读 HID 使用表最稳的顺序是“先页后号”。先确认当前字段属于哪个 Usage Page再在这个页里找 Usage ID。键盘上的 A 键通常落在键盘键区页鼠标或手柄上的 1 号按钮通常落在按钮页桌面坐标的 X 轴和 Y 轴通常落在通用桌面页。三组例子放在一起看就能体会页号的作用同样是编号换一个页就换了一个语义世界。实际调试时看到描述符里出现0x07先别急着背具体键位先确认它是键盘键区页看到0x09先想到按钮页看到0x01先想到通用桌面页。页确认之后再去查具体编号。这样即使遇到不熟悉的设备也不容易把按钮页的 1 号按钮误认成键盘页的某个键。表是查出来的不是猜出来的。使用页十六进制管的领域典型条目0x01通用桌面X 轴、Y 轴、滚轮、系统控制0x07键盘/键区字母键、修饰键、功能键0x09按钮按钮 1、按钮 2、按钮 N0x08LED 指示灯大写锁定灯、数字锁定灯0x05游戏控制游戏手柄、方向帽、扳机这张表只列常见页目的是建立“页号是领域”的直觉不是把整本使用表压缩成五行。真正的 1.3 版文档里还有模拟控制、VR 控制、运动控制、电话设备、通用设备控制等页每页下面又有大量条目。读的时候先建立页的坐标系再按设备功能去查具体条目比从第一页硬背到最后有效得多。4.2 Usage Type 是交互语义线性、开关、瞬时、触发分别怎么理解Usage Type 像给功能贴“交互方式”的标签。线性控制适合摇杆、滑块、旋钮这类连续变化的东西开关控制适合电源开关这种保持开或关的状态瞬时控制适合“按住有效、松开无效”的按钮单次触发适合按一下只发生一次的动作重触发适合按住后反复触发的动作。数据类里有选择器、静态值、静态标志、动态标志、动态值分别对应“从几个选项里选一个”“只读数值”“只读状态位”“会变化的状态位”“会变化的数值”。这些标签的意义在于让主机理解“该怎么对待这个字段”。一个瞬时按钮和一个开关按钮在报告里都可能只占 1 位但用户体验完全不同前者按住才有效后者按一下切换状态。一个线性轴和一个静态值也都可能是 8 位数字但前者表示连续输入后者表示某个固定参数。只看位宽会把交互语义丢掉。对开发者来说选 Usage 时不只要看名字像不像还要看交互性质对不对。把一个旋钮硬塞进开关控制主机可能把它当“开/关”处理细腻的旋转量就丢了把一个自复位按钮写成开关控制用户松手后状态可能还挂着。使用表不是只给你一个名字它还暗示了这个功能应该怎么被操作、怎么被反馈。4.3 Report Descriptor 负责“数据怎么排”Usage 负责“这排数据叫什么”一份 HID 报告可以想成一排格子每个格子里放若干位数据。Report Descriptor 负责说明这排格子怎么划分哪几位是按钮状态哪几位是 X 轴哪几位是 Y 轴逻辑最小值、逻辑最大值、报告大小、报告计数分别是多少。Usage 负责给这些格子贴标签这一格叫“按钮 1”那一格叫“X 轴”另一格叫“Y 轴”。标签和格子对上了主机才能既读对位置又理解含义。调试时经常出现一种情况报告长度对了按钮也能按但系统把某个键认成了别的东西。这时不要只怀疑电气问题也要看描述符里的 Usage 有没有贴错。另一个常见情况是 Usage 写对了但报告里的位偏移错了主机读到的值总是差一位。前者是语义错后者是布局错排查方向完全不同。把这两层分开定位问题会快很多。使用表 1.3 里的条目是“标签库”不是“布局模板”。它不会告诉你某个设备必须用 8 字节报告也不会规定按钮必须排在前三位。设备厂商按自己的硬件设计布局再用 Usage 给字段命名。主机按描述符解析布局再按使用表解释语义。三者各司其职任何一层偷懒都会让设备在某类主机上表现古怪。4.4 用一段代码把 page usage 的组合讲清楚下面这段 Python 伪代码只做一件事把“页号 编号”组合成一个可读的功能名。它不解析真实二进制报告也不依赖任何厂商库目的是让页和编号的关系变得直观。# 用“页号 编号”查功能名展示 Usage Page 的上下文作用 USAGE_PAGE_GENERIC_DESKTOP 0x01 USAGE_PAGE_KEYBOARD 0x07 USAGE_PAGE_BUTTON 0x09 USAGE_X 0x30 USAGE_Y 0x31 USAGE_KEY_A 0x04 USAGE_BUTTON_1 0x01 def describe_usage(page, usage): # 键是二元组单独一个 usage 编号不足以确定语义 table { (USAGE_PAGE_GENERIC_DESKTOP, USAGE_X): 通用桌面页的 X 轴, (USAGE_PAGE_GENERIC_DESKTOP, USAGE_Y): 通用桌面页的 Y 轴, (USAGE_PAGE_KEYBOARD, USAGE_KEY_A): 键盘键区页的 A 键, (USAGE_PAGE_BUTTON, USAGE_BUTTON_1): 按钮页的 1 号按钮, } # 未知用法不抛异常按保留处理避免老主机因新编号崩溃 return table.get((page, usage), 未知用法按保留分支处理) print(describe_usage(0x07, 0x04)) # 键盘键区页 A 键 print(describe_usage(0x09, 0x01)) # 按钮页 1 号按钮 print(describe_usage(0x01, 0x30)) # 通用桌面页 X 轴代码里的page是 Usage Pageusage是 Usage ID0x开头表示十六进制。字典的键用二元组是为了强调“页号加编号”才构成完整语义单独拿0x04去查可能查到键盘 A 键也可能在别的页里查到完全不同的功能。最后的默认返回值不是随便写的它对应未知用法的保留处理不认识就标记为未知不硬猜成某个已有功能。这段代码也解释了为什么调试描述符时不能只看一个数字。真实报告里还会有 Report ID、报告长度、逻辑范围、位偏移代码刻意省略这些只保留语义查询这一层。把这一层看明白再去读完整描述符就不容易被一堆十六进制数淹没。4.5 版本修订与未知用法1.3 版不是一本死字典1.3 版的修订历史里能看到多轮审查请求和若干设备使用表的合并这说明它是一份维护中的公共资料不是一次性写死的清单。新设备形态出现表里可能增加条目旧条目有歧义也可能被澄清。对开发者来说最稳妥的习惯是选编号前查当前版本写完描述符后再对照一遍别靠记忆里的老编号硬写。未知用法的处理同样重要。设备用了较新的条目老主机可能不认识主机遇到不认识的编号也不该把整台设备当成坏设备。保留分支让新旧双方有个缓冲区。这个思路很像软件里的“未知字段先留着”不解释、不崩溃、不误判等有能力的系统再去处理。使用表管的是语义共识共识之外还要留一点兼容空间设备才不容易在真实环境里踩坑。5. 什么时候该用、什么时候别硬套HID 使用表的边界判断5.1 适合用 HID 使用表的场景做标准键盘、鼠标、游戏手柄、触摸屏、遥控器、简单传感器这类人机交互设备时使用表就是案头字典。设备功能越接近表中已有条目越应该优先选标准用法而不是自己发明编号。标准用法能让主机直接理解语义减少私有驱动跨系统互通也更稳。尤其是按钮、轴、LED、电话控制这些常见功能表里已经有成熟分类硬造一套私有语义反而增加调试成本。写报告描述符时先用使用表确定字段语义再设计报告布局顺序不要反过来。先布局后找名字很容易出现“格子排好了却找不到贴切标签”的情况最后只能塞进一个勉强相近的用法主机解释就会偏。把语义先定下来布局按语义和硬件精度去排后面查错也更容易。5.2 不适合硬套的场景如果设备传的是私有高速数据、复杂状态机、非人机交互业务或者功能与表中条目明显不对应就不要为了“看起来标准”硬塞进按钮页或通用桌面页。使用表管语义字典不管电气特性、传输协议、业务状态机也不保证某个私有功能一定有贴切条目。硬套的结果往往是主机把它解释成另一个功能用户看到的是玄学行为开发者拿到的是血泪经验。场景建议原因标准键盘、鼠标、手柄优先查表选标准用法主机认识跨系统互通更稳带少量自定义键的设备常用键走标准页剩余功能谨慎扩展不要让自定义键挤占标准语义私有数据采集设备不要硬套人机交互语义使用表不负责私有业务状态新功能暂时无贴切条目留未知处理分支别乱猜老主机遇到未知用法时应能安全降级在模拟项目X里我曾把一个自定义旋钮硬映射到某个通用轴报告长度和数值范围都对了但不同系统对那个轴的解释不一致表现像“转一点跳三格”。后来回头查表才发现交互性质和用途页都选得勉强。从那以后我每次写描述符都强制走一遍“先查页、再查编号、最后对交互类型”的流程遇到未知用法就留保留分支。这份 1.3 版表适合放在手边遇到编号拿不准时翻页确认资源页里是完整文档取一份本地留存比临时在群里问“这个 0x 是啥”更稳。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站