Rust开发STM32F103C8T6 HID鼠标:设备未识别且无法移动
STM32F103C8T6(Blue Pill)Rust HID鼠标设备未识别&无动作排查
问题背景
- 基于
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart模板创建项目,已配置memory.x文件 - stm32f1xx-hal的usb_serial示例可被主机正常识别且串口通信正常
- 修改非STM32F1平台的HID鼠标示例后,出现以下问题:
lsusb始终无法识别USB设备- 写入HID报告时鼠标无动作
用户提供的代码如下:
fn main() { // boilerplate copied from usb_serial stm32f1xx_hal example let dp = pac::Peripherals::take().unwrap(); let mut flash = dp.FLASH.constrain(); let rcc = dp.RCC.constrain(); let clocks = rcc .cfgr .use_hse(8.MHz()) .sysclk(48.MHz()) .pclk1(24.MHz()) .freeze(&mut flash.acr); assert!(clocks.usbclk_valid()); // Configure the on-board LED (PC13, green) let mut gpioc = dp.GPIOC.split(); let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); led.set_high(); // Turn off let mut gpioa = dp.GPIOA.split(); // BluePill board has a pull-up resistor on the D+ line. // Pull the D+ pin down to send a RESET condition to the USB bus. // This forced reset is needed only for development, without it host // will not reset your device when you upload new firmware. let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); usb_dp.set_low(); hprintln!("USB reset"); delay(clocks.sysclk().raw()); hprintln!("USB reset fisnished"); // and this code is based in the rest of the examples let usb = Peripheral { usb: dp.USB, pin_dm: gpioa.pa11.into_floating_input(&mut gpioa.crh), pin_dp: usb_dp.into_floating_input(&mut gpioa.crh), // pin_dp: usb_dp.into_floating_input(&mut gpioa.crh), }; let usb_bus = UsbBus::new(usb); // let mut serial = SerialPort::new(&usb_bus); let mut mouse = UsbHidClassBuilder::new() .add_device(WheelMouseConfig::default()) .build(&usb_bus); let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0x0001)) .manufacturer("TEST") .product("TEST") .serial_number("TEST") .supports_remote_wakeup(false) .device_class(0x03) // HID .build(); let mut report = WheelMouseReport::default(); delay(clocks.sysclk().raw()); hprintln!("USB device created"); loop { if !usb_dev.poll(&mut [&mut mouse]) { led.set_high(); // Turn off continue; } match mouse.device().write_report(&report) { Ok(_) => { led.set_low(); // Turn on hprintln!("Ok") } Err(err) => { hprintln!("Error: {:?}", err); } }; report = WheelMouseReport::default(); report.x += 110; report.y += -100; report.buttons = 0x01; } }
排查解决方案
1. 修正USB设备描述符配置
- 移除设备级HID类配置:删除
.device_class(0x03),HID类应通过接口描述符定义,设备级描述符保持默认的0x00即可。主机依赖接口级的类识别HID设备,设备级强制设置0x03会导致识别逻辑异常。 - 验证HID报告描述符:
WheelMouseConfig::default()生成的报告描述符可能存在兼容性问题,可手动替换为标准鼠标报告描述符(包含按键、X/Y轴、滚轮),确保符合USB HID 1.1规范。
2. 优化USB复位与引脚初始化
- 使用精确延迟实现USB复位:替换自定义的
delay函数为stm32f1xx-hal提供的Delay外设,确保D+引脚低电平持续至少10ms:// 需要先获取CorePeripherals let cp = cortex_m::Peripherals::take().unwrap(); let mut delay = Delay::new(cp.SYST, clocks); // 复位USB总线 let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); usb_dp.set_low(); hprintln!("USB reset"); delay.delay_ms(10u32); // 精确10ms延迟 hprintln!("USB reset finished"); - 确认引脚模式切换正确性:确保PA12从推挽输出切换为浮空输入时,HAL正确更新GPIO配置寄存器,避免引脚处于异常状态。
3. 调整报告写入逻辑
- 修正报告发送时机:当前逻辑先发送默认空报告,再修改报告,导致首次发送无效数据。调整为每次循环先构造有效报告再发送:
loop { if !usb_dev.poll(&mut [&mut mouse]) { led.set_high(); continue; } // 构造有效鼠标报告 let report = WheelMouseReport { buttons: 0x01, // 左键按下 x: 10, // X轴移动 y: -10, // Y轴移动 wheel: 0, ..Default::default() }; match mouse.device().write_report(&report) { Ok(_) => { led.set_low(); hprintln!("Report sent"); } Err(UsbError::WouldBlock) => { // 设备忙,无需处理,下次poll重试 } Err(err) => { hprintln!("Error: {:?}", err); } }; delay.delay_ms(50u32); // 添加延迟避免报告发送过于频繁 } - 处理
WouldBlock错误:usb_dev.poll()成功仅表示USB总线有活动,不代表HID类准备好接收报告,必须处理WouldBlock错误,避免误判为发送失败。
4. 确认依赖库版本兼容性
- 确保
stm32f1xx-hal、usbd-device、usbd-human-interface-device版本匹配:优先使用与stm32f1xx-hal usb_serial示例一致的usbd-device版本,避免API差异导致的兼容性问题。
5. 硬件排查
- 检查USB供电:部分Blue Pill板USB供电不稳定,尝试使用外部3.3V供电或更换高质量USB线缆。
- 验证引脚连接:检查PA11(DM)、PA12(DP)引脚是否虚焊,确保物理连接正常。
内容的提问来源于stack exchange,提问作者Yoyas
相关产品推荐
相关产品推荐

