USB HID Mouse
该示例通过 USB 控制上报给PC的数据,实现改变PC显示器中光标的移动方向和位置。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL87x2G HDK |
RTL87x2G EVB |
更多信息请参考:快速入门
配置选项
该示例可配置的宏如下:
DLPS_EN
:配置该宏可选择USB在SUSPEND后是否进入DLPS,1
表示允许进入DLPS,0
不允许进入DLPS。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
请按照以下步骤操作构建并运行该示例:
测试验证
USB数据线接到PC上后,可以在设备管理器的mouse栏目识别到多了一个hid-mouse。 通过查看设备的PID和VID设置,即可判断识别设备枚举过程都成功。 如果将上报数据的x轴数据设置为1,并多次调用上报函数,即可观测到鼠标一直在屏幕上右移。
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\samples\usb\usb_hid_mouse\proj
源码路径:
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
中包含了如下流程:
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状态的说明,详见 常见问题。
usb_spd_cb_register()
:注册USB速度获取回调函数,可以获取到当前枚举到的USB速度是全速还是高速。usb_dm_core_init()
:设置USB速度以及需要开启的USB功能, 如本示例中想设置速度为高速,开启USB HID 功能,可以设置speed为 USB_HIGH_SPEED,hid_enable为 1。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描述符的配置,详见 常见问题。
usb_hid_init
:初始化USB hid class 相关的描述符以及回调函数。配置USB HID接口、端点相关的描述符以及报告描述。
接口描述符设置如下:
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, };
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, };
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), }, };
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 */ };
注册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);
初始化USB hid driver。
app_usb_start
: 初始化和使能USB,打开USB clock。实现控制鼠标发送数据,可以调用
app_cfu_usb_send_data
来上报数据给PC。app_cfu_usb_send_data
有三个参数,分别是USB report id,数据指针和数据长度。初次调用该函数需要先调用
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_PDN
:USB上电后的默认状态。USB_ATTACHED
:使能了USB,但没有打开USB clock。USB_POWERED
:使能并打开了USB clock。USB_ADDRESSED
:USB设备已经被分配地址。USB_CONFIGURED
:USB设备已被配置,进入这个状态,一般认为USB枚举成功了。USB_SUSPENDED
:USB进入suspend状态。
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
设置为 0x32 :设备需要从总线中获得100mA的最大电流,单位为2mA。