菜鸟科技网

USB HID命令具体如何发送与解析?

核心概念:什么是 USB HID 命令?

首先要明确一个关键点:USB HID 并没有一个像网络协议那样标准化的“命令”列表

USB HID命令具体如何发送与解析?-图1
(图片来源网络,侵删)

它不像 Modbus 或 SPI 有固定的 0x03 读寄存器、0x06 写寄存器这样的操作码,相反,HID 的“命令”是通过一个数据包(称为 Report)来传递的,而这个数据包的格式和内容由设备自己定义

  • 设备端(固件):开发者设计一个数据结构(一个包含 8 个字节的数组),并规定每个字段的含义,第一个字节是按键码,第二个字节是鼠标 X 轴偏移,第三个字节是鼠标 Y 轴偏移。
  • 主机端(驱动/应用程序):必须知道并遵守这个预先定义好的数据格式,当主机收到这个数据包时,它会按照预先的约定去解析每个字节。

当谈论“HID 命令”时,我们实际上是在谈论“自定义的 Report 格式”以及“如何通过这个格式来发送指令或数据”


HID 的基石:Report 描述符

理解 Report 描述符是掌握 HID 命令的关键,它是设备向主机描述自己数据结构的“说明书”,主机通过解析这个描述符,才能知道:

  • 设备有哪些功能(有 64 个按键,一个 X 轴,一个 Y 轴)。
  • 每个功能的数据是什么类型(是按键(Usage Page 0x07)、鼠标(Usage Page 0x01)还是自定义数据(Usage Page 0xFF00))。
  • 数据的格式(是数组、变量还是常量)。
  • 数据的范围和逻辑最小/最大值。

一个典型的 HID 命令(数据包)的生成流程如下:

USB HID命令具体如何发送与解析?-图2
(图片来源网络,侵删)
  1. 设计 Report 描述符:在设备固件中,开发者定义好 Report 描述符。
  2. 主机枚举并解析:当设备插入时,主机读取并解析这个 Report 描述符,生成一个“报告 ID”(Report ID)和“报告大小”的映射关系。
  3. 构建数据包:设备根据 Report 描述符的格式,填充一个数据数组。
  4. 发送数据包:设备通过 USB 端点(通常是端点 1 IN/OUT)将这个数据包发送给主机。
  5. 主机解析:主机收到数据包后,根据之前解析的 Report 描述符,正确地解读出数据。

HID 命令的实际形式与示例

让我们来看几个具体的例子,理解 HID 命令是如何工作的。

示例 1:最简单的自定义设备

假设我们想做一个设备,它只有一个功能:当主机发送一个 0xAA 命令时,设备上的 LED 亮;发送 0x55 时,LED 灭。

设备端(固件设计):

  1. 定义 Report 描述符: 这个设备需要一个输入报告(从设备到主机,通常用于状态反馈,我收到了命令”)和一个输出报告(从主机到设备,用于控制命令,点亮 LED”)。

    USB HID命令具体如何发送与解析?-图3
    (图片来源网络,侵删)

    一个简化的描述符可能是这样的(使用 C 语言风格描述):

    // 输出报告: 1字节,用于接收主机的控制命令
    0x06, 0xFF, 0xFF, // Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,       // Usage (Vendor Usage 1)
    0x15, 0x00,       // Logical Minimum (0)
    0x26, 0xFF, 0x00, // Logical Maximum (255)
    0x75, 0x08,       // Report Size (8 bits)
    0x95, 0x01,       // Report Count (1)
    0x09, 0x00,       // Usage (Vendor Usage 0)
    0x81, 0x02,       // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x09, 0x00,       // Usage (Vendor Usage 0)
    0x91, 0x02,       // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0xC0,             // End Collection

    解读

    • Usage Page 0xFF00:表示这是厂商自定义的功能。
    • Output Report:定义了一个 8 位(1字节)的输出报告,这就是我们的“命令”入口,主机向这个报告写入 0xAA0x55
    • Input Report:定义了一个 8 位的输入报告,设备可以用来反馈状态,命令已收到”。
  2. 固件逻辑

    • 在固件的 USB_Setup() 或中断服务程序中,检查是否有数据从主机的 Output Report 端点(通常是端点 1 OUT)到来。
    • 如果收到了数据,读取这个 1 字节的数据。
    • 如果数据是 0xAA,点亮 LED;如果是 0x55,熄灭 LED。
    • (可选)向主机的 Input Report 端点(端点 1 IN)发送一个 0x01,表示“操作成功”。

主机端(应用程序设计,Python + hidapi):

import hid
# 1. 打开设备 (需要知道设备的 Vendor ID 和 Product ID)
device = hid.hid_open(0x1234, 0x0001) # 替换成你的 VID/PID
if not device:
    print("设备未找到!")
    exit()
# 2. 发送命令:点亮 LED
# hid_write 的第二个参数就是 Output Report 的数据
# 注意:有些设备可能需要一个 Report ID (通常是第一个字节)
# 假设我们的 Report ID 是 0
command_on = [0x00, 0xAA] # Report ID (0) + Command (0xAA)
hid_write(device, command_on)
# 3. 发送命令:熄灭 LED
command_off = [0x00, 0x55] # Report ID (0) + Command (0x55)
hid_write(device, command_off)
# 4. (可选) 读取设备反馈的数据
# hid_read 会从 Input Report 端点读取数据
data = hid_read(device, 64, 5000) # 读取最多64字节,超时5秒
if data:
    print(f"从设备收到反馈: {data}")
hid_close(device)

在这个例子中,[0xAA][0x55] 就是我们的“HID 命令”。


示例 2:更复杂的命令(带 Report ID)

当一个设备有多个功能(同时处理键盘、鼠标和自定义命令)时,就需要使用 Report ID 来区分不同的报告。

假设一个设备:

  • Report ID 1:键盘输入。
  • Report ID 2:鼠标输入。
  • Report ID 3:自定义控制命令。

主机发送命令给 Report ID 3:

数据包的结构会是:[Report ID] + [Command Data]

发送一个“读取设备版本号”的命令:

  • Report ID: 0x03
  • Command: 0x01 (代表“读取版本号”)
  • 数据包: [0x03, 0x01]

设备收到 [0x03, 0x01] 后,就知道这是发给“自定义控制”功能的“读取版本号”命令,然后执行相应操作,并通过 Input Report(Report ID 0x03)将版本号(如 0x01, 0x00 代表 V1.0)发回。


通用 HID (HID) vs. 自定义 HID (Vendor-defined)

特性 通用 HID (e.g., Keyboard, Mouse) 自定义 HID (Vendor-defined)
Report 描述符 遵循 USB HID 规范中预定义的 Usage Page (如 0x01 Generic Desktop, 0x07 Keyboard)。 使用厂商自定义的 Usage Page (0xFF00 - 0xFFFF)。
驱动 操作系统自带通用 HID 驱动,无需安装额外驱动。 通常需要安装 INF 文件或使用通用 HID 驱动(如 hidapi, libusb)。
命令/数据 格式是标准化的,键盘扫描码是固定的。 格式完全由开发者自定义,非常灵活。
适用场景 标准人机交互设备。 工业控制、嵌入式系统、DIY 项目、数据采集等。

对于大多数开发者来说,自定义 HID 是更常用的选择,因为它给予了最大的自由度。


实用工具和库

  • 分析工具:

    • HIDusbfy: Windows 下的经典工具,可以实时监控 HID 通信,查看收发的 Report 数据。
    • Wireshark + USBPcap: 可以抓取底层的 USB 数据包,非常强大。
    • Linux hidrd: 命令行工具,用于转换和解析 Report 描述符。
  • 主机端开发库:

    • hidapi: 跨平台 (Windows, macOS, Linux, Android) 的 C/C++ 库,是开发 HID 应用的首选。
    • Python hidapi: Python 封装的 hidapi,使用非常方便。
    • .NET HidLibrary: .NET 平台下常用的库。
    • Java jinput / hid4java: Java 平台下的库。
  1. HID 命令的本质:不是固定的指令码,而是遵循设备自定义格式的数据包(Report)
  2. 核心是 Report 描述符:它是设备与主机之间的“协议说明书”,定义了数据包的结构和含义。
  3. 双向通信:使用 Input Report(设备→主机)和 Output Report(主机→设备)进行数据交换。
  4. 灵活性:自定义 HID 极其灵活,允许你设计任何你需要的数据格式,是嵌入式与 PC 通信的强大工具。
  5. 开发流程
    • 设备端:编写固件,定义 Report 描述符,实现数据收发逻辑。
    • 主机端:使用 HID 库(如 hidapi),根据设备的 Report 描述符来构建和解析数据包。

理解了这些,你就可以开始设计自己的 USB HID 设备和与之通信的应用程序了。

分享:
扫描分享到社交APP
上一篇
下一篇