USB HID Mouse

该示例通过 USB 控制上报给PC的数据,实现改变PC显示器中光标的移动方向和位置。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考:快速入门

配置选项

该示例可配置的宏如下:

  1. DLPS_EN :配置该宏可选择USB在SUSPEND后是否进入DLPS, 1 表示允许进入DLPS, 0 不允许进入DLPS。

  2. REMOTE_WAKEUP_EN :配置该宏可以选择是否配置远程唤醒测试流程, 1 表示配置远程唤醒, 0 不配置远程唤醒。

备注

如果 REMOTE_WAKEUP_EN 设置为1,连接P0_6和VCC,按下复位按键重新初始化IC,等PC进入睡眠状态后连接P0_6和VDD,可以观察到PC的屏幕被点亮,即远程唤醒成功。

编译和下载

该示例的工程路径如下:

Project file: samples\usb\usb_hid_mouse\proj\mdk

Project file: samples\usb\usb_hid_mouse\proj\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MPTool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

USB数据线接到PC上后,可以在设备管理器的mouse栏目识别到多了一个hid-mouse,也可以查看PID和VID设置,即可判断识别设备枚举过程都成功;如果将上报数据的x轴数据设置为1,并多次调用上报函数,即可观测到鼠标一直在屏幕上右移。

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  1. 工程路径: sdk\samples\usb\usb_hid_mouse\proj

  2. 源码路径: sdk\samples\usb\usb_hid_mouse\src

该工程的工程文件代码架构如下:

└── Project: input_polling
    └── secure_only_app
        └── Device                   includes startup code
            ├── startup_rtl.c
            └── system_rtl.c
        ├── CMSIS                    includes CMSIS header files
        ├── CMSE Library             Non-secure callable lib
        └── Lib                      includes all binary symbol files that user application is built on
            ├── usb.lib
            └── usb_hal.lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c
            ├── app_task.c
            ├── app_usb.c
            ├── usb_dev.c
            └── usb_hid.c

初始化

调用 app_usb_init 对USB模块进行初始化,包括:注册回调函数,初始化USB设备和配置描述符,初始化USB接口和端点,初始化HID。

usb_dm_cb_register(app_usb_dm_cb);
usb_spd_cb_register(app_usb_spd_cb);

T_USB_CORE_CONFIG config = {.speed = USB_HIGH_SPEED, .class_set = {.hid_enable = 1, .uac_enable = 0}};
usb_dm_core_init(config);

usb_dev_init();
usb_hid_init();
app_usb_start();

app_usb_init 中包含了如下流程:

  1. usb_dm_cb_register() :注册USB动态管理回调函数,可以在 app_usb_dm_cb 中查看当前USB状态。

    typedef enum
    {
    USB_PDN = 0,
    USB_ATTACHED = 1,
    USB_POWERED = 2,
    USB_DEFAULT = 3,
    USB_ADDRESSED = 4,
    USB_CONFIGURED = 5,
    USB_SUSPENDED = 6,
    } T_USB_POWER_STATE;
    

    备注

    对于USB状态的说明,详见 常见问题

  2. usb_spd_cb_register():注册USB速度获取回调函数,可以获取到当前枚举到的USB速度是全速还是高速。

  3. usb_dm_core_init():设置USB速度以及需要开启的USB功能, 如本示例中想设置速度为高速,开启USB HID 功能,可以设置speed为 USB_HIGH_SPEED,hid_enable为 1

  4. usb_dev_init:初始化USB device相关的描述符。

    static T_USB_DEVICE_DESC usb_dev_desc =
    {
        // The following items are defined in device descriptor.
        .idVendor= 0x0bda,
        .idProduct = 0x8762,
        .bcdDevice = 0x0200,
        .iManufacturer = STRING_ID_MANUFACTURER,
        .iProduct= STRING_ID_PRODUCT,
        .iSerialNumber= STRING_ID_SERIALNUM,
    }
    static T_USB_CONFIG_DESC usb_cfg_desc =
    {
        // The following items are defined in config descriptor.
        .bNumInterfaces=2,
        .bmAttributes= 0xA, // remote wake up
        .bcdDevice = 0x0200,
        .bMaxPower = 0x32,  // The unit is 2mA
    }
    

    备注

    对于USB描述符的配置,详见 常见问题

  5. usb_hid_init :初始化USB hid class 相关的描述符以及回调函数。

    1. 配置USB HID接口、端点相关的描述符以及报告描述。

      1. 接口描述符设置如下:

        static T_USB_INTERFACE_DESC hid_std_if_desc =
        {
            .bLength            = sizeof(T_USB_INTERFACE_DESC),
            .bDescriptorType    = USB_DESC_TYPE_INTERFACE,
            .bInterfaceNumber   = 0,
            .bAlternateSetting  = 0,
            .bNumEndpoints      = 1,
            .bInterfaceClass    = USB_CLASS_CODE_HID,
            .bInterfaceSubClass = 1,  // Is boot interface supported
            .bInterfaceProtocol = 2,
            .iInterface         = 0,
        };
        
      2. USB端点描述符设置如下:

        static T_USB_ENDPOINT_DESC int_in_ep_desc_fs =
        {
            .bLength           = sizeof(T_USB_ENDPOINT_DESC),
            .bDescriptorType   = USB_DESC_TYPE_ENDPOINT,
            .bEndpointAddress  = USB_DIR_IN | 0x01,
            .bmAttributes      = USB_EP_TYPE_INT,
            .wMaxPacketSize    = 0x40,
            .bInterval         = 1,
        };
        
        static T_USB_ENDPOINT_DESC int_in_ep_desc_hs =
        {
            .bLength           = sizeof(T_USB_ENDPOINT_DESC),
            .bDescriptorType   = USB_DESC_TYPE_ENDPOINT,
            .bEndpointAddress  = USB_DIR_IN | 0x01,
            .bmAttributes      = USB_EP_TYPE_INT,
            .wMaxPacketSize    = 0x40,
            .bInterval         = 4,
        };
        
      3. USB HID描述符如下:

        static T_HID_CS_IF_DESC  hid_cs_if_desc =
        {
            .bLength            = sizeof(T_HID_CS_IF_DESC),
            .bDescriptorType    = DESC_TYPE_HID,
            .bcdHID             = 0x0110,
            .bCountryCode       = 0,
            .bNumDescriptors    = 1,
            .desc[0]            =
            {
                .bDescriptorType = DESC_TYPE_REPORT,
                .wDescriptorLength = sizeof(report_descs),
            },
        
        };
        
      4. USB HID报告描述符如下:

        static const char report_descs[] =
        {
            0x06, 0x0B, 0xFF,   // Usage Page (Vendor Defined 0xFF0B)
            0x0A, 0x04, 0x01,   // Usage (0x0104)
            0xA1, 0x01,         // Collection (Application)
            // 8-bit data
            0x15, 0x00,                         // Logical Minimum (0)
            0x26, 0xFF, 0x00,                   // Logical Maximum (255)
            0x75, 0x08,                         // Report Size (8)
            0x95, 0x3c,                         // Report Count (60)
            0x85, REPORT_ID_CFU_FEATURE,        // Report ID (0x2A)
            0x09, 0x60,                         // Usage (0x60)
            0x82, 0x02, 0x01,                   // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
            0x09, USAGE_ID_CFU_PAYLOAD_OUTPUT,  // Usage (0x61)
            0x92, 0x02, 0x01,                   // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
            0x09, USAGE_ID_CFU_FEATURE,         // Usage (0x62)
            0xB2, 0x02, 0x01,                   // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
            0x85, REPORT_ID_CFU_FEATURE_EX,     // Report ID (0x2B)
            0x09, USAGE_ID_CFU_FEATURE_EX,      // Usage (0x65)
            0xB2, 0x02, 0x01,                   // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
            // 32-bit data
            0x17, 0x00, 0x00, 0x00, 0x80,       // Logical Minimum (-2147483649)
            0x27, 0xFF, 0xFF, 0xFF, 0x7F,       // Logical Maximum (2147483646)
            0x75, 0x20,                         // Report Size (32)
            0x95, 0x04,                         // Report Count (4)
            0x85, REPORT_ID_CFU_PAYLOAD_INPUT,  // Report ID (0x2C)
            0x19, USAGE_ID_CFU_PAYLOAD_INPUT_MIN,// Usage Minimum (0x66)
            0x29, USAGE_ID_CFU_PAYLOAD_INPUT_MAX,// Usage Maximum (0x69)
            0x81, 0x02,                         // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
            0x85, REPORT_ID_CFU_OFFER_INPUT,    // Report ID (0x2D)
            0x19, USAGE_ID_CFU_OFFER_INPUT_MIN, // Usage Minimum (0x8A)
            0x29, USAGE_ID_CFU_OFFER_INPUT_MAX, // Usage Maximum (0x8D)
            0x81, 0x02,                         // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
            0x19, USAGE_ID_CFU_OFFER_OUTPUT_MIN,// Usage Minimum (0x8E)
            0x29, USAGE_ID_CFU_OFFER_OUTPUT_MAX,// Usage Maximum (0x91)
            0x91, 0x02,                         // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
            0xC0,                               // End Collection
        
            0x05, 0x01,     /* USAGE_PAGE       (Generic Desktop) main item*/
            0x09, 0x02,     /* USAGE            (Mouse) */
            0xa1, 0x01,     /* COLLECTION       (Application) */
            0x85, MOUSE_REPORT_ID, /* REPORT_ID        (2) */
            0x09, 0x01,     /* USAGE            (Pointer) */
            0xa1, 0x00,     /* COLLECTION       (Physical) */
            0x05, 0x09,     /* USAGE_PAGE       (Button) */
            0x19, 0x01,     /* USAGE_MINIMUM    (Button 1) */
            0x29, 0x05,     /* USAGE_MAXIMUM    (Button 5) */
            0x15, 0x00,     /* LOGICAL_MINIMUM  (0) */
            0x25, 0x01,     /* LOGICAL_MAXIMUM  (1) */
            0x95, 0x05,     /* REPORT_COUNT     (5) */
            0x75, 0x01,     /* REPORT_SIZE      (1) */
            0x81, 0x02,     /* INPUT            (Data,Var,Abs) */
            0x95, 0x01,     /* REPORT_COUNT     (1) */
            0x75, 0x03,     /* REPORT_SIZE      (3) */
            0x81, 0x01,     /* INPUT            (Cnst,Var,Abs) */
            0x05, 0x01,     /* USAGE_PAGE       (Generic Desktop) main item*/
            0x09, 0x30,     /* USAGE            (X) */
            0x09, 0x31,     /* USAGE            (Y) */
            0x16, 0x01, 0x80,   /* LOGICAL_MINIMUM  (-32767) */
            0x26, 0xff, 0x7f,   /* LOGICAL_MAXIMUM  (32767) */
            0x75, 0x10,     /* REPORT_SIZE      (16) */
            0x95, 0x02,     /* REPORT_COUNT     (2) */
            0x81, 0x06,     /* INPUT            (Data,Var,Rel) */
            0x09, 0x38,     /* USAGE            (Wheel) */
            0x15, 0x81,     /* LOGICAL_MINIMUM  (-127) */
            0x25, 0x7f,     /* LOGICAL_MAXIMUM  (127) */
            0x75, 0x08,     /* REPORT_SIZE      (8) */
            0x95, 0x01,     /* REPORT_COUNT     (1) */
            0x81, 0x06,     /* INPUT            (Data,Var,Rel) */
            0x05, 0x0c,     /* USAGE_PAGE       (Consumer)*/
            0x0a, 0x38, 0x02,   /* USAGE            (AC Pan) */
            0x15, 0x81,     /* LOGICAL_MINIMUM  (-127) */
            0x25, 0x7f,     /* LOGICAL_MAXIMUM  (127) */
            0x75, 0x08,     /* REPORT_SIZE      (8) */
            0x95, 0x01,     /* REPORT_COUNT     (1) */
            0x81, 0x06,     /* INPUT            (Data,Var,Rel) */
            0xc0,           /* END_COLLECTION */
            0xc0,           /* END_COLLECTION */
        
            0x06, 0x01, 0xff, /* USAGE_PAGE       (vendor) */
            0x09, 0x01,     /* USAGE            (vendor) */
            0xa1, 0x01,     /* COLLECTION       (Application) */
            0x85, VENDOR_REPORT_ID, /* REPORT_ID        (0x10) */
            0x19, 0x00,     /* USAGE_MINIMUM    (0) */
            0x29, 0xff,     /* USAGE_MAXIMUM    (0xff) */
            0x15, 0x00,     /* LOGICAL_MINIMUM  (0) */
            0x26, 0xff, 0x00,     /* LOGICAL_MAXIMUM  (0xff) */
            0x75, 0x08,     /* REPORT_SIZE      (8) */
            0x95, 0x04,     /* REPORT_COUNT     (4) */
            0x81, 0x02,     /* INPUT            (Data,Var,Abs) */
            0xc0,           /* END_COLLECTION */
        };
        
    2. 注册usb接口相关请求的回调函数。

    T_USB_HID_DRIVER_CBS cbs;
    cbs.get_report = usb_hid_get_report;
    cbs.set_report = usb_hid_set_report;
    cbs.get_protocol = usb_hid_get_protocol;
    cbs.set_protocol = usb_hid_set_protocol;
    cbs.get_idle = usb_hid_get_idle;
    cbs.set_idle = usb_hid_set_idle;
    
    usb_hid_driver_cbs_register(inst, &cbs);
    
    1. 初始化USB hid driver。

  6. app_usb_start : 初始化和使能USB,打开USB clock。

  7. 实现控制鼠标发送数据,可以调用 app_cfu_usb_send_data 来上报数据给PC。

    1. app_cfu_usb_send_data 有三个参数,分别是USB report id,数据指针和数据长度。

    2. 初次调用该函数需要先调用 app_cfu_tpl_open 初始化USB缓存队列,以存放report data。

    bool app_cfu_usb_send_data(uint8_t report_id, uint8_t *data, uint16_t len)
    {
        uint8_t *header = NULL;
        uint16_t data_len = sizeof(report_id) + len;
        bool ret = false;
    
        header = malloc(data_len);
        if (!header)
        {
            APP_PRINT_ERROR0("app_cfu_usb_send_data: no memory for data");
            return ret;
        }
        header[0] = report_id;
        memcpy(&header[1], data, len);
        app_cfu_xmit(header, data_len);
        free(header);
        return ret;
    }
    

功能实现

初始化后,调用 app_cfu_usb_send_data 上报数据,实现控制HID设备发送数据到PC。

uint8_t Report[7] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t ReportLen = 7;
app_cfu_usb_send_data(0x01, Report, ReportLen);

常见问题

usb 状态说明

usb device 描述符

  • idVender

    设置为 0x0bda:厂商ID号。长度为两字节,实验中选取了0x0bda为厂商ID号。

  • idProduct

    设置为 0x8762:产品ID号。长度为两字节,实验中选取了0x8762为产品ID号。

  • bcdDevice

    设置为 0x0200:设备的版本号。长度为2字节,实验中选取了0x0200,当同一个产品升级后,可以通过修改设备的版本号来进行区别。

  • pManufacturerStr

    设置为 RealTek:描述厂商的字符串。

  • pProductStr

    设置为 USB HID Device:描述产品的字符串。

  • pSerialNumberStr

    设置为 L"0123456789A:描述设备的序列号的字符串。

usb configuration 描述符

  • bNumInterfaces

    设置为 2 :即支持2个interface。

  • bmAttributes

    设置 0xA0 :设置设备支持远程唤醒。

  • bMaxPower

    设置为 32 :设备需要从总线中获取64mA的最大电流,单位为2mA。