HID协议学习
0. 文档资料
USB_HID协议中文版_USB接口HID设备_AUJsRmB9kg.pdf
HID+报告描述符精细说明_mgCxM8_ci9.pdf
hut1_22_U3cvnwn_ZZ.pdf
1. 基本概念
HID协议是一种基于USB的通讯协议,用于在计算机和输入设备之间进行数据传输。HID协议定义了标准的数据格式、命令结构以及传输方式,使得不同厂商生产的输入设备可以被操作系统识别和兼容。
-
HID协议的实现基于USB(Universal Serial Bus)接口,因此HID设备通常都是通过USB接口与计算机相连的。在HID协议中,设备会向计算机发送一系列的报告,描述设备的状态和用户的操作。计算机则可以根据这些报告来识别设备,并对用户的操作做出相应的响应。
-
HID协议支持的设备种类非常广泛,包括但不限于键盘、鼠标、游戏手柄、摄像头、扫描仪等等。这些设备都可以通过HID协议与计算机进行通信,从而实现与用户的交互。
2. 报告描述符详解
报表描述符定义了执行设备功能的数据格式和使用方法。
报表描述符和 USB 的其他描述符是不一样的,它不是一个简单的表格,报表描述符是 USB 所有描述符中最复杂的。报表描述符非常复杂而有弹性,因为它需要处理各种用途的设备。报表的数据必须以简洁的格式来储存,这样才不会浪费设备内的储存空间以及数据传输时的总线时间。实际上可以这样理解,报表内容的简洁,是通过报表描述符全面的、复杂的数据描述实现的。
报表描述符必须先描述数据的大小与内容。报表描述符的内容与大小因设备的不同而不同,在进行报表传输之前,主机必须先请求设备的报表描述符,只有得到了报表描述符才可正确解析报表的数据。报表描述符是报表描述项目(Item)的集合,每一个描述项目都有相对统一的数据结构,项目很多,通过编码实现
2.1 项目
见上图,报表描述符由描述 HID 设备的数据项目(Item)组成,项目的第一个字节(项目前缀)由三部分构成,即项目类型(item type)、项目标签(item tag)和项目长度(item size)。其中项目类型说明项目的数据类型,项目标签说明项目的功能,项目长度说明项目的数据部分的长度。
HID 的项目有短项目和长项目两种,其中短项目的格式如下图
短项目的数据字节数由 bSize 的值定义,bSize 为 0、1、2、3 时 Data 部分的字节数分别为 0、1、2、4 个字节。短项目的项目类型由 bType 定义,bType 为 0、1、2 时分别为 Main、Global 和 Local 类型。
长项目可以携带较多的数据,其格式如下图
项目中的第一个字节为上图中的特定值时表明该项目是一个长项目。长项目中的bDataSize 说明 Data 部分的字节数,bLongItemTag 在 HID 规范中没有定义。
下面是通过汇编实现的一个简单的报表描述符,描述符的每一行是一个项目,该描述符描述了一个从设备接收 2 个字节的输入报表和发送 2 个字节到设备的输出报表。
HID_Report_desc_table:
db 06h, A0h, FFh ; Usage Page(Vendor defined) 定义设备功能
db 09h, A5h ; Usage(Vendor Defined) 定义用法
db A1h, 01h ; Collection(Application) 开一个集合
db 09H, A6h ; Usage(Vendor defined) 定义用法
; 输入报表
db 09h, A7h ; Usgae(Vendor defined) 定义用法
db 15h, 80h ; Logical Minimum 定义输入最小值=-128
db 25h, 7Fh ; Logical Maximum 定义输入最大值=+127
db 75h, 08h ; Report Size 定义报表数据项大小=8
db 95h, 02h ; Report Count 定义报表数据向个数=2
db 81h, 02h ; Input(Data,Variable,Absolute) 输入项目
; 输出报表
db 09h, A9h ; Usgae(Vendor defined) 定义用法
db 15h, 80h ; Logical Minimum 定义输入最小值=-128
db 25h, 7Fh ; Logical Maximum 定义输入最大值=+127
db 75h, 08h ; Report Size 定义报表数据项大小=8
db 95h, 02h ; Report Count 定义报表数据向个数=2
db 91h, 02h ; Output(Data,Variable,Absolute) 输出项目
db C0h ; End Collection 关闭集合
2.2 项目分类
报表的项目有 Main
、Global
和 Local
三大类,每一类都有多个不同的项目,实现不同
的描述。
Main 类项目用于定义报表描述符中的数据项
。也可以组合其中的若干数据项成为一个集合。Main 项目可以分为带数据的 Main 项目和不带数据的 Main 项目。带数据项的 Main用于生成报表中的数据项,包括 Input、Output 和 Feature 项目。不带数据的 Main 项目不生成报表中的数据项,包括 Collection 和 End Collection 项目。
Global 类项目实现对数据的描述,用来识别报表并且描述报表内的数据
,包括数据的功能、最大与最小允许值以及数据项的大小与数目等。改变由 Main 类项目生成的项目状态表。Global 类项目描述对后续的所有项目有效,除非遇到有新的 Global 类项目。
Local 类项目定义控制的特征
,这一类项目的作用域不超过下一个 Main 项目,所以在每一 Main 项目之前可能有多个 Local 项目。Local 项目用于描述后面的 Input、Output 和Feature 项目`
2.3报告描述符案例解析
案例1
static const u8 hidkey_report_map[] = {
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x09, 0xE9, // Usage (Volume Increment)
0x09, 0xEA, // Usage (Volume Decrement)
0x09, 0xCD, // Usage (Play/Pause)
0x09, 0xE2, // Usage (Mute)
0x09, 0xB6, // Usage (Scan Previous Track)
0x09, 0xB5, // Usage (Scan Next Track)
0x09, 0xB3, // Usage (Fast Forward)
0x09, 0xB4, // Usage (Rewind)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x10, // Report Count (16)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// 35 bytes
};
例如看以上代码,ID设备的报告描述符(Report Descriptor)和一些使用该设备时可能需要的命名常量。报告描述符定义了所使用的HID协议中消费者设备(Consumer Device)的控制键,这些键包括音量调节、播放/暂停、静音、上一个/下一个曲目以及快进/快退。每个控制键由一个唯一的Usage ID(Usage Page + Usage ID)标识,并且在报告输入功能中使用1位来表示该键是否被按下。
这段代码定义了一个HID报告描述符,用于实现一组控制功能按键的映射。具体来说,它描述了一组使用Consumer Page的控制按键 Usage,并指定相应Usage的最小值、最大值、大小(占据多少比特)、数量等信息。
常量hidkey_report_map[]列出了描述符中的各个字段及其值:
-
第1-2字节:Usage Page标识了所采用的设备类型(这里为0x0C,表示使用 Consumer Page)。
-
第3-4字节:Usage标识了使用哪些控制按键(包括音量加减、播放/暂停、静音、上一曲、下一曲、快进、倒退)。
-
第5-6字节:Collection字段说明后续定义的用途属于Application集合。类似于c语言的花括号
-
第7字节:Report ID标识了该HID报告的唯一性,用于区分多个报告。
-
第8~23字节:定义了使用的各个控制按键对应的Usage。
-
第24~26字节:Logical Minimum指定了使用的控制按键取值的最小值(此处为0)。
-
第27~29字节:Logical Maximum指定了使用的控制按键取值的最大值(此处为1)。
-
第30字节:Report Size指定了每个Usage所占用的比特数(此处为1)。
-
第31字节:Report Count指定了使用的Usage的数量(此处为16)。
-
第32字节:Input字段说明了这一组控制按键的发送信息是从设备传输到主机的。
-
第33字节:End Collection表示集合定义结束。类似于c语言的花括号
该HID报告描述符的作用在于,让USB主机知道如何解析来自HID设备的输入数据。通过正确解析描述符,实现与设备间的通信和交互。
// consumer key
#define CONSUMER_VOLUME_INC 0x0001
#define CONSUMER_VOLUME_DEC 0x0002
#define CONSUMER_PLAY_PAUSE 0x0004
#define CONSUMER_MUTE 0x0008
#define CONSUMER_SCAN_PREV_TRACK 0x0010
#define CONSUMER_SCAN_NEXT_TRACK 0x0020
#define CONSUMER_SCAN_FRAME_FORWARD 0x0040
#define CONSUMER_SCAN_FRAME_BACK 0x0080
如看上述代码,在源码常量则将对应的控制键映射为了十六进制值,这些值可以用来生成报告数据并将其发送到主机。例如,按下音量增加键时,将使用0x0001值向主机发送包含音量增加键的报告数据。同样,通过检查从主机返回的报告数据,程序也可以知道用户是否按下了特定的控制键,以便在应用程序中执行相应的操作。那为什么0x0001可以对应音量升呢?那是因为是通过bit位来控制功能的发送,当发送的数据第一位为1时音量升高,第二位为1时音量降低,其余位都为0…具体可以看一下手绘图,这是一一对应的一个关系,这种对应关系也是官方自己定义的
比如发送一个 {0x00, 0x01}(相当于0x0001)的数据给usb,具体表现出来就是 音量加的功能。为什么?首先发送数据之前得知道需要发多少Bytes(发送多少
Bytes = (Report Size * Report Count)/ 8);
其次每一个Usage的值只能取0(Logical Minimum)或者1(Logical Maximum);那就可以知道如果要发送音量加就需要设置为{0x00, 0x01}。
案例2
static const u8 keyfob_report_map[] = {
//通用按键
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report ID (3)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0B, // Report Count (11)
0x0A, 0x23, 0x02, // Usage (AC Home)
0x0A, 0x21, 0x02, // Usage (AC Search)
0x0A, 0xB1, 0x01, // Usage (AL Screen Saver)
0x09, 0xB8, // Usage (Eject)
0x09, 0xB6, // Usage (Scan Previous Track)
0x09, 0xCD, // Usage (Play/Pause)
0x09, 0xB5, // Usage (Scan Next Track)
0x09, 0xE2, // Usage (Mute)
0x09, 0xEA, // Usage (Volume Decrement)
0x09, 0xE9, // Usage (Volume Increment)
0x09, 0x30, // Usage (Power)
0x0A, 0xAE, 0x01, // Usage (AL Keyboard Layout)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x0D, // Report Size (13)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// 119 bytes
};
extern int ble_hid_data_send(u8 report_id, u8 *data, u16 len);
再来看这个案例,如果我们要实现
Usage (AC Search)
这个功能我们应该发送什么数据呢?依旧用位按键定位法,首先我们先算一下要发送多少字节的数据,可以看到有两个size和count,因此我们算出来的结果是111+131 = 24bit =3byte,因此我们要发送的字节数为3
而我们的操作数有12位,因此三字节发送的数据与之对应的是
0x000001(000000000000000000000001)—Usage (AC Home)
0x000002(000000000000000000000010)—Usage (AC Search)
0x000004(000000000000000000000100)—Usage (AL Screen Saver)
0x000008(000000000000000000001000)—Usage (Eject)
0x000100(000000000000000000010000)—Usage (Scan Previous Track)
…
如此往下一一按bit进行对应就可以实现自己的功能
以下是这个数组中每个数值(十六进制)的详细解释:
-
0x05, 0x0C:使用页 (Usage Page) - Consumer。说明此hid report map定义了一个消费者设备类别。
-
0x09, 0x01:使用 (Usage) - 消费器控制 (Consumer Control)。说明此hid report map定义了一个消费者控制按键。
-
0xA1, 0x01:集合 (Collection) - 应用程序 (Application)。指定本HID report map包含应用程序类型的控件,并且相邻的USSAGE数据由同一集合处理。
-
0x85, 0x03:报告 ID (Report ID) - 3。指定此hid report map定义的报告类型为ID号为3的报告。
-
0x15, 0x00:逻辑最小值 (Logical Minimum) - 0。指定接收此报告的系统(如电脑)将接收到的字节映射到具有最小可能值的数字值。
-
0x25, 0x01:逻辑最大值 (Logical Maximum) - 1。指定接收此报告的系统将接收到的字节映射到具有最大可能值的数字值。
-
0x75, 0x01:报告大小 (Report Size) - 1。指定其中每个按键事件的报告数据位数是1位,即开/关状态。
-
0x95, 0x0B:报告计数 (Report Count) - 11。指定此hid report map定义的报告中包括11个按键事件。
-
0x0A, 0x23, 0x02:使用 (Usage) - AC Home。指定第一个按键对应于AC Home使用码,表示主页功能键。
-
0x0A, 0x21, 0x02:使用 (Usage) - AC Search。指定第二个按键对应于AC Search使用码,表示搜索功能键。
-
0x0A, 0xB1, 0x01:使用 (Usage) - AL Screen Saver。指定第三个按键对应于AL Screen Saver使用码,表示启动屏幕保护程序。
-
0x09, 0xB8:使用 (Usage) - 弹出式按钮(Eject)。
-
0x09, 0xB6:使用 (Usage) - 扫描上一曲目 (Scan Previous Track)。
-
0x09, 0xCD:使用 (Usage) - 播放/暂停 (Play/Pause)。
-
0x09, 0xB5:使用 (Usage) - 扫描下一曲目 (Scan Next Track)。
-
0x09, 0xE2:使用 (Usage) - 静音 (Mute)。
-
0x09, 0xEA:使用 (Usage) - 音量减小 (Volume Decrement)。
-
0x09, 0xE9: 使用 (Usage) - 音量增大 (Volume Increment)。
-
0x09, 0x30:使用 (Usage) - 电源 (Power)。
-
0x0A, 0xAE, 0x01:使用 (Usage) - AL Keyboard Layout。指定最后一个按键对应于AL Keyboard Layout使用码,表示更改键盘布局。
-
0x81, 0x02:输入 (Input) - 数据、变量、绝对值、没有回绕、线性、优先状态、无空位置
-
0x95, 0x01:报告计数 (Report Count) - 1。指定此hid report map定义的报告中包括1个按键事件。
-
0x75, 0x0D:报告大小 (Report Size) - 13。指定其中每个按键事件的报告数据位数是13位,即keyfob_report_map数组中11个字节(共88位)全部使用,报告数据位长为13位,其他5bits使用填充符号“0”进行填充。
-
0x81, 0x03:输入 (Input) - 常量、变量、绝对值、没有回绕、线性、优先状态、无空位置。表示HID设备将输出一个全为0的字节作为该 Report ID 的最后一项,也就是按下按键后立即释放时触发的事件。因为退出报告只有该按键特殊事件,并需要调整指针来写入发送函数,所以状态应为常量。
-
0xC0:结束集合 (End Collection)。指定此 hid report map 定义的集合结束。
参考资料
https://blog.csdn.net/pig10086/article/details/71438990
https://blog.csdn.net/lep150510/article/details/119572626
3.如何自己实现一个报告描述符实现一个功能呢?
参考
https://www.usbzh.com/article/detail-830.html
4. 特点
HID协议的优点包括以下几个方面:
-
简单易用:HID协议定义的指令和数据格式都很简单,易于理解并进行开发。对于绝大多数的输入设备,我们只需要遵循HID协议来实现输入输出即可。
-
高效性能:HID协议使用了一种类似于中断传输的方式,在需要时及时传输数据,降低了延迟,并保证了数据的实时性。这使得HID协议更适合用于实时性要求比较高的应用。
-
可靠性:HID协议的实现通常基于硬件设计,配合驱动程序和操作系统支持,使得输入设备能够与计算机无缝连接和互通。也因此HID协议具有高可靠性、稳定性等特点,提高了产品的品质。
此外,HID协议还支持多种输入设备类型(如键盘、鼠标、摇杆等),并且支持对输入设备进行配置和控制,如设置灯光、自定义按键功能等。
总的来说,HID协议是一种很好的通讯协议,使得输入设备的设计和使用更加简单、高效、可靠,同时也提高了用户的使用体验。