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

STM32F723 USB HID缓冲透传实现及头文件报错问题咨询

STM32F723 USB HID桥接设备实现方案

一、"HID already been declared"编译报错解决

报错原因是ST官方USB Host库的usbh_hid.h和USB Device库的usbd_hid.h存在大量重名的宏、枚举类型定义(比如HID类型枚举、HID子类宏等),直接在同一个编译单元(同一个.c文件)中包含两个头文件必然触发重定义错误。
最稳妥的解决方法是隔离两个USB栈的头文件作用域,不需要修改官方库源码:

  • 新建独立的桥接模块文件usbd_hid_bridge.c和usbd_hid_bridge.h
  • 仅在usbd_hid_bridge.c中包含usbd_hid.h,所有和USB Device HID相关的操作都封装成对外接口函数,在usbd_hid_bridge.h中只声明这些接口,不引入任何usbd_hid的类型定义
  • main.c等其他业务文件只需要包含usbd_hid_bridge.h,不需要直接引入usbd_hid.h,从根源上避免重名冲突

如果确实需要在同一个.c文件中引用两个头文件,可以在引入第二个头文件前#undef所有冲突的宏定义,这种方法容易漏项,不推荐:

#include "usbh_hid.h"
// 先取消Host侧HID头文件定义的重名宏
#undef HID
#undef HID_MOUSE
#undef HID_KEYBOARD
#undef HID_BOOT
// 再引入Device侧头文件
#include "usbd_hid.h"

二、HID缓冲设备功能实现

核心设计原则:USB Device侧的HID设备从上电完成枚举后始终保持连接状态,完全不随Host侧外接鼠标的拔插改变枚举状态,这样PC端永远识别到HID鼠标处于已连接状态,不会感知到外接鼠标的断开事件。

1. 描述符配置(优先选固定标准描述符方案)

如果你不需要兼容带自定义按键/功能的非标准鼠标,直接将USB Device侧的HID报告描述符配置为标准4字节Boot鼠标报告描述符即可,兼容市面上绝大多数普通鼠标,不需要动态读取外接鼠标的描述符,也不会触发PC端的设备重枚举,完全满足无缝缓冲的需求:
标准Boot鼠标报告格式:

  • Byte0:按键状态,bit0=左键、bit1=右键、bit2=中键,其余位填0
  • Byte1:X轴相对位移,int8_t类型
  • Byte2:Y轴相对位移,int8_t类型
  • Byte3:滚轮相对位移,int8_t类型
    直接替换CubeMX生成的usbd_hid.c里的HID_MOUSE_ReportDesc数组为标准鼠标报告描述符即可。

如果你确实需要适配自定义HID鼠标的特殊功能,可在Host侧第一次枚举到外接鼠标时,从Host HID栈的句柄中读取到外接鼠标的报告描述符,替换Device侧的HID报告描述符后触发一次Device端USB重枚举(拉低USB DP引脚10ms再释放),注意这一步仅在第一次识别到新型号鼠标时执行一次,后续外接鼠标拔插时绝对不能再次触发重枚举,否则PC端会识别到设备断开。

2. 代码实现步骤

  1. 桥接层接口封装(usbd_hid_bridge.h):
    #ifndef __USBD_HID_BRIDGE_H
    #define __USBD_HID_BRIDGE_H
    #include <stdint.h>
    
    // 设置外接鼠标连接状态
    void HID_Bridge_SetExtMouseState(uint8_t connected);
    // 发送鼠标报告给PC
    uint8_t HID_Bridge_SendMouseReport(int8_t x, int8_t y, int8_t wheel, uint8_t btn_state);
    // 发送空报告(鼠标断开时调用)
    uint8_t HID_Bridge_SendNullReport(void);
    
    #endif
    
  2. 桥接层实现(usbd_hid_bridge.c):
    #include "usbd_hid_bridge.h"
    #include "usbd_hid.h"
    #include "usb_device.h"
    #include <string.h>
    
    static uint8_t ext_mouse_connected = 0;
    // 标准鼠标报告缓冲区
    static uint8_t mouse_report[4] = {0};
    
    void HID_Bridge_SetExtMouseState(uint8_t connected)
    {
        ext_mouse_connected = connected;
        if (!connected) {
            // 鼠标断开时立刻发一次空报告,避免PC端停留在最后一次按键/位移状态
            HID_Bridge_SendNullReport();
        }
    }
    
    uint8_t HID_Bridge_SendMouseReport(int8_t x, int8_t y, int8_t wheel, uint8_t btn_state)
    {
        if (!ext_mouse_connected) return USBD_FAIL;
        mouse_report[0] = btn_state;
        mouse_report[1] = (uint8_t)x;
        mouse_report[2] = (uint8_t)y;
        mouse_report[3] = (uint8_t)wheel;
        return USBD_HID_SendReport(&hUsbDeviceFS, mouse_report, sizeof(mouse_report));
    }
    
    uint8_t HID_Bridge_SendNullReport(void)
    {
        memset(mouse_report, 0, sizeof(mouse_report));
        return USBD_HID_SendReport(&hUsbDeviceFS, mouse_report, sizeof(mouse_report));
    }
    
  3. 主逻辑和回调修改:
    • 在USB Host的用户回调USBH_UserProcess中监听设备连接/断开事件:
      // 找到HOST_USER_CLASS_ACTIVE和HOST_USER_DISCONNECTION事件分支
      case HOST_USER_CLASS_ACTIVE:
          if (USBH_HID_GetDeviceType(&hUsbHostFS) == HID_MOUSE) {
              HID_Bridge_SetExtMouseState(1);
          }
          break;
      case HOST_USER_DISCONNECTION:
          HID_Bridge_SetExtMouseState(0);
          break;
      
    • 修改现有的HID数据接收回调,不要直接透传整个HID_MOUSE_Info_TypeDef结构体(这个结构体内部有指针,长度和格式都不符合标准鼠标报告要求),提取有效字段后调用桥接接口发送:
      void USBH_HID_EventCallback(USBH_HandleTypeDef *phost)
      {
          if (USBH_HID_GetDeviceType(phost) == HID_MOUSE)
          {   
              HID_MOUSE_Info_TypeDef *Mouse_Info = USBH_HID_GetMouseInfo(phost);
              if (Mouse_Info == NULL) return;
      
              HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
              // 组装按键状态:bit0左键、bit1右键、bit2中键
              uint8_t btn_state = (Mouse_Info->buttons[0] ? 0x01 : 0) | 
                                  (Mouse_Info->buttons[1] ? 0x02 : 0) | 
                                  (Mouse_Info->buttons[2] ? 0x04 : 0);
              // 调用桥接接口发报告,ST库已经将x/y/wheel解析为int8_t相对位移,直接强转即可
              HID_Bridge_SendMouseReport((int8_t)Mouse_Info->x, (int8_t)Mouse_Info->y, 
                                        (int8_t)Mouse_Info->wheel, btn_state);
      
              // 原有串口打印逻辑,注意补全缺失的分号
              int len = sprintf(TxBuff, "X=%d ,Y=%d, Button1=%d, Button2=%d, Button3=%d\n", 
                              (int8_t)Mouse_Info->x, (int8_t)Mouse_Info->y, 
                              Mouse_Info->buttons[0], Mouse_Info->buttons[1], Mouse_Info->buttons[2]);
              HAL_UART_Transmit(&huart7, (uint8_t*)TxBuff, len, 1000);
          }
      }
      

3. 注意事项

  • 不要在任何业务逻辑中调用USBD_Stop、USBD_DeInit等会终止USB Device设备的函数,保证Device侧从初始化完成后始终处于配置完成状态
  • 外接鼠标断开后不需要做任何Device侧的状态修改,只需要停止透传真实鼠标数据,按需发送空报告即可,PC端只会认为鼠标没有输入操作,不会检测到设备断开
  • 现有代码中HAL_UART_Transmit行末尾缺失分号,需要补上避免编译报错

内容的提问来源于stack exchange,提问作者Ilia Hadzhiev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 21:54:20