STM32F4 USB Host 功能实现

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. 查看设备规范
相关推荐
金戈鐡馬6 小时前
定时器+中断优化单总线通信
stm32·单片机·嵌入式硬件
cici158746 小时前
STM32 + VS1003/VS1053 MP3播放器SD卡读取程序
stm32·单片机·嵌入式硬件
念一不念二6 小时前
[SSD]SSD主控
嵌入式硬件
xiangw@GZ6 小时前
DDR3 颗粒信号定义解析
单片机·嵌入式硬件
小+不通文墨7 小时前
在树莓派中部署emqx
经验分享·笔记·单片机·学习
Deitymoon7 小时前
STM32——oled显示字符串和数字
stm32·单片机·嵌入式硬件
深圳市晨芯阳科技有限公司7 小时前
带延时功能的电压检测系列晨芯阳HC809
单片机·嵌入式硬件·电源芯片·深圳市晨芯阳科技有限公司
xiangw@GZ7 小时前
DDR2 / DDR3 / DDR4 颗粒信号差异对照表
单片机·嵌入式硬件
科芯创展7 小时前
1A,60VIN,1MHz,XZ4116,降压恒流LED驱动芯片 输入电压:5V-60V
stm32·单片机·嵌入式硬件