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

STM32F103C8 USB HID设备读取主机数据及写入Flash实现方法

STM32F103C8 USB HID 主机数据接收与Flash写入实现方案

1. 读取主机发送的大尺寸数据(如1024字节)

默认HID报告长度通常较小(当前代码为8字节),要支持1024字节数据接收,需修改以下核心配置:

修改HID报告描述符

在usbd_hid.c或usbd_hid_if.c中,将输入报告的长度调整为1024字节,示例报告描述符如下:

__ALIGN_BEGIN static uint8_t HID_ReportDesc_FS[USBD_HID_REPORT_DESC_SIZE] __ALIGN_END = {
  0x06, 0x00, 0xFF,                    // USAGE_PAGE (Vendor Defined)
  0x09, 0x01,                          // USAGE (Vendor Usage 1)
  0xA1, 0x01,                          // COLLECTION (Application)
  // 输入报告:主机→设备,1024字节
  0x09, 0x02,                          // USAGE (Vendor Usage 2)
  0x15, 0x00,                          // LOGICAL_MINIMUM (0)
  0x26, 0xFF, 0x03,                    // LOGICAL_MAXIMUM (1023)
  0x75, 0x08,                          // REPORT_SIZE (8)
  0x95, 0x00, 0x04,                    // REPORT_COUNT (1024)
  0x81, 0x02,                          // INPUT (Data,Var,Abs)
  // 输出报告:设备→主机,保持原8字节(可按需修改)
  0x09, 0x03,                          // USAGE (Vendor Usage 3)
  0x15, 0x00,                          // LOGICAL_MINIMUM (0)
  0x26, 0xFF, 0x00,                    // LOGICAL_MAXIMUM (255)
  0x75, 0x08,                          // REPORT_SIZE (8)
  0x95, 0x08,                          // REPORT_COUNT (8)
  0x91, 0x02,                          // OUTPUT (Data,Var,Abs)
  0xC0                                 // END_COLLECTION
};

修改HID宏定义

在usbd_hid.h中更新报告相关宏:

#define USBD_HID_REPORT_SIZE        1024U
#define USBD_HID_REPORT_DESC_SIZE   33U  // 对应上述报告描述符的实际长度

定义接收缓冲区

在usbd_hid_if.c中声明全局缓冲区存储接收数据:

uint8_t hid_rx_buffer[1024] = {0};

2. 实现接收数据回调触发

CubeIDE生成的USB HID框架中,USBD_HID_ReceiveReport_FS是主机数据接收完成的回调函数,直接在该函数内处理数据即可:

在usbd_hid_if.c中重写该函数:

int8_t USBD_HID_ReceiveReport_FS(uint8_t* report, uint16_t len)
{
  // 拷贝接收到的数据到自定义缓冲区
  memcpy(hid_rx_buffer, report, len);
  
  // 此处可添加自定义逻辑,比如标记数据接收完成、触发后续处理等
  
  // 通知USB栈准备接收下一包数据
  USBD_HID_SetRxBuffer(&hUsbDeviceFS, hid_rx_buffer);
  USBD_HID_ReceivePacket(&hUsbDeviceFS);
  
  return (USBD_OK);
}

同时,在main.c的初始化流程中开启USB接收:

// MX_USB_DEVICE_Init()之后添加
USBD_HID_SetRxBuffer(&hUsbDeviceFS, hid_rx_buffer);
USBD_HID_ReceivePacket(&hUsbDeviceFS);

3. 将接收的数据写入Flash

STM32F103C8的Flash容量为64KB,地址范围0x08000000~0x08010000,建议使用最后一页(起始地址0x0800FC00,每页1KB)存储数据,避免覆盖程序区。

实现Flash操作函数

在main.c中添加Flash解锁、擦除、写入、锁定的封装函数:

#include "stm32f1xx_hal_flash.h"

// Flash写入:addr为目标地址,data为数据指针,len为字节长度
HAL_StatusTypeDef Flash_Write(uint32_t addr, uint8_t* data, uint32_t len)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  // 解锁Flash
  HAL_FLASH_Unlock();
  
  // 擦除目标页
  FLASH_EraseInitTypeDef erase_init;
  erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
  erase_init.PageAddress = addr & 0xFFFFF000;  // 对齐到页起始地址
  erase_init.NbPages = 1;
  uint32_t page_error = 0;
  status = HAL_FLASHEx_Erase(&erase_init, &page_error);
  if(status != HAL_OK) goto flash_exit;
  
  // 按半字(2字节)写入Flash(F1仅支持半字/字编程)
  uint32_t i;
  for(i=0; i<len; i+=2)
  {
    uint16_t half_word = data[i];
    if(i+1 < len) half_word |= (data[i+1] << 8);
    status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr+i, half_word);
    if(status != HAL_OK) goto flash_exit;
  }
  
flash_exit:
  // 锁定Flash
  HAL_FLASH_Lock();
  return status;
}

// Flash读取函数
void Flash_Read(uint32_t addr, uint8_t* data, uint32_t len)
{
  memcpy(data, (uint8_t*)addr, len);
}

在回调中触发Flash写入

修改USBD_HID_ReceiveReport_FS,在数据接收完成后调用写入函数:

int8_t USBD_HID_ReceiveReport_FS(uint8_t* report, uint16_t len)
{
  memcpy(hid_rx_buffer, report, len);
  
  // 将数据写入Flash最后一页
  if(len > 0)
  {
    Flash_Write(0x0800FC00, hid_rx_buffer, len);
  }
  
  USBD_HID_SetRxBuffer(&hUsbDeviceFS, hid_rx_buffer);
  USBD_HID_ReceivePacket(&hUsbDeviceFS);
  
  return (USBD_OK);
}

更新后的main.c代码

#include "main.h"
#include "usb_device.h"
#include "usbd_hid.h"
#include "stm32f1xx_hal_flash.h"

extern USBD_HandleTypeDef hUsbDeviceFS;
extern uint8_t hid_rx_buffer[1024];

static uint8_t hid_report[0x08] = {0x00};

// Flash函数声明
HAL_StatusTypeDef Flash_Write(uint32_t addr, uint8_t* data, uint32_t len);
void Flash_Read(uint32_t addr, uint8_t* data, uint32_t len);

int main(){
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USB_DEVICE_Init();

    // 初始化USB接收
    USBD_HID_SetRxBuffer(&hUsbDeviceFS, hid_rx_buffer);
    USBD_HID_ReceivePacket(&hUsbDeviceFS);

    while(1){
        hid_report[0x02] = 0x04;
        USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&hid_report, sizeof(hid_report));
        HAL_Delay(50);

        hid_report[0x02] = 0x00;
        USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&hid_report, sizeof(hid_report));
        HAL_Delay(1000);
    }
}

// Flash写入实现
HAL_StatusTypeDef Flash_Write(uint32_t addr, uint8_t* data, uint32_t len)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  HAL_FLASH_Unlock();
  
  FLASH_EraseInitTypeDef erase_init;
  erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
  erase_init.PageAddress = addr & 0xFFFFF000;
  erase_init.NbPages = 1;
  uint32_t page_error = 0;
  status = HAL_FLASHEx_Erase(&erase_init, &page_error);
  if(status != HAL_OK) goto flash_exit;
  
  uint32_t i;
  for(i=0; i<len; i+=2)
  {
    uint16_t half_word = data[i];
    if(i+1 < len) half_word |= (data[i+1] << 8);
    status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr+i, half_word);
    if(status != HAL_OK) goto flash_exit;
  }
  
flash_exit:
  HAL_FLASH_Lock();
  return status;
}

// Flash读取实现
void Flash_Read(uint32_t addr, uint8_t* data, uint32_t len)
{
  memcpy(data, (uint8_t*)addr, len);
}

内容的提问来源于stack exchange,提问作者étale-cohomology

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:49:54