1. USB HID 鼠标基础描述符实现
任务说明
本案例实现一个基于STM32F103系列MCU的USB HID键盘设备。设备上电后完成USB枚举,被主机识别为标准键盘;按下开发板上的用户按键时,设备向主机发送一次按键事件(例如字母'A'的按下与释放),主机端可观察到字符输入。核心交付物为完整的USB设备初始化代码、HID报告描述符、按键扫描与上报逻辑。
硬件连接与依赖
- MCU:STM32F103C8T6(Blue Pill开发板),外部8MHz晶振,系统时钟72MHz
- USB:PA11(USB_DM)、PA12(USB_DP),板载1.5kΩ上拉电阻至PA12
- 用户按键:PC13(低电平有效,板载按键)
- 开发环境:STM32CubeIDE 1.13+,STM32CubeF1固件包
- 中间件:USB Device库(使用CubeMX生成的PCD驱动)
实现方案
使用STM32CubeMX生成USB Device工程,选择Communication Device Class (CDC)模板后手动修改描述符与端点配置为HID键盘类。核心工作包括:
- 配置USB为Device模式,低速/全速选择全速(12Mbps)
- 编写HID键盘报告描述符(8字节:修饰键+保留+6个按键)
- 实现HID类请求回调(GetReport、SetReport等)
- 主循环轮询按键,检测到按下时通过USB发送按键事件
代码实现
以下为关键代码,完整工程由CubeMX生成,此处给出核心修改与新增部分。
// usbd_hid_keyboard.h —— HID键盘报告描述符
#define HID_KEYBOARD_REPORT_DESC_SIZE 63
static const uint8_t HID_KeyboardReportDesc[HID_KEYBOARD_REPORT_DESC_SIZE] = {
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (224)
0x29, 0xE7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Constant)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x91, 0x02, // Output (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output (Constant)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array)
0xC0 // End Collection
};
// usbd_hid_keyboard.c —— 按键发送函数
#include "usbd_hid_keyboard.h"
// 发送按键事件:按下并释放一个键
// keycode: HID键盘键码,如 'a'对应0x04,'A'对应0x04+左Shift修饰
void HID_KBD_SendKey(uint8_t keycode, uint8_t modifier) {
uint8_t report[8] = {0};
// 按下事件
report[0] = modifier; // 修饰键(如左Shift)
report[2] = keycode; // 第一个按键
USBD_HID_SendReport(&hUsbDeviceFS, report, 8);
// 释放事件(延迟约10ms后发送空报告)
HAL_Delay(10);
memset(report, 0, sizeof(report));
USBD_HID_SendReport(&hUsbDeviceFS, report, 8);
}
// 主循环中调用示例(main.c中)
void HID_KBD_Process(void) {
static uint8_t last_state = 1; // PC13上拉,按下为0
uint8_t current_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);
if (current_state == 0 && last_state == 1) {
// 检测到按下沿,发送字母 'A'(无Shift时为小写a)
HID_KBD_SendKey(0x04, 0x00); // 0x04 = 键盘a
}
last_state = current_state;
}
USB配置要点
在CubeMX中需完成以下配置:
- USB_OTG_FS:激活为Device模式
- 中间件选择USB Device,Class选择Communication Device Class(生成框架后手动改)
- 修改usbd_conf.h中端点参数:HID使用端点1 IN,中断传输,最大包长64字节
- 在usbd_hid.c中注册HID类回调,实现GetHIDDescriptor、GetReportDesc等函数
验证方法
枚举验证:将开发板通过USB连接PC,在设备管理器中应出现“USB输入设备”或“HID键盘设备”,且无黄色感叹号。Linux下执行lsusb可见设备,dmesg输出包含“input: USB HID v1.10 Keyboard”信息。
功能验证:打开任意文本编辑器,按下开发板PC13按键,应输入字符“a”。若需输出大写“A”,修改调用为HID_KBD_SendKey(0x04, 0x02)(0x02为左Shift修饰键)。
判定标准:
- 设备枚举成功,主机识别为键盘设备
- 每次按下按键,文本编辑器恰好输入一个字符
- 连续快速按键不丢失(间隔大于50ms时)
常见问题排查
- 枚举失败:检查PA12上拉电阻是否连接,USB_DM/DP线序是否正确
- 无字符输出:确认报告描述符长度与usbd_hid.c中定义一致(63字节)
- 按键无响应:用万用表测量PC13按下时电平变化,确认GPIO配置为输入上拉