Skip to content

USB HID 模拟鼠标操作

1. 什么是 USB HID ?

USB HID 是一种通用标准,用于连接 USB 设备和计算机。它允许计算机和 USB 设备之间进行通信,并提供了一种标准的接口,用于处理输入和输出。USB HID 设备可以包括键盘、鼠标、游戏控制器、传感器等。 它与CDC 的主要区别在于,CDC 是用于串行通信的,而 HID 是用于输入和输出的。

2. USB HID 测试的引脚定义

没有对应的特殊要求

3. stm32cubeMX中USB HID的配置

  1. 打开STM32CubeMX,选择你的STM32芯片型号,并创建一个新的项目。
  2. 在“Pinout & Configuration”选项卡中,找到“Connectivity”部分,点击“USB”选项。
  3. 在“Configuration”选项卡中,找到“Device(FS)”选项,并勾选“Device”选项。
  4. 在 "Middlewares and software packages" 选项卡中,找到 "USB Device" 选项,并点击.
  5. 在 "Class For FS IP" 下拉菜单中选择 "Human Interface Device(HID)"。 选项。
  6. 下面的选项部分还要特别注意。
  7. 其中自供电模式是
    • 如果选择 Enabled,那么USB设备必须自供电.
    • 如果选择 Disabled,那么USB设备不能自供电, 需要通过USB主机供电, 对于像键盘鼠标这种设备,就需要设置成 Disabled.
  8. 需要注意的是: USB接口,需要配置时钟为48MHZ.

4. 分析USB HID设备代码

一般USB的设备描述符包含三种: USBD_HID_CfgFSDesc, USBD_HID_CfgHSDesc, USBD_HID_OtherSpeedCfgDesc, 分别对应的全速\高速\其它速度的描述符.这三种描述符相差不大,所有的代码都是相同的,只是最后一行的速度不同.

c
__ALIGN_BEGIN static uint8_t USBD_HID_CfgFSDesc[USB_HID_CONFIG_DESC_SIZ]  __ALIGN_END =
{

    0x09, /* bLength: Configuration Descriptor size */
    USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
    USB_HID_CONFIG_DESC_SIZ,
    /* wTotalLength: Bytes returned */
    0x00,
    0x01,         /*bNumInterfaces: 接口数量(1)*/
    0x01,         /*bConfigurationValue: 配置编号(1)*/
    0x00,         /*iConfiguration: 配置字符串索引(0=无)*/
    0xE0,         /*bmAttributes:   设备属性(0xe0=自供电、远程唤醒) */
    0x32,         /*MaxPower 100 mA: 电源消耗(100mA)*/

  /************** Descriptor of Joystick Mouse interface ****************/
    0x09,         /*bLength: 接口描述符大小(9字节)*/
    USB_DESC_TYPE_INTERFACE, /*bDescriptorType: 描述符类型(接口=0x04)*/
    0x00,         /*bInterfaceNumber: 接口编号(0)*/
    0x00,         /*bAlternateSetting: 备用设置编号(0)*/
    0x01,         /*bNumEndpoints: 此接口使用的端点数量(1个)*/
    0x03,         /*bInterfaceClass: 接口类(HID=0x03)*/
    0x01,         /*bInterfaceSubClass: 接口子类(启动协议=1)*/
    0x02,         /*nInterfaceProtocol: 接口协议(鼠标=2)*/
    0,            /*iInterface: 接口字符串描述符索引(0=无)*/
  /******************** Descriptor of Joystick Mouse HID ********************/
    0x09,         /*bLength: HID描述符大小(9字节)*/
    HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID类型(0x21)*/
    0x11,         /*bcdHID: HID类规范版本号(1.11)*/
    0x01,
    0x00,         /*bCountryCode: 国家代码(0=不支持本地化)*/
    0x01,         /*bNumDescriptors: 后续描述符数量(1个)*/
    0x22,         /*bDescriptorType: 报告描述符类型(0x22)*/
    HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: 报告描述符长度*/
    0x00,
  /******************** Descriptor of Mouse endpoint ********************/
    0x07,          /*bLength: 端点描述符大小(7字节)*/
    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点类型(0x05)*/
    HID_EPIN_ADDR,     /*bEndpointAddress: 端点地址(IN方向)*/
    0x03,          /*bmAttributes: 端点属性(中断传输)*/
    HID_EPIN_SIZE, /*wMaxPacketSize: 最大包大小*/
    0x00,
    HID_FS_BINTERVAL,          /*bInterval: 轮询间隔*/
    /* 34 */
    };

您观察得非常准确!这段配置描述符确实包含三个关键部分,这三部分共同定义了一个USB HID鼠标设备的核心特性。让我逐一详细解析:

一、整体结构解析

这是一个USB配置描述符,它定义了设备的一种工作配置。在您的代码中,它包含:

配置描述符头 (9字节) + 接口描述符 (9字节) + HID描述符 (9字节) + 端点描述符 (7字节)

总共34字节,与代码最后的注释/* 34 */对应。

二、各部分详细解析

1. 接口描述符 (Interface Descriptor)

c
/************** Descriptor of Joystick Mouse interface ****************/
0x09,         /*bLength: 接口描述符大小(9字节)*/
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: 描述符类型(接口=0x04)*/
0x00,         /*bInterfaceNumber: 接口编号(0)*/
0x00,         /*bAlternateSetting: 备用设置编号(0)*/
0x01,         /*bNumEndpoints: 此接口使用的端点数量(1个)*/
0x03,         /*bInterfaceClass: 接口类(HID=0x03)*/
0x01,         /*bInterfaceSubClass: 接口子类(启动协议=1)*/
0x02,         /*nInterfaceProtocol: 接口协议(鼠标=2)*/
0,            /*iInterface: 接口字符串描述符索引(0=无)*/

作用:告诉主机"我有一个接口,它是HID鼠标"

  • bInterfaceNumber: 0 - 这是设备的第一个接口
  • bInterfaceClass: 0x03 - 这是 HID类设备
  • nInterfaceProtocol: 0x02 - 具体是鼠标 (1=键盘,2=鼠标)

2. HID描述符 ( HID Descriptor)

c
/******************** Descriptor of Joystick Mouse HID ********************/
0x09,         /*bLength: HID描述符大小(9字节)*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID类型(0x21)*/
0x11,         /*bcdHID: HID类规范版本号(1.11)*/
0x01,
0x00,         /*bCountryCode: 国家代码(0=不支持本地化)*/
0x01,         /*bNumDescriptors: 后续描述符数量(1个)*/
0x22,         /*bDescriptorType: 报告描述符类型(0x22)*/
HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: 报告描述符长度*/
0x00,

作用:提供HID设备的详细信息

  • bcdHID: 0x0111 - 符合HID规范1.11版本
  • bCountryCode: 0x00 - 美国英语布局
  • 关键信息0x22表示后面有一个报告描述符,长度为HID_MOUSE_REPORT_DESC_SIZE
    • 报告描述符定义了鼠标的数据格式(X/Y移动、按钮状态等)

3. 端点描述符 (Endpoint Descriptor)

c
/******************** Descriptor of Mouse endpoint ********************/
0x07,          /*bLength: 端点描述符大小(7字节)*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点类型(0x05)*/
HID_EPIN_ADDR,     /*bEndpointAddress: 端点地址(IN方向)*/
0x03,          /*bmAttributes: 端点属性(中断传输)*/
HID_EPIN_SIZE, /*wMaxPacketSize: 最大包大小*/
0x00,
HID_FS_BINTERVAL,          /*bInterval: 轮询间隔*/

作用:定义数据传输的具体通道

  • HID_EPIN_ADDR - 端点地址(如0x81,表示端点1,IN方向)
  • bmAttributes: 0x03 - 使用中断传输模式(最适合HID设备)
  • HID_EPIN_SIZE - 每次传输的最大数据量(通常4字节)
  • HID_FS_BINTERVAL - 轮询间隔(您在之前配置的0x0A=10ms)

三、从USB协议角度理解这三部分

想象一下USB通信就像建立一个公司组织架构

接口描述符 = 部门定义

  • 定义了这是一个"鼠标部门"(HID鼠标接口)
  • 确定了部门编号(0号部门)
  • 指定了部门类型(HID类,鼠标协议)

HID描述符 = 部门工作规范

  • 定义了部门的操作手册(HID规范1.11版)
  • 说明了部门需要填写的"报表格式"(报告描述符定义了鼠标数据格式)
  • 指定了工作语言(英语,代码页0)

端点描述符 = 数据传输通道

  • 定义了一个专用的"汇报通道"(端点)
  • 确定了汇报频率(每10ms汇报一次)
  • 限定了每次汇报的数据量(最大4字节)
  • 指定了汇报方向(设备→主机,IN方向)

四、这三部分如何协同工作?

  1. 设备插入时:主机读取整个配置描述符
  2. 识别设备类型:看到接口描述符,知道是HID鼠标
  3. 加载驱动:根据HID描述符,加载相应的HID驱动程序
  4. 建立通信:根据端点描述符,建立中断传输通道
  5. 数据传输:设备每10ms通过指定的端点发送鼠标数据

五、实际工作流程示例

c
// 当鼠标移动时:
1. 固件准备好鼠标数据(例如:按钮状态,X/Y位移)
2. 通过端点1(HID_EPIN_ADDR)发送数据
3. 主机每10ms检查一次这个端点
4. 收到数据后,操作系统移动光标

六. 总结

有了上面的描述,我们就大体知道了这个HID鼠标设备具体工作流程了. 但是还有一个问题: 如何定义鼠标数据格式? 这就需要另一个描述符: 鼠标报告描述符, 这个描述符定义了鼠标数据的具体格式, 包括按钮状态、X/Y位移等信息.

5. 分析鼠标报告描述符

c

__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]  __ALIGN_END =
{

    //------  定义设备为鼠标    -------
    0x05, 0x01,  //    Usage Page (Generic Desktop)     - 用途页:通用桌面
    0x09, 0x02,  //    Usage (Mouse)                    - 用途:鼠标
    0xA1, 0x01,  //    Collection (Application)         - 开始应用集合
    0x09, 0x01,  //    Usage (Pointer)               -   用途:指针设备

    // ##############  结束物理集合  ##############
    0xA1, 0x00,  //     Collection (Physical)          -   开始物理集合
    //------  定义3个按钮  -------
    0x05, 0x09,  //     Usage Page (Button)          -     用途页:按钮
    0x19, 0x01,  //     Usage Minimum (1)           -     最小用途:按钮1
    0x29, 0x03,  //     Usage Maximum (3)           -     最大用途:按钮3
    0x15, 0x00,  //     Logical Minimum (0)         -     逻辑最小值:0
    0x25, 0x01,  //     Logical Maximum (1)         -     逻辑最大值:1
    // =====  定义3个按钮  =====
    0x95, 0x03,  //     Report Count (3)            -     报告数量:3个
    0x75, 0x01,  //     Report Size (1)             -     报告大小:每个1位
    0x81, 0x02,  //     Input (Data,Var,Abs)        -     输入:数据,变量,绝对值
    //  ====  定义了5位的常量填充位,凑齐1字节  =====
    0x95, 0x01,  //     Report Count (1)            -     报告数量:1个
    0x75, 0x05,  //     Report Size (5)             -     报告大小:5位
    0x81, 0x01,  //     Input (Cnst,Arr,Abs)        -     输入:常量,数组,绝对值
    // ------  定义X/Y位移  -------
    0x05, 0x01,  //     Usage Page (Generic Desktop) -     用途页:通用桌面
    0x09, 0x30,  //     Usage (X)                    -     用途:X轴
    0x09, 0x31,  //     Usage (Y)                    -     用途:Y轴
    0x09, 0x38,  //     Usage (Wheel)                -     用途:滚轮
    0x15, 0x81,  //     Logical Minimum (-127)       -     逻辑最小值:-127
    0x25, 0x7F,  //     Logical Maximum (127)        -     逻辑最大值:+127
    0x75, 0x08,  //     Report Size (8)             -     报告大小:8位
    0x95, 0x03,  //     Report Count (3)            -     报告数量:3个
    0x81, 0x06,  //     Input (Data,Var,Rel)        -     输入:数据,变量,相对值
    // ##############  结束物理集合  ##############
    0xC0,       //   End Collection                  -   结束物理集合
    // --------  额外的功能定义  --------
    0x09, 0x3c, //   Usage (Motion Wakeup)          -   用途:运动唤醒
    0x05, 0xff, //   Usage Page (Vendor Defined)     -  用途页:厂商定义
    0x09, 0x01, //   Usage (Vendor 1)               -   用途:厂商1
    0x15, 0x00, //   Logical Minimum (0)             -  逻辑最小值:0
    0x25, 0x01, //   Logical Maximum (1)            -   逻辑最大值:1
    0x75, 0x01, //   Report Size (1)                -   报告大小:1位
    0x95, 0x02, //   Report Count (2)               -   报告数量:2个
    0xb1, 0x22, //   Feature (Data,Var,Abs,NoPref)  -   特征:数据,变量,绝对值
    0x75, 0x06, //   Report Size (6)                -   报告大小:6位
    0x95, 0x01, //   Report Count (1)               -   报告数量:1个
    0xb1, 0x01, //   Feature (Cnst,Arr,Abs)         -   特征:常量,数组,绝对值
    // ##############  结束物理集合  ##############
    0xC0,       //   End Collection 
};