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
相关产品推荐
相关产品推荐

