suoerkang 2026-01-14 19:12 采纳率: 0%
浏览 3

单片机通过USB模拟USP沟通主机

使用设备:正点原子 STM32 F407 IGT6 开发板,现在已通过 CubeMX 配置 PA11、PA12 对应的 USB 接口为USB 设备模式(模拟 UPS 电池设备)。
目前现象:台式机设备管理器可以识别出 USB HID UPS电池、控制面板内出现有电源选项有菜单、但是任务栏出电池图标灰色、电量显示 100% 却配个“空电池”视觉图,上传报文电量无变化。
现状
通信层(底层): 正常。USBlyzer工具抓到的 01 02 02 27 00 64 00 64 00 0A 05 说明 STM32 已经按照指令发出了数据。
驱动层(中层): 正常。Windows 已加载 hidups.sys,在电源选项里可以看到“关键电池电量操作”。
应用层(视觉层): 异常。
灰色图标:表示 Windows 认为当前处于“放电状态”(Discharging),但因为它不确定电池的“健康度”或“满充参考值”,所以不敢点亮图标。

我的代码
USP HID

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
    0x05, 0x84,                      // Usage Page (Power Device)
    0x09, 0x04,                      // Usage (UPS)
    0xa1, 0x01,                      // Collection (Application)

    0x85, 0x01,                      // Report ID (1)

    // --- 1. 电源状态  ---
    0x05, 0x84,                      // Usage Page (Power Device)
    0x09, 0x3d,                      // Usage (AC Present) - Bit 0
    0x09, 0x45,                      // Usage (Discharging) - Bit 1
    0x15, 0x00, 
    0x25, 0x01, 
    0x75, 0x01, 
    0x95, 0x02, 
    0x81, 0x02,                      // Input (Data,Var,Abs)
    0x95, 0x06, 
    0x81, 0x03,                      // Padding (6 bits)

    // --- 2. 相对电量百分比  ---
    0x05, 0x85,                      // Usage Page (Battery System)
    0x09, 0x6d,                      // Usage (Relative State of Charge)
    0x15, 0x00, 
    0x25, 0x64,                      // Logical Max: 100
    0x75, 0x08, 
    0x95, 0x01, 
    0x81, 0x02,                      // Input (Data,Var,Abs)
    /* USER CODE END 0 */
    0xC0                             // End Collection
};

Main.c

main(){
  初始化();
  while1){
    sync_report[0] = 0x01; // Report ID
    sync_report[2] = 0x02; //百分比模式
    USB_Send_Retry(sync_report, 2)
  }
}

uint8_t USB_Send_Retry(uint8_t *report, uint16_t len) {
    USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;
    
    for (uint8_t retry = 0; retry < 10; retry++) {
        // 关键点:不仅看返回值,还要看 HID 状态机是否为空闲
        if (hhid->state == CUSTOM_HID_IDLE) {
            if (USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, len) == USBD_OK) {
                return 0;
            }
        }
        HAL_Delay(5); 
    }
    return 1;
}

请教一下大家,我使用USBlyzer抓到怎么样的数据是正确。
我尝试过的报文如下:
01 64 (ID 01) 和 02 00 01 (ID 02)。分开报
01 02 4D。合并报
01 02 02 27 00 64 00。使用16位精度
01 02 02 47 00 64 00 64 00 0A 05。网上找的全功能UPS报文
使用这些报文都无法实现Windows系统下电池电量的修改,依旧是100%,就像是系统下没有处理这些报文。

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-01-14 19:13
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    根据你的描述,你已经通过 CubeMX 配置了 STM32 F407 IGT6 开发板的 USB 接口为 USB 设备模式(模拟 UPS 电池设备),并且在 Windows 系统中识别到了 HID UPS 电池设备,但存在以下问题:

    • 任务栏电池图标灰色
    • 电量显示 100% 却配“空电池”视觉图
    • 上传报文电量无变化

    这些问题表明:底层通信正常,驱动层也加载成功,但应用层(Windows 视觉界面)未正确解析或更新电量数据。


    🔍 一、问题分析

    1. Windows 对 HID UPS 的处理机制

    Windows 会通过 HID UPS 类型设备 获取电池状态信息,并根据上报的报告内容进行显示。

    • 关键字段包括

      • AC Present(Bit 0)
      • Discharging(Bit 1)
      • Relative State of Charge(相对电量百分比)
    • 如果这些字段没有正确设置,Windows 就无法识别实际电量状态,导致图标灰显、电量不变。

    2. 你的 HID 报告描述符分析

    // HID Report Descriptor
    __ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
    {
        0x05, 0x84,                      // Usage Page (Power Device)
        0x09, 0x04,                      // Usage (UPS)
        0xa1, 0x01,                      // Collection (Application)
    
        0x85, 0x01,                      // Report ID (1)
    
        // --- 1. 电源状态  ---
        0x05, 0x84,                      // Usage Page (Power Device)
        0x09, 0x3d,                      // Usage (AC Present) - Bit 0
        0x09, 0x45,                      // Usage (Discharging) - Bit 1
        0x15, 0x00, 
        0x25, 0x01, 
        0x75, 0x01, 
        0x95, 0x02, 
        0x81, 0x02,                      // Input (Data,Var,Abs)
        0x95, 0x06, 
        0x81, 0x03,                      // Padding (6 bits)
    
        // --- 2. 相对电量百分比  ---
        0x05, 0x85,                      // Usage Page (Battery System)
        0x09, 0x6d,                      // Usage (Relative State of Charge)
        0x15, 0x00, 
        0x25, 0x64,                      // Logical Max: 100
        0x75, 0x08, 
        0x95, 0x01, 
        0x81, 0x02,                      // Input (Data,Var,Abs)
        /* USER CODE END 0 */
        0xC0                             // End Collection
    };
    
    • 报告结构如下:
      • Report ID = 1
      • 第1字节:电源状态(AC Present + Discharging)
      • 第2字节:相对电量百分比(0~100)

    ✅ 二、正确的 USB 抓包应包含哪些内容?

    使用 USBlyzer 工具抓取的数据应满足以下条件:

    1. 报告 ID 必须是 0x01

    • 每个报告必须带有 Report ID = 0x01,否则 Windows 无法识别该报告。

    2. 电源状态字段(Bit 0 和 Bit 1)

    • Bit 0: AC Present(是否接通市电)
    • Bit 1: Discharging(是否放电)

    3. 相对电量百分比(0~100)

    • 例如:0x64 表示 100%,0x0A 表示 10%

    ✅ 正确的报文格式示例:

    01 02 64   → Report ID=0x01, Power Status=0x02 (Discharging), Relative State of Charge=0x64 (100%)
    

    注意: 你之前发送的 01 02 02 27 00 64 00 64 00 0A 05 是错误的格式,没有正确构造 HID 报告结构


    🛠 三、修改后的代码建议

    ✅ 1. 构造正确的报告数据

    uint8_t sync_report[2];  // Report ID + Power Status + Relative Charge
    sync_report[0] = 0x01;   // Report ID
    sync_report[1] = 0x02;   // Power Status: 0x02 (Discharging)
    sync_report[2] = 0x64;   // Relative State of Charge: 100%
    

    注意:上面代码有误,因为 sync_report[2] 超出了数组长度!请修正为:

    uint8_t sync_report[3];  // Report ID + Power Status + Relative Charge
    sync_report[0] = 0x01;   // Report ID
    sync_report[1] = 0x02;   // Power Status: 0x02 (Discharging)
    sync_report[2] = 0x64;   // Relative State of Charge: 100%
    

    ✅ 2. 修改 USB_Send_Retry 函数

    确保每次发送的是完整报告,且长度为 3 字节:

    uint8_t USB_Send_Retry(uint8_t *report, uint16_t len) {
        USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;
        
        for (uint8_t retry = 0; retry < 10; retry++) {
            if (hhid->state == CUSTOM_HID_IDLE) {
                if (USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, len) == USBD_OK) {
                    return 0;
                }
            }
            HAL_Delay(5); 
        }
        return 1;
    }
    

    ✅ 3. 主函数调用方式

    void main() {
        Init();  // 初始化
        while (1) {
            uint8_t sync_report[3];
            sync_report[0] = 0x01;   // Report ID
            sync_report[1] = 0x02;   // Discharging
            sync_report[2] = 0x64;   // 100%
            
            USB_Send_Retry(sync_report, 3);
            HAL_Delay(1000);  // 每秒发送一次
        }
    }
    

    🧠 四、常见错误及解决方法

    | 问题 | 原因 | 解决方案 | |------|------|----------| | 电池图标灰色 | 电源状态未正确设置(如未放电) | 设置 Power Status = 0x02(Discharging) | | 电量显示 100% | 未正确设置 Relative State of Charge | 使用 0x64 表示 100% | | 报文未被识别 | 报告格式错误(缺少 Report ID 或字段错位) | 确保报文格式为:01 02 64 |


    ✅ 五、总结

    | 项目 | 内容 | |------|------| | 正确报文格式 | 01 02 64(Report ID=1, Discharging, 100%) | | 关键字段 | Power Status (bit 0-1), Relative State of Charge | | 推荐频率 | 每秒发送一次 | | 调试工具 | USBlyzer(确认报文格式和内容) |


    🔚 最终建议

    • 严格按照 HID 报告格式发送数据,确保每个字段对应正确。
    • 测试不同电量值(如 0x0A 表示 10%),观察 Windows 是否能动态更新。
    • 参考标准 HID UPS 报文格式,确保兼容性。

    如有需要,我可以提供完整的 HID 报告模板和示例代码。

    评论

报告相同问题?

问题事件

  • 创建了问题 1月14日