STM32F4 USB Host实现方案,包括U盘读写、鼠标、键盘等设备的支持。这里重点实现MSC(大容量存储设备)和HID类设备的驱动。
一、系统架构
1.1 硬件配置
STM32F407ZGT6 (168MHz, 192KB RAM, 1MB Flash)
├── USB_OTG_HS (高速USB) - 外接USB3300 PHY
│ ├── DM → PB14
│ ├── DP → PB15
│ ├── ID → PB12
│ └── VBUS → PB13
├── USB_OTG_FS (全速USB) - 内部PHY
│ ├── DM → PA11
│ └── DP → PA12
├── SD卡接口 (可选)
├── LED指示灯
└── 串口调试
1.2 软件架构
USB_HOST_Project/
├── Core/
│ ├── main.c
│ └── ...
├── Drivers/
│ ├── STM32F4xx_HAL_Driver/
│ └── BSP/
├── Middlewares/
│ ├── ST/STM32_USB_Host_Library/
│ │ ├── Class/
│ │ │ ├── MSC/
│ │ │ ├── HID/
│ │ │ └── ...
│ │ └── Core/
│ └── FATFS/
├── USB_HOST/
│ ├── App/
│ └── Target/
└── Applications/
├── usb_msc_app.c/.h
├── usb_hid_app.c/.h
└── usb_test.c/.h
二、USB Host配置
2.1 USB Host配置头文件
c
/**
* @file usb_host_config.h
* @brief USB Host配置
*/
#ifndef __USB_HOST_CONFIG_H
#define __USB_HOST_CONFIG_H
#include "stm32f4xx_hal.h"
/* USB配置选项 */
#define USB_HOST_DEBUG 1 // 启用调试输出
#define USB_HOST_MAX_DEVICES 5 // 最大设备数
#define USB_HOST_MAX_PIPES 10 // 最大管道数
#define USB_HOST_MAX_INTERFACES 8 // 最大接口数
#define USB_HOST_MAX_ENDPOINTS 8 // 最大端点数
#define USB_HOST_MSC_MAX_LUN 2 // MSC设备最大逻辑单元数
#define USB_HOST_HID_MAX_INSTANCES 3 // HID设备最大实例数
/* 设备类支持 */
#define USB_HOST_SUPPORT_MSC 1 // 支持MSC类
#define USB_HOST_SUPPORT_HID 1 // 支持HID类
#define USB_HOST_SUPPORT_CDC 0 // 支持CDC类
#define USB_HOST_SUPPORT_AUDIO 0 // 支持音频类
/* 超时设置 */
#define USB_HOST_CONNECT_TIMEOUT 5000 // 连接超时(ms)
#define USB_HOST_ENUM_TIMEOUT 10000 // 枚举超时(ms)
#define USB_HOST_CMD_TIMEOUT 10000 // 命令超时(ms)
#define USB_HOST_TRANSFER_TIMEOUT 5000 // 传输超时(ms)
/* MSC配置 */
#define MSC_MEDIA_PACKET 512 // 数据包大小
#define MSC_MAX_SUPPORTED_LUN 2 // 支持的最大LUN
#define MSC_MAX_BULK_STALL 3 // 最大BULK STALL重试次数
/* HID配置 */
#define HID_MAX_REPORT_SIZE 64 // 最大报告大小
#define HID_POLLING_INTERVAL 10 // 轮询间隔(ms)
/* USB速度 */
typedef enum {
USB_SPEED_LOW = 0,
USB_SPEED_FULL,
USB_SPEED_HIGH
} USB_SpeedType;
/* USB设备状态 */
typedef enum {
USB_DEVICE_DISCONNECTED = 0,
USB_DEVICE_CONNECTED,
USB_DEVICE_ENUMERATED,
USB_DEVICE_READY,
USB_DEVICE_ERROR
} USB_DeviceState;
/* USB设备信息 */
typedef struct {
uint8_t dev_addr; // 设备地址
USB_SpeedType speed; // 设备速度
uint8_t class; // 设备类
uint8_t subclass; // 设备子类
uint8_t protocol; // 协议
uint16_t vid; // 厂商ID
uint16_t pid; // 产品ID
uint8_t ep0_size; // 端点0大小
char manufacturer[32]; // 厂商字符串
char product[32]; // 产品字符串
char serial[32]; // 序列号
} USB_DeviceInfo;
/* 函数声明 */
void USBH_Init(void);
void USBH_Process(void);
uint8_t USBH_IsDeviceConnected(void);
USB_DeviceState USBH_GetDeviceState(void);
USB_DeviceInfo* USBH_GetDeviceInfo(void);
void USBH_PrintDeviceInfo(USB_DeviceInfo *info);
#endif /* __USB_HOST_CONFIG_H */
2.2 USB Host主程序
c
/**
* @file usb_host_main.c
* @brief USB Host主程序
*/
#include "usb_host_config.h"
#include "usb_msc_app.h"
#include "usb_hid_app.h"
#include <stdio.h>
#include <string.h>
/* 全局变量 */
USBH_HandleTypeDef hUsbHostHS; // USB Host句柄
USB_DeviceInfo usb_device_info;
volatile USB_DeviceState usb_device_state = USB_DEVICE_DISCONNECTED;
volatile uint8_t usb_device_class = 0;
uint8_t usb_initialized = 0;
/* 应用句柄 */
#if USB_HOST_SUPPORT_MSC
MSC_Application_TypeDef msc_app = {0};
#endif
#if USB_HOST_SUPPORT_HID
HID_Application_TypeDef hid_app = {0};
#endif
/**
* @brief USB Host初始化
*/
void USBH_Init(void)
{
/* 初始化USB Host库 */
USBH_InitTypeDef init = {0};
/* 设置USB Host回调 */
init.pUser = NULL;
init.pClass = NULL;
init.pData = NULL;
/* 初始化Host库 */
if(USBH_Init(&hUsbHostHS, &init, 0) != USBH_OK)
{
printf("USB Host library init failed!\n");
return;
}
/* 注册设备类驱动 */
#if USB_HOST_SUPPORT_MSC
if(USBH_RegisterClass(&hUsbHostHS, USBH_MSC_CLASS) != USBH_OK)
{
printf("Failed to register MSC class\n");
}
else
{
printf("MSC class registered\n");
}
#endif
#if USB_HOST_SUPPORT_HID
if(USBH_RegisterClass(&hUsbHostHS, USBH_HID_CLASS) != USBH_OK)
{
printf("Failed to register HID class\n");
}
else
{
printf("HID class registered\n");
}
#endif
/* 启动Host进程 */
if(USBH_Start(&hUsbHostHS) != USBH_OK)
{
printf("Failed to start USB Host\n");
return;
}
usb_initialized = 1;
printf("USB Host initialized successfully\n");
}
/**
* @brief USB Host处理循环
*/
void USBH_Process(void)
{
if(!usb_initialized) return;
/* USB Host后台处理 */
USBH_Process(&hUsbHostHS);
/* 更新设备状态 */
USBH_HandleTypeDef *phost = &hUsbHostHS;
switch(phost->gState)
{
case HOST_IDLE:
case HOST_DEV_WAIT_FOR_ATTACHMENT:
usb_device_state = USB_DEVICE_DISCONNECTED;
break;
case HOST_DEV_ATTACHED:
usb_device_state = USB_DEVICE_CONNECTED;
break;
case HOST_DEV_ENUMERATED:
usb_device_state = USB_DEVICE_ENUMERATED;
/* 获取设备信息 */
if(phost->device.Data != NULL)
{
usb_device_class = phost->device.CfgDesc.Itf_Desc[0].bInterfaceClass;
USBH_UpdateDeviceInfo(phost, &usb_device_info);
}
break;
case HOST_CLASS:
usb_device_state = USB_DEVICE_READY;
break;
case HOST_ABORT_STATE:
case HOST_ERROR:
usb_device_state = USB_DEVICE_ERROR;
break;
}
/* 根据设备类调用相应的应用处理 */
switch(usb_device_class)
{
#if USB_HOST_SUPPORT_MSC
case USB_CLASS_MASS_STORAGE:
MSC_App_Process(&msc_app, &hUsbHostHS);
break;
#endif
#if USB_HOST_SUPPORT_HID
case USB_HID_CLASS:
HID_App_Process(&hid_app, &hUsbHostHS);
break;
#endif
default:
/* 不支持的设备类 */
break;
}
}
/**
* @brief 更新设备信息
*/
void USBH_UpdateDeviceInfo(USBH_HandleTypeDef *phost, USB_DeviceInfo *info)
{
if(phost == NULL || info == NULL) return;
USBH_DevDescTypeDef *dev_desc = &phost->device.DevDesc;
info->dev_addr = phost->device.address;
info->speed = (dev_desc->bMaxPacketSize0 == 64) ? USB_SPEED_HIGH : USB_SPEED_FULL;
info->class = dev_desc->bDeviceClass;
info->subclass = dev_desc->bDeviceSubClass;
info->protocol = dev_desc->bDeviceProtocol;
info->vid = dev_desc->idVendor;
info->pid = dev_desc->idProduct;
info->ep0_size = dev_desc->bMaxPacketSize0;
/* 获取字符串描述符 */
if(dev_desc->iManufacturer != 0)
{
USBH_Get_StringDesc(phost, dev_desc->iManufacturer,
(uint8_t*)info->manufacturer, sizeof(info->manufacturer));
}
if(dev_desc->iProduct != 0)
{
USBH_Get_StringDesc(phost, dev_desc->iProduct,
(uint8_t*)info->product, sizeof(info->product));
}
if(dev_desc->iSerialNumber != 0)
{
USBH_Get_StringDesc(phost, dev_desc->iSerialNumber,
(uint8_t*)info->serial, sizeof(info->serial));
}
}
/**
* @brief 打印设备信息
*/
void USBH_PrintDeviceInfo(USB_DeviceInfo *info)
{
if(info == NULL) return;
printf("\n=== USB Device Info ===\n");
printf("Address: %d\n", info->dev_addr);
printf("Speed: %s\n", info->speed == USB_SPEED_HIGH ? "High" :
info->speed == USB_SPEED_FULL ? "Full" : "Low");
printf("Class: 0x%02X\n", info->class);
printf("Vendor ID: 0x%04X\n", info->vid);
printf("Product ID: 0x%04X\n", info->pid);
if(info->manufacturer[0] != 0)
printf("Manufacturer: %s\n", info->manufacturer);
if(info->product[0] != 0)
printf("Product: %s\n", info->product);
if(info->serial[0] != 0)
printf("Serial: %s\n", info->serial);
printf("======================\n");
}
/**
* @brief 检查设备是否连接
*/
uint8_t USBH_IsDeviceConnected(void)
{
return (usb_device_state != USB_DEVICE_DISCONNECTED);
}
/**
* @brief 获取设备状态
*/
USB_DeviceState USBH_GetDeviceState(void)
{
return usb_device_state;
}
/**
* @brief 获取设备信息
*/
USB_DeviceInfo* USBH_GetDeviceInfo(void)
{
return &usb_device_info;
}
三、MSC (U盘) 应用实现
3.1 MSC应用头文件
c
/**
* @file usb_msc_app.h
* @brief USB MSC应用实现
*/
#ifndef __USB_MSC_APP_H
#define __USB_MSC_APP_H
#include "usb_host_config.h"
#include "usbh_msc.h"
#include "ff.h"
#include "diskio.h"
/* MSC应用状态 */
typedef enum {
MSC_APP_IDLE = 0,
MSC_APP_START,
MSC_APP_DISK_CONNECT,
MSC_APP_DISK_DISCONNECT,
MSC_APP_DISK_OPERATION,
MSC_APP_DISK_ERROR,
MSC_APP_UNRECOVERED_ERROR
} MSC_App_State;
/* MSC应用结构体 */
typedef struct {
MSC_App_State state;
uint8_t lun; // 逻辑单元号
uint8_t is_initialized; // 初始化标志
uint8_t is_ready; // 就绪标志
uint8_t capacity; // 容量
uint8_t write_protect; // 写保护
uint32_t block_size; // 块大小
uint32_t block_count; // 块数量
FATFS fs; // FatFS文件系统
char path[4]; // 路径 (e.g., "0:/")
char current_dir[256]; // 当前目录
} MSC_Application_TypeDef;
/* 函数声明 */
void MSC_App_Init(MSC_Application_TypeDef *app);
void MSC_App_DeInit(MSC_Application_TypeDef *app);
void MSC_App_Process(MSC_Application_TypeDef *app, USBH_HandleTypeDef *phost);
uint8_t MSC_App_ConnectDrive(MSC_Application_TypeDef *app);
uint8_t MSC_App_DisconnectDrive(MSC_Application_TypeDef *app);
uint8_t MSC_App_DiskInfo(MSC_Application_TypeDef *app);
uint8_t MSC_App_ReadSectors(MSC_Application_TypeDef *app,
uint8_t *buff,
uint32_t sector,
uint32_t count);
uint8_t MSC_App_WriteSectors(MSC_Application_TypeDef *app,
uint8_t *buff,
uint32_t sector,
uint32_t count);
uint8_t MSC_App_FileList(MSC_Application_TypeDef *app, const char *path);
uint8_t MSC_App_ReadFile(MSC_Application_TypeDef *app,
const char *filename,
uint8_t *buffer,
uint32_t max_len);
uint8_t MSC_App_WriteFile(MSC_Application_TypeDef *app,
const char *filename,
uint8_t *data,
uint32_t len);
uint8_t MSC_App_GetFreeSpace(MSC_Application_TypeDef *app,
uint32_t *total_sectors,
uint32_t *free_sectors);
void MSC_App_PrintStatus(MSC_Application_TypeDef *app);
#endif /* __USB_MSC_APP_H */
3.2 MSC应用实现
c
/**
* @file usb_msc_app.c
* @brief USB MSC应用实现
*/
#include "usb_msc_app.h"
#include <stdio.h>
#include <string.h>
/* 全局文件系统变量 */
static FATFS *fs_ptr = NULL;
static FIL file;
static DIR dir;
static FILINFO fno;
/**
* @brief MSC应用初始化
*/
void MSC_App_Init(MSC_Application_TypeDef *app)
{
if(app == NULL) return;
memset(app, 0, sizeof(MSC_Application_TypeDef));
app->state = MSC_APP_IDLE;
app->lun = 0;
app->is_initialized = 0;
app->is_ready = 0;
strcpy(app->path, "0:/");
strcpy(app->current_dir, "/");
printf("MSC Application initialized\n");
}
/**
* @brief MSC应用反初始化
*/
void MSC_App_DeInit(MSC_Application_TypeDef *app)
{
if(app == NULL) return;
if(app->is_initialized)
{
f_mount(NULL, app->path, 0); // 卸载文件系统
}
memset(app, 0, sizeof(MSC_Application_TypeDef));
printf("MSC Application deinitialized\n");
}
/**
* @brief MSC应用处理
*/
void MSC_App_Process(MSC_Application_TypeDef *app, USBH_HandleTypeDef *phost)
{
if(app == NULL || phost == NULL) return;
switch(app->state)
{
case MSC_APP_IDLE:
/* 等待设备连接 */
if(USBH_MSC_IsReady(phost))
{
app->state = MSC_APP_START;
printf("MSC device is ready\n");
}
break;
case MSC_APP_START:
/* 连接驱动器 */
if(MSC_App_ConnectDrive(app) == 0)
{
app->state = MSC_APP_DISK_CONNECT;
}
else
{
app->state = MSC_APP_DISK_ERROR;
}
break;
case MSC_APP_DISK_CONNECT:
/* 获取磁盘信息 */
if(MSC_App_DiskInfo(app) == 0)
{
app->state = MSC_APP_DISK_OPERATION;
app->is_ready = 1;
printf("MSC disk operation ready\n");
/* 列出根目录文件 */
MSC_App_FileList(app, "/");
}
else
{
app->state = MSC_APP_DISK_ERROR;
}
break;
case MSC_APP_DISK_OPERATION:
/* 磁盘操作状态,可以执行读写操作 */
break;
case MSC_APP_DISK_DISCONNECT:
/* 磁盘断开 */
MSC_App_DisconnectDrive(app);
app->state = MSC_APP_IDLE;
break;
case MSC_APP_DISK_ERROR:
printf("MSC disk error\n");
app->state = MSC_APP_IDLE;
break;
}
}
/**
* @brief 连接驱动器
*/
uint8_t MSC_App_ConnectDrive(MSC_Application_TypeDef *app)
{
FRESULT res;
/* 挂载文件系统 */
res = f_mount(&app->fs, app->path, 0);
if(res != FR_OK)
{
printf("Failed to mount file system: %d\n", res);
app->is_initialized = 0;
return 1;
}
app->is_initialized = 1;
printf("File system mounted successfully\n");
return 0;
}
/**
* @brief 断开驱动器
*/
uint8_t MSC_App_DisconnectDrive(MSC_Application_TypeDef *app)
{
if(app->is_initialized)
{
f_mount(NULL, app->path, 0);
app->is_initialized = 0;
app->is_ready = 0;
printf("File system unmounted\n");
}
return 0;
}
/**
* @brief 获取磁盘信息
*/
uint8_t MSC_App_DiskInfo(MSC_Application_TypeDef *app)
{
DWORD free_clusters, total_sectors, free_sectors;
FATFS *fs = &app->fs;
/* 获取可用空间 */
FRESULT res = f_getfree(app->path, &free_clusters, &fs);
if(res != FR_OK)
{
printf("Failed to get free space: %d\n", res);
return 1;
}
/* 计算总扇区数和可用扇区数 */
total_sectors = (fs->n_fatent - 2) * fs->csize;
free_sectors = free_clusters * fs->csize;
app->block_count = total_sectors;
app->capacity = (total_sectors * 512) / (1024 * 1024); // MB
printf("Disk Info:\n");
printf(" Total sectors: %lu\n", total_sectors);
printf(" Free sectors: %lu\n", free_sectors);
printf(" Capacity: %lu MB\n", app->capacity);
printf(" Sector size: 512 bytes\n");
return 0;
}
/**
* @brief 读取扇区
*/
uint8_t MSC_App_ReadSectors(MSC_Application_TypeDef *app,
uint8_t *buff,
uint32_t sector,
uint32_t count)
{
if(!app->is_ready) return 1;
/* 通过USB Host库读取扇区 */
USBH_StatusTypeDef status = USBH_MSC_Read(USBH_GetActiveClass(&hUsbHostHS),
sector,
buff,
count);
if(status != USBH_OK)
{
printf("Failed to read sectors: %d\n", status);
return 1;
}
return 0;
}
/**
* @brief 写入扇区
*/
uint8_t MSC_App_WriteSectors(MSC_Application_TypeDef *app,
uint8_t *buff,
uint32_t sector,
uint32_t count)
{
if(!app->is_ready) return 1;
/* 通过USB Host库写入扇区 */
USBH_StatusTypeDef status = USBH_MSC_Write(USBH_GetActiveClass(&hUsbHostHS),
sector,
buff,
count);
if(status != USBH_OK)
{
printf("Failed to write sectors: %d\n", status);
return 1;
}
return 0;
}
/**
* @brief 列出文件
*/
uint8_t MSC_App_FileList(MSC_Application_TypeDef *app, const char *path)
{
FRESULT res;
uint32_t file_count = 0;
uint32_t dir_count = 0;
printf("\nDirectory listing: %s\n", path);
printf("=====================\n");
res = f_opendir(&dir, path);
if(res != FR_OK)
{
printf("Failed to open directory: %d\n", res);
return 1;
}
while(1)
{
res = f_readdir(&dir, &fno);
if(res != FR_OK || fno.fname[0] == 0) break;
if(fno.fattrib & AM_DIR)
{
printf("[DIR] %-20s\n", fno.fname);
dir_count++;
}
else
{
printf("[FILE] %-20s %10lu bytes\n", fno.fname, fno.fsize);
file_count++;
}
}
f_closedir(&dir);
printf("Total: %lu files, %lu directories\n", file_count, dir_count);
return 0;
}
/**
* @brief 读取文件
*/
uint8_t MSC_App_ReadFile(MSC_Application_TypeDef *app,
const char *filename,
uint8_t *buffer,
uint32_t max_len)
{
FRESULT res;
UINT bytes_read = 0;
char full_path[256];
/* 构建完整路径 */
snprintf(full_path, sizeof(full_path), "%s%s", app->current_dir, filename);
/* 打开文件 */
res = f_open(&file, full_path, FA_READ);
if(res != FR_OK)
{
printf("Failed to open file: %s (error: %d)\n", filename, res);
return 1;
}
/* 读取文件内容 */
res = f_read(&file, buffer, max_len, &bytes_read);
if(res != FR_OK)
{
printf("Failed to read file: %d\n", res);
f_close(&file);
return 1;
}
/* 确保字符串以null结尾 */
if(bytes_read < max_len)
{
buffer[bytes_read] = '\0';
}
printf("Read %lu bytes from %s\n", bytes_read, filename);
f_close(&file);
return 0;
}
/**
* @brief 写入文件
*/
uint8_t MSC_App_WriteFile(MSC_Application_TypeDef *app,
const char *filename,
uint8_t *data,
uint32_t len)
{
FRESULT res;
UINT bytes_written = 0;
char full_path[256];
/* 构建完整路径 */
snprintf(full_path, sizeof(full_path), "%s%s", app->current_dir, filename);
/* 打开文件 (创建或截断) */
res = f_open(&file, full_path, FA_WRITE | FA_CREATE_ALWAYS);
if(res != FR_OK)
{
printf("Failed to open file for writing: %d\n", res);
return 1;
}
/* 写入数据 */
res = f_write(&file, data, len, &bytes_written);
if(res != FR_OK || bytes_written != len)
{
printf("Failed to write file: %d (written: %lu/%lu)\n",
res, bytes_written, len);
f_close(&file);
return 1;
}
printf("Wrote %lu bytes to %s\n", bytes_written, filename);
f_close(&file);
return 0;
}
/**
* @brief 获取可用空间
*/
uint8_t MSC_App_GetFreeSpace(MSC_Application_TypeDef *app,
uint32_t *total_sectors,
uint32_t *free_sectors)
{
FATFS *fs = &app->fs;
DWORD free_clusters;
FRESULT res;
res = f_getfree(app->path, &free_clusters, &fs);
if(res != FR_OK)
{
return 1;
}
*total_sectors = (fs->n_fatent - 2) * fs->csize;
*free_sectors = free_clusters * fs->csize;
return 0;
}
/**
* @brief 打印状态
*/
void MSC_App_PrintStatus(MSC_Application_TypeDef *app)
{
printf("\n=== MSC Application Status ===\n");
printf("State: %d\n", app->state);
printf("Initialized: %s\n", app->is_initialized ? "Yes" : "No");
printf("Ready: %s\n", app->is_ready ? "Yes" : "No");
printf("LUN: %d\n", app->lun);
printf("Capacity: %lu MB\n", app->capacity);
printf("Block Size: %lu bytes\n", app->block_size);
printf("Block Count: %lu\n", app->block_count);
printf("Current Directory: %s\n", app->current_dir);
printf("=============================\n");
}
四、HID (鼠标/键盘) 应用实现
4.1 HID应用头文件
c
/**
* @file usb_hid_app.h
* @brief USB HID应用实现
*/
#ifndef __USB_HID_APP_H
#define __USB_HID_APP_H
#include "usb_host_config.h"
#include "usbh_hid.h"
#include "usbh_hid_parser.h"
/* HID设备类型 */
typedef enum {
HID_TYPE_UNKNOWN = 0,
HID_TYPE_MOUSE,
HID_TYPE_KEYBOARD,
HID_TYPE_JOYSTICK,
HID_TYPE_GAMEPAD
} HID_DeviceType;
/* 鼠标按键状态 */
typedef struct {
uint8_t left : 1;
uint8_t right : 1;
uint8_t middle : 1;
uint8_t side1 : 1;
uint8_t side2 : 1;
uint8_t : 3; // 保留
} HID_MouseButtons;
/* 鼠标报告结构 */
typedef struct {
HID_MouseButtons buttons;
int8_t x;
int8_t y;
int8_t wheel;
} HID_MouseReport;
/* 键盘LED状态 */
typedef struct {
uint8_t num_lock : 1;
uint8_t caps_lock : 1;
uint8_t scroll_lock : 1;
uint8_t compose : 1;
uint8_t kana : 1;
uint8_t : 3; // 保留
} HID_KeyboardLEDs;
/* 键盘报告结构 */
typedef struct {
uint8_t modifier; // 修饰键
uint8_t reserved; // 保留
uint8_t keycode[6]; // 按键代码
} HID_KeyboardReport;
/* HID应用结构体 */
typedef struct {
HID_DeviceType type;
uint8_t is_initialized;
uint8_t is_ready;
uint8_t report_size;
uint8_t *report_buffer;
/* 鼠标特定数据 */
HID_MouseReport mouse;
int16_t mouse_x;
int16_t mouse_y;
uint8_t mouse_buttons;
/* 键盘特定数据 */
HID_KeyboardReport keyboard;
HID_KeyboardLEDs leds;
uint8_t last_key;
/* 回调函数指针 */
void (*mouse_callback)(HID_MouseReport *report);
void (*keyboard_callback)(HID_KeyboardReport *report);
void (*joystick_callback)(void *report);
} HID_Application_TypeDef;
/* 函数声明 */
void HID_App_Init(HID_Application_TypeDef *app);
void HID_App_DeInit(HID_Application_TypeDef *app);
void HID_App_Process(HID_Application_TypeDef *app, USBH_HandleTypeDef *phost);
uint8_t HID_App_ParseReportDescriptor(HID_Application_TypeDef *app,
uint8_t *report_desc,
uint16_t desc_len);
void HID_App_SetMouseCallback(HID_Application_TypeDef *app,
void (*callback)(HID_MouseReport *));
void HID_App_SetKeyboardCallback(HID_Application_TypeDef *app,
void (*callback)(HID_KeyboardReport *));
void HID_App_ProcessMouseReport(HID_Application_TypeDef *app, uint8_t *report);
void HID_App_ProcessKeyboardReport(HID_Application_TypeDef *app, uint8_t *report);
void HID_App_SetLEDs(HID_Application_TypeDef *app, HID_KeyboardLEDs leds);
void HID_App_PrintDeviceInfo(HID_Application_TypeDef *app);
#endif /* __USB_HID_APP_H */
4.2 HID应用实现
c
/**
* @file usb_hid_app.c
* @brief USB HID应用实现
*/
#include "usb_hid_app.h"
#include <stdio.h>
#include <string.h>
/* 键盘映射表 */
static const char *key_names[256] = {
[0x04] = "A", [0x05] = "B", [0x06] = "C", [0x07] = "D",
[0x08] = "E", [0x09] = "F", [0x0A] = "G", [0x0B] = "H",
[0x0C] = "I", [0x0D] = "J", [0x0E] = "K", [0x0F] = "L",
[0x10] = "M", [0x11] = "N", [0x12] = "O", [0x13] = "P",
[0x14] = "Q", [0x15] = "R", [0x16] = "S", [0x17] = "T",
[0x18] = "U", [0x19] = "V", [0x1A] = "W", [0x1B] = "X",
[0x1C] = "Y", [0x1D] = "Z",
[0x1E] = "1", [0x1F] = "2", [0x20] = "3", [0x21] = "4",
[0x22] = "5", [0x23] = "6", [0x24] = "7", [0x25] = "8",
[0x26] = "9", [0x27] = "0",
[0x28] = "ENTER", [0x29] = "ESC", [0x2A] = "BACKSPACE",
[0x2B] = "TAB", [0x2C] = "SPACE", [0x2D] = "-", [0x2E] = "=",
[0x2F] = "[", [0x30] = "]", [0x31] = "\\", [0x33] = ";",
[0x34] = "'", [0x35] = "`", [0x36] = ",", [0x37] = ".",
[0x38] = "/", [0x39] = "CAPS LOCK",
[0x3A] = "F1", [0x3B] = "F2", [0x3C] = "F3", [0x3D] = "F4",
[0x3E] = "F5", [0x3F] = "F6", [0x40] = "F7", [0x41] = "F8",
[0x42] = "F9", [0x43] = "F10", [0x44] = "F11", [0x45] = "F12",
[0x46] = "PRINT SCREEN", [0x47] = "SCROLL LOCK", [0x48] = "PAUSE",
[0x49] = "INSERT", [0x4A] = "HOME", [0x4B] = "PAGE UP",
[0x4C] = "DELETE", [0x4D] = "END", [0x4E] = "PAGE DOWN",
[0x4F] = "RIGHT", [0x50] = "LEFT", [0x51] = "DOWN", [0x52] = "UP",
[0x53] = "NUM LOCK", [0x54] = "KP /", [0x55] = "KP *",
[0x56] = "KP -", [0x57] = "KP +", [0x58] = "KP ENTER",
[0x59] = "KP 1", [0x5A] = "KP 2", [0x5B] = "KP 3",
[0x5C] = "KP 4", [0x5D] = "KP 5", [0x5E] = "KP 6",
[0x5F] = "KP 7", [0x60] = "KP 8", [0x61] = "KP 9",
[0x62] = "KP 0", [0x63] = "KP .",
[0xE0] = "LEFT CTRL", [0xE1] = "LEFT SHIFT", [0xE2] = "LEFT ALT",
[0xE3] = "LEFT GUI", [0xE4] = "RIGHT CTRL", [0xE5] = "RIGHT SHIFT",
[0xE6] = "RIGHT ALT", [0xE7] = "RIGHT GUI"
};
/**
* @brief HID应用初始化
*/
void HID_App_Init(HID_Application_TypeDef *app)
{
if(app == NULL) return;
memset(app, 0, sizeof(HID_Application_TypeDef));
app->type = HID_TYPE_UNKNOWN;
app->is_initialized = 0;
app->is_ready = 0;
app->report_size = 0;
app->report_buffer = NULL;
app->mouse_callback = NULL;
app->keyboard_callback = NULL;
app->joystick_callback = NULL;
printf("HID Application initialized\n");
}
/**
* @brief HID应用反初始化
*/
void HID_App_DeInit(HID_Application_TypeDef *app)
{
if(app == NULL) return;
if(app->report_buffer != NULL)
{
free(app->report_buffer);
app->report_buffer = NULL;
}
memset(app, 0, sizeof(HID_Application_TypeDef));
printf("HID Application deinitialized\n");
}
/**
* @brief HID应用处理
*/
void HID_App_Process(HID_Application_TypeDef *app, USBH_HandleTypeDef *phost)
{
if(app == NULL || phost == NULL) return;
/* 检查设备是否就绪 */
if(!USBH_HID_IsReady(phost))
{
app->is_ready = 0;
return;
}
if(!app->is_ready)
{
/* 初始化HID设备 */
HID_HandleTypeDef *hid = (HID_HandleTypeDef*)phost->pActiveClass->pData;
if(hid != NULL)
{
/* 确定设备类型 */
app->report_size = hid->length;
/* 分配报告缓冲区 */
app->report_buffer = (uint8_t*)malloc(app->report_size);
if(app->report_buffer == NULL)
{
printf("Failed to allocate HID report buffer\n");
return;
}
/* 解析报告描述符以确定设备类型 */
uint8_t *report_desc = hid->pData;
uint16_t desc_len = hid->length;
HID_App_ParseReportDescriptor(app, report_desc, desc_len);
app->is_ready = 1;
app->is_initialized = 1;
printf("HID device ready: Type=%d, Report Size=%d\n",
app->type, app->report_size);
}
}
if(app->is_ready)
{
/* 读取HID报告 */
uint8_t status = USBH_HID_GetReport(phost, 0x01, 0, app->report_buffer, app->report_size);
if(status == USBH_OK)
{
/* 根据设备类型处理报告 */
switch(app->type)
{
case HID_TYPE_MOUSE:
HID_App_ProcessMouseReport(app, app->report_buffer);
break;
case HID_TYPE_KEYBOARD:
HID_App_ProcessKeyboardReport(app, app->report_buffer);
break;
default:
/* 未知设备类型 */
break;
}
}
}
}
/**
* @brief 解析报告描述符
*/
uint8_t HID_App_ParseReportDescriptor(HID_Application_TypeDef *app,
uint8_t *report_desc,
uint16_t desc_len)
{
if(app == NULL || report_desc == NULL || desc_len == 0)
return 1;
/* 简化的报告描述符解析 */
uint16_t i = 0;
uint8_t usage_page = 0;
uint8_t usage = 0;
while(i < desc_len)
{
uint8_t byte = report_desc[i];
uint8_t tag = (byte >> 4) & 0x0F;
uint8_t type = (byte >> 2) & 0x03;
uint8_t size = byte & 0x03;
if(type == 0) // Main items
{
if(tag == 0x08) // Usage
{
if(size == 1 && i+1 < desc_len)
{
usage = report_desc[i+1];
/* 根据Usage确定设备类型 */
if(usage_page == 0x01) // Generic Desktop
{
switch(usage)
{
case 0x02: // Mouse
app->type = HID_TYPE_MOUSE;
printf("Detected Mouse device\n");
break;
case 0x04: // Joystick
app->type = HID_TYPE_JOYSTICK;
printf("Detected Joystick device\n");
break;
case 0x05: // Game Pad
app->type = HID_TYPE_GAMEPAD;
printf("Detected Gamepad device\n");
break;
}
}
else if(usage_page == 0x07) // Keyboard/Keypad
{
if(usage == 0x01 || usage == 0x06)
{
app->type = HID_TYPE_KEYBOARD;
printf("Detected Keyboard device\n");
}
}
}
}
else if(tag == 0x06) // Usage Page
{
if(size == 1 && i+1 < desc_len)
{
usage_page = report_desc[i+1];
}
}
}
i += 1 + size;
}
return 0;
}
/**
* @brief 处理鼠标报告
*/
void HID_App_ProcessMouseReport(HID_Application_TypeDef *app, uint8_t *report)
{
if(app == NULL || report == NULL) return;
/* 解析鼠标报告 (标准鼠标格式) */
HID_MouseReport mouse_report;
mouse_report.buttons.left = (report[0] & 0x01) ? 1 : 0;
mouse_report.buttons.right = (report[0] & 0x02) ? 1 : 0;
mouse_report.buttons.middle = (report[0] & 0x04) ? 1 : 0;
mouse_report.buttons.side1 = (report[0] & 0x08) ? 1 : 0;
mouse_report.buttons.side2 = (report[0] & 0x10) ? 1 : 0;
mouse_report.x = (int8_t)report[1];
mouse_report.y = (int8_t)report[2];
mouse_report.wheel = (int8_t)report[3];
/* 更新累积位置 */
app->mouse_x += mouse_report.x;
app->mouse_y += mouse_report.y;
app->mouse_buttons = report[0];
/* 保存最新报告 */
app->mouse = mouse_report;
/* 调用回调函数 */
if(app->mouse_callback != NULL)
{
app->mouse_callback(&mouse_report);
}
/* 调试输出 */
static uint32_t last_print = 0;
uint32_t current_time = HAL_GetTick();
if(current_time - last_print > 100) // 每100ms打印一次
{
last_print = current_time;
printf("Mouse: X=%+4d, Y=%+4d, Wheel=%+3d, Buttons=0x%02X\n",
mouse_report.x, mouse_report.y, mouse_report.wheel, report[0]);
}
}
/**
* @brief 处理键盘报告
*/
void HID_App_ProcessKeyboardReport(HID_Application_TypeDef *app, uint8_t *report)
{
if(app == NULL || report == NULL) return;
/* 解析键盘报告 (标准键盘格式) */
HID_KeyboardReport kb_report;
kb_report.modifier = report[0];
kb_report.reserved = report[1];
memcpy(kb_report.keycode, &report[2], 6);
/* 保存最新报告 */
app->keyboard = kb_report;
/* 检测按键按下 */
for(int i = 0; i < 6; i++)
{
if(kb_report.keycode[i] != 0 && kb_report.keycode[i] != app->last_key)
{
app->last_key = kb_report.keycode[i];
/* 获取键名 */
const char *key_name = (app->last_key < 256) ?
key_names[app->last_key] : "Unknown";
printf("Key Pressed: 0x%02X (%s), Modifier: 0x%02X\n",
app->last_key, key_name ? key_name : "Unknown", kb_report.modifier);
/* 特殊按键处理 */
switch(app->last_key)
{
case 0x39: // Caps Lock
app->leds.caps_lock ^= 1;
HID_App_SetLEDs(app, app->leds);
break;
case 0x53: // Num Lock
app->leds.num_lock ^= 1;
HID_App_SetLEDs(app, app->leds);
break;
}
}
}
/* 检测按键释放 */
uint8_t all_zero = 1;
for(int i = 0; i < 6; i++)
{
if(kb_report.keycode[i] != 0)
{
all_zero = 0;
break;
}
}
if(all_zero && app->last_key != 0)
{
app->last_key = 0;
}
/* 调用回调函数 */
if(app->keyboard_callback != NULL)
{
app->keyboard_callback(&kb_report);
}
}
/**
* @brief 设置鼠标回调
*/
void HID_App_SetMouseCallback(HID_Application_TypeDef *app,
void (*callback)(HID_MouseReport *))
{
if(app != NULL)
{
app->mouse_callback = callback;
}
}
/**
* @brief 设置键盘回调
*/
void HID_App_SetKeyboardCallback(HID_Application_TypeDef *app,
void (*callback)(HID_KeyboardReport *))
{
if(app != NULL)
{
app->keyboard_callback = callback;
}
}
/**
* @brief 设置键盘LED
*/
void HID_App_SetLEDs(HID_Application_TypeDef *app, HID_KeyboardLEDs leds)
{
if(app == NULL || !app->is_ready) return;
uint8_t led_data = 0;
led_data |= leds.num_lock ? 0x01 : 0;
led_data |= leds.caps_lock ? 0x02 : 0;
led_data |= leds.scroll_lock ? 0x04 : 0;
/* 通过USB Host库设置LED */
USBH_StatusTypeDef status = USBH_HID_SetReport(&hUsbHostHS,
0x02, // Output报告
0x00, // Report ID
&led_data, 1);
if(status == USBH_OK)
{
app->leds = leds;
printf("Keyboard LEDs set: 0x%02X\n", led_data);
}
}
/**
* @brief 打印设备信息
*/
void HID_App_PrintDeviceInfo(HID_Application_TypeDef *app)
{
printf("\n=== HID Application Status ===\n");
printf("Device Type: %d\n", app->type);
printf("Initialized: %s\n", app->is_initialized ? "Yes" : "No");
printf("Ready: %s\n", app->is_ready ? "Yes" : "No");
printf("Report Size: %d\n", app->report_size);
if(app->type == HID_TYPE_MOUSE)
{
printf("Mouse X: %d, Y: %d\n", app->mouse_x, app->mouse_y);
printf("Mouse Buttons: 0x%02X\n", app->mouse_buttons);
}
else if(app->type == HID_TYPE_KEYBOARD)
{
printf("Last Key: 0x%02X\n", app->last_key);
printf("LEDs: NumLock=%d, CapsLock=%d, ScrollLock=%d\n",
app->leds.num_lock, app->leds.caps_lock, app->leds.scroll_lock);
}
printf("==============================\n");
}
参考代码 实现STM32F4的USBhost功能 www.youwenfan.com/contentcsv/70868.html
五、主程序与系统集成
5.1 主程序
c
/**
* @file main.c
* @brief USB Host主程序
*/
#include "main.h"
#include "usb_host_config.h"
#include "usb_msc_app.h"
#include "usb_hid_app.h"
#include <stdio.h>
#include <string.h>
/* 全局变量 */
UART_HandleTypeDef huart1;
USBH_HandleTypeDef hUsbHostHS;
HCD_HandleTypeDef hhcd_USB_OTG_HS;
/* 应用实例 */
#if USB_HOST_SUPPORT_MSC
MSC_Application_TypeDef msc_app;
#endif
#if USB_HOST_SUPPORT_HID
HID_Application_TypeDef hid_app;
#endif
/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USB_OTG_HS_HCD_Init(void);
void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id);
void Error_Handler(void);
void USB_Test_Procedure(void);
/* 重定向printf到串口 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 1000);
return len;
}
/**
* @brief 主函数
*/
int main(void)
{
/* HAL库初始化 */
HAL_Init();
/* 系统时钟配置 */
SystemClock_Config();
/* 外设初始化 */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USB_OTG_HS_HCD_Init();
printf("\nSTM32F4 USB Host Demo\n");
printf("=====================\n");
/* 初始化应用 */
#if USB_HOST_SUPPORT_MSC
MSC_App_Init(&msc_app);
#endif
#if USB_HOST_SUPPORT_HID
HID_App_Init(&hid_app);
/* 设置HID回调 */
HID_App_SetMouseCallback(&hid_app, NULL);
HID_App_SetKeyboardCallback(&hid_app, NULL);
#endif
/* 初始化USB Host */
USBH_Init();
printf("System initialized. Waiting for USB device...\n");
uint32_t last_status_time = HAL_GetTick();
uint8_t last_device_state = USB_DEVICE_DISCONNECTED;
/* 主循环 */
while(1)
{
/* USB Host处理 */
USBH_Process();
/* 获取当前设备状态 */
USB_DeviceState current_state = USBH_GetDeviceState();
USB_DeviceInfo *dev_info = USBH_GetDeviceInfo();
/* 设备状态变化检测 */
if(current_state != last_device_state)
{
last_device_state = current_state;
switch(current_state)
{
case USB_DEVICE_CONNECTED:
printf("USB device connected\n");
break;
case USB_DEVICE_ENUMERATED:
printf("USB device enumerated\n");
if(dev_info != NULL)
{
USBH_PrintDeviceInfo(dev_info);
}
break;
case USB_DEVICE_READY:
printf("USB device ready for use\n");
/* 根据设备类执行相应操作 */
if(dev_info->class == USB_CLASS_MASS_STORAGE)
{
printf("MSC device detected\n");
}
else if(dev_info->class == USB_HID_CLASS)
{
printf("HID device detected\n");
}
break;
case USB_DEVICE_DISCONNECTED:
printf("USB device disconnected\n");
break;
case USB_DEVICE_ERROR:
printf("USB device error\n");
break;
}
}
/* 定期状态报告 */
uint32_t current_time = HAL_GetTick();
if(current_time - last_status_time > 5000) // 每5秒
{
last_status_time = current_time;
if(USBH_IsDeviceConnected())
{
printf("USB device is connected\n");
/* 打印应用状态 */
#if USB_HOST_SUPPORT_MSC
if(msc_app.is_ready)
{
MSC_App_PrintStatus(&msc_app);
}
#endif
#if USB_HOST_SUPPORT_HID
if(hid_app.is_ready)
{
HID_App_PrintDeviceInfo(&hid_app);
}
#endif
}
else
{
printf("No USB device connected\n");
}
}
/* 按键检测 */
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) // 用户按键
{
HAL_Delay(200); // 去抖
if(USBH_IsDeviceConnected())
{
printf("Running test procedure...\n");
USB_Test_Procedure();
}
}
HAL_Delay(10);
}
}
/**
* @brief USB测试程序
*/
void USB_Test_Procedure(void)
{
USB_DeviceInfo *dev_info = USBH_GetDeviceInfo();
if(dev_info == NULL) return;
switch(dev_info->class)
{
case USB_CLASS_MASS_STORAGE:
MSC_Test_Procedure(&msc_app);
break;
case USB_HID_CLASS:
HID_Test_Procedure(&hid_app);
break;
default:
printf("No test available for this device class\n");
break;
}
}
/**
* @brief MSC测试程序
*/
void MSC_Test_Procedure(MSC_Application_TypeDef *app)
{
if(!app->is_ready) return;
printf("\n=== MSC Test Procedure ===\n");
/* 1. 列出根目录文件 */
printf("1. Listing root directory:\n");
MSC_App_FileList(app, "/");
/* 2. 创建测试文件 */
printf("\n2. Creating test file...\n");
const char *test_data = "This is a test file created by STM32 USB Host\n";
if(MSC_App_WriteFile(app, "test.txt", (uint8_t*)test_data, strlen(test_data)) == 0)
{
printf("Test file created successfully\n");
}
/* 3. 读取测试文件 */
printf("\n3. Reading test file...\n");
uint8_t buffer[256];
if(MSC_App_ReadFile(app, "test.txt", buffer, sizeof(buffer)) == 0)
{
printf("File content:\n%s", buffer);
}
/* 4. 获取磁盘空间信息 */
printf("\n4. Disk space information:\n");
uint32_t total_sectors, free_sectors;
if(MSC_App_GetFreeSpace(app, &total_sectors, &free_sectors) == 0)
{
printf("Total: %lu sectors, Free: %lu sectors\n",
total_sectors, free_sectors);
printf("Total: %.2f MB, Free: %.2f MB\n",
(total_sectors * 512.0) / (1024 * 1024),
(free_sectors * 512.0) / (1024 * 1024));
}
printf("\n=== Test Complete ===\n");
}
/**
* @brief HID测试程序
*/
void HID_Test_Procedure(HID_Application_TypeDef *app)
{
if(!app->is_ready) return;
printf("\n=== HID Test Procedure ===\n");
switch(app->type)
{
case HID_TYPE_MOUSE:
printf("Mouse test: Move mouse and click buttons\n");
printf("Current position: X=%d, Y=%d\n", app->mouse_x, app->mouse_y);
break;
case HID_TYPE_KEYBOARD:
printf("Keyboard test: Press keys to see output\n");
printf("Press CapsLock to toggle LED\n");
break;
case HID_TYPE_JOYSTICK:
case HID_TYPE_GAMEPAD:
printf("Game controller test: Move joystick and press buttons\n");
break;
default:
printf("Unknown HID device type\n");
break;
}
printf("\n=== Test Running (Press key to exit) ===\n");
/* 运行测试10秒 */
uint32_t start_time = HAL_GetTick();
while(HAL_GetTick() - start_time < 10000)
{
USBH_Process();
HAL_Delay(1);
}
printf("\n=== Test Complete ===\n");
}
/**
* @brief 系统时钟配置
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/* 配置HSE 8MHz */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* 配置系统时钟 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
/* 配置USB时钟 */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48CLKSOURCE_PLLQ;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
}
/**
* @brief USB OTG HS HCD初始化
*/
static void MX_USB_OTG_HS_HCD_Init(void)
{
hhcd_USB_OTG_HS.Instance = USB_OTG_HS;
hhcd_USB_OTG_HS.Init.Host_channels = 12;
hhcd_USB_OTG_HS.Init.speed = HCD_SPEED_HIGH;
hhcd_USB_OTG_HS.Init.dma_enable = DISABLE;
hhcd_USB_OTG_HS.Init.phy_itface = HCD_PHY_ULPI;
hhcd_USB_OTG_HS.Init.Sof_enable = DISABLE;
hhcd_USB_OTG_HS.Init.low_power_enable = DISABLE;
hhcd_USB_OTG_HS.Init.vbus_sensing_enable = DISABLE;
hhcd_USB_OTG_HS.Init.use_external_vbus = DISABLE;
if(HAL_HCD_Init(&hhcd_USB_OTG_HS) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO初始化
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO时钟使能 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/* 用户按键 (PA0) */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* LED (PC13) */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USB HS引脚 */
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/* ULPI引脚配置 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_5|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
六、调试与测试
6.1 测试脚本
c
/**
* @file usb_test.c
* @brief USB测试程序
*/
#include "usb_host_config.h"
/**
* @brief 综合测试
*/
void USB_Comprehensive_Test(void)
{
printf("\n=== USB Host Comprehensive Test ===\n");
/* 测试1: 枚举所有连接的设备 */
printf("\n1. Enumerating all devices...\n");
USB_DeviceInfo *dev_info = USBH_GetDeviceInfo();
if(dev_info != NULL)
{
USBH_PrintDeviceInfo(dev_info);
}
/* 测试2: MSC设备测试 */
if(dev_info->class == USB_CLASS_MASS_STORAGE)
{
printf("\n2. MSC Device Test:\n");
/* 获取磁盘信息 */
uint32_t total_sectors, free_sectors;
if(MSC_App_GetFreeSpace(&msc_app, &total_sectors, &free_sectors) == 0)
{
printf("Disk space: Total=%luMB, Free=%luMB\n",
(total_sectors * 512) / (1024 * 1024),
(free_sectors * 512) / (1024 * 1024));
}
/* 性能测试 */
printf("\n3. Performance Test:\n");
USB_Performance_Test();
}
/* 测试3: HID设备测试 */
if(dev_info->class == USB_HID_CLASS)
{
printf("\n4. HID Device Test:\n");
if(hid_app.type == HID_TYPE_MOUSE)
{
printf("Mouse test: Move mouse for 5 seconds...\n");
uint32_t start_time = HAL_GetTick();
int16_t start_x = hid_app.mouse_x;
int16_t start_y = hid_app.mouse_y;
while(HAL_GetTick() - start_time < 5000)
{
USBH_Process();
HAL_Delay(1);
}
printf("Mouse moved: X=%+d, Y=%+d\n",
hid_app.mouse_x - start_x,
hid_app.mouse_y - start_y);
}
}
printf("\n=== Test Complete ===\n");
}
/**
* @brief 性能测试
*/
void USB_Performance_Test(void)
{
uint8_t buffer[512];
uint32_t start_time, end_time;
uint32_t total_bytes = 0;
uint32_t iterations = 100;
printf("Reading %d sectors...\n", iterations);
start_time = HAL_GetTick();
for(uint32_t i = 0; i < iterations; i++)
{
if(MSC_App_ReadSectors(&msc_app, buffer, i, 1) == 0)
{
total_bytes += 512;
}
else
{
printf("Read error at sector %lu\n", i);
break;
}
}
end_time = HAL_GetTick();
uint32_t elapsed_ms = end_time - start_time;
float speed_kbps = (total_bytes * 1000.0f) / (elapsed_ms * 1024.0f);
printf("Performance: %lu bytes in %lu ms (%.2f KB/s)\n",
total_bytes, elapsed_ms, speed_kbps);
}
七、常见问题解决
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 设备不识别 | 1. 电源问题 2. 硬件连接错误 3. PHY配置错误 | 1. 检查VBUS供电 2. 检查USB线缆 3. 检查ULPI连接 |
| 枚举失败 | 1. 设备描述符错误 2. 超时设置太短 3. 设备不兼容 | 1. 增加枚举超时 2. 检查设备电源 3. 尝试不同设备 |
| MSC设备无法挂载 | 1. 文件系统不支持 2. 分区表错误 3. 格式不兼容 | 1. 格式化为FAT32 2. 检查分区表 3. 重新格式化 |
| HID设备无响应 | 1. 报告描述符解析错误 2. 轮询间隔太短 3. 设备需要特殊初始化 | 1. 增加轮询间隔 2. 检查设备类 3. 查看设备规范 |