You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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鼠标示例后,出现以下问题:
    1. lsusb始终无法识别USB设备
    2. 写入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 05:14:55