5.30 USBHS 使用说明

N32H49x 系列内置高速 USB(USBHS)控制器,支持主机、设备与双角色三种工作模式,最高传输速率 480 Mbps。控制器集成高速 PHY,支持控制、批量、中断与同步四种传输类型,主机侧提供 16 个通道,设备侧提供多端点与专用 4 KB 数据 RAM,并支持 DMA 搬运。本章说明 USBHS 驱动的文件组成、接口来源与 16 个示例。寄存器位定义与协议交互请配合用户手册的“USB 高速主机/设备”章节阅读。

5.30.1 相关文件

相对路径 作用
firmware/n32h49x_usbhs_driver/driver/inc/usbhs_core.h 声明 USBHS 控制器级公开函数与核心数据结构,包括通用初始化与端点收发、主机通道操作与设备端点操作,供主机、设备与双角色程序共用
firmware/n32h49x_usbhs_driver/driver/src/usbhs_core.c 实现控制器级操作:时钟与 PHY 打开、核心复位、主机通道与设备端点初始化、数据包读写与全局中断控制
projects/n32h49x_EVAL/examples/USBHS/ USBHS 示例根目录,含 USBHS_Device 组的 9 个设备示例、USBHS_Host 组的 6 个主机示例与 USBHS_Dual_Role 组的 1 个双角色示例,共 16 个示例
projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC/ 以设备 CDC 示例为例的目录组成:MDK-ARM/、EWARM/、inc/、src/ 与 readme.txt
projects/n32h49x_EVAL/bsp/ 各示例共用的板级支持文件,提供按键、LED、日志打印与毫秒延时等公共操作

5.30.2 API 参考

N32H49x 的 USBHS 接口由 firmware/n32h49x_usbhs_driver/ 提供,不依赖标准外设库。该目录按职能分为三部分。driver/ 是控制器级接口,usbhs_defines.h 与 usbhs_regs.h 给出寄存器与常量定义,usbhs_core.h 声明主机与设备共用的核心操作,例如 USB_BasicInit、USB_CoreInit、USB_EnableGlobalInt、USB_ReadPacket、USB_WritePacket、USB_StopHost、USB_StopDevice,以及主机通道操作 USB_HCH_* 与设备端点操作 USB_EP*。device/ 在控制器级接口之上封装设备协议栈与类驱动,核心为 usbd_core.h 中的 USBD_Init、USBD_Class_cb_TypeDef 与 USBD_User_cb_TypeDef,CDC、HID、MSC 各类驱动位于 device/class/ 下对应子目录。host/ 在控制器级接口之上封装主机协议栈与类驱动,核心为 usbh_core.h 中的 USBH_Init、USBH_Main_Process、USBH_Class_cb_TypeDef 与 USBH_User_cb_TypeDef,CDC、HID、MSC 各类驱动位于 host/class/ 下对应子目录。示例通过 USBD_Init 或 USBH_Init 注册类回调与用户回调来使用这些接口,函数原型与参数以对应头文件的声明为准。

5.30.3 Demo 示例说明

本节说明 USBHS 的 16 个示例:设备组覆盖 CDC、HID、MSC 及其组合设备,主机组覆盖 CDC、HID、MSC 的枚举与文件读写,双角色组在同一工程内切换主机与设备两种角色。

5.30.3.1 CDC 设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC/

打开 USBHS 时钟与高速 PHY→注册 CDC 设备类与用户回调→主机枚举为虚拟串口→USB 与 USART1 双向转发数据

项目 源码解读
功能 USB CDC 虚拟串口设备;
USB 收到的数据从 USART1 转发,USART1 收到的数据经 USB 发回主机。
主机识别为标准通信设备。
源码组成 src/main.c:时钟配置、USBD_Init 与主循环;
src/usbd_cdc_vcp.c:VCP_fops 回调表与 USART1 收发桥接;
src/usbd_desc.c、inc/usbd_desc.h:设备、配置与字符串描述符;
src/usbd_user.c:用户回调;
readme.txt:环境与使用说明;
MDK-ARM/、EWARM/:工程文件。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz,频率档位 16/32 MHz;
PHY 收发时序由 USBHSCTRL1、USBHSCTRL2 调整。
CDC 通信口为 USART1,115200 bps、8 位数据、无校验、1 位停止位;
用宏切换全速与高速模式。
程序与数据路径 main 调用 USBHS_ConfigCLK 打开 USBHS 时钟与 PHY,再以 USBD_Init 注册描述符、USBD_CDC_cb 与 USER_cb,随后进入空循环;
收发由中断驱动:USB 端点中断把数据写入 APP_Rx_Buffer 并触发 VCP_DataTx,USART1 接收中断经 VCP_DataRx 把数据交给 USB 发出。
主要 API USBD_Init、CDC_IF_Prop_TypeDef、VCP_Init、VCP_DataTx、VCP_DataRx、USBDEV_EP_Tx
验证方法 编译下载后把 USB 接到主机,操作系统识别出一个虚拟串口;
用串口助手发送数据,与其相连的 USART1 侧可收到相同数据;
USART1 侧发送的数据也会在串口助手中显示。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把 USBHS 配置为高速设备,通过 CDC 类回调把 USB 与 USART1 连接成一条双向通道。

数据路径: USBHS_ConfigCLK(main.c)→ USBD_Init(main.c)→ USBD_CDC_cb 与 VCP_fops(usbd_cdc_vcp.c)→ USB 端点数据经 VCP_DataRx 交给 USART1 中断经 VCP_DataTx 写入 APP_Rx_Buffer → 主机

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再按板级调试结果写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

时钟源改为外部晶体时,只调整 RCC_ConfigUSBHSClk 的入参,PHY 时序按实测波形微调。

2. 注册 CDC 类并等待中断驱动。 main 先配置时钟,再以 USBD_Init 注册描述符、USBD_CDC_cb 与 USER_cb,之后收发全部由中断完成。

src/main.csrc/main.c · main
    USBHS_ConfigCLK();

    USBD_Init(&USB_dev, &USBD_desc, &USBD_CDC_cb, &USER_cb);

    while (1)
    {
    }

3. 用回调表桥接 USB 与 USART1。 usbd_cdc_vcp.c 用 CDC_IF_Prop_TypeDef 聚合初始化、反初始化、控制请求与收发回调,其中 VCP_Ctrl 处理主机下发的线路编码请求。

src/usbd_cdc_vcp.csrc/usbd_cdc_vcp.c
CDC_IF_Prop_TypeDef VCP_fops = 
{
    VCP_Init,
    VCP_DeInit,
    VCP_Ctrl,
    VCP_DataTx,
    VCP_DataRx
};

移植时把 VCP_Init、VCP_DataTx、VCP_DataRx 内的 USART 换成目标串口即可。

对应源码: src/main.c、src/usbd_cdc_vcp.c

5.30.3.2 CDC 与 HID 组合设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC_HID_Composite/

打开 USBHS 时钟与高速 PHY→注册 CDC 与 HID 复合类回调→主机枚举出虚拟串口与 HID 设备→按键触发 HID 上报并支持串口收发

项目 源码解读
功能 USB CDC 与 HID 组合设备;
同一设备对外提供虚拟串口与自定义 HID 两个接口。
按键按下时经 HID 端点上报状态。
源码组成 src/main.c:时钟配置、USBD_Init、按键初始化与 HID 上报主循环;
src/usbd_hid_cdc_composite.c、inc/usbd_hid_cdc_composite.h:复合类回调;
src/usbd_cdc_core.c 与 src/usbd_customhid_core.c:CDC 与 HID 子类;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
CDC 与 HID 各占用独立接口与端点。
Send_Buffer[2] 预备 HID 报告,PrevXferDone 标记上次上报是否完成。
程序与数据路径 main 先初始化 LED 与按键,调用 USBHS_ConfigCLK 后以 USBD_Init 注册 USBD_HID_CDC_cb 与 USER_cb;
主循环在设备进入 USB_CONFIGURED 且上次上报完成时读按键状态填入 Send_Buffer,调用 USBD_CUSTOM_HID_SendReport 经 HID 端点发出并清除 PrevXferDone。
主要 API USBD_Init、USBD_CUSTOM_HID_SendReport、KEY_Press_Status_Read、USB_CONFIGURED
验证方法 下载运行后主机枚举出虚拟串口与 HID 设备;
按下评估板按键,HID 查看工具中报告值随之变化;
串口侧可与 CDC 功能收发数据。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把 CDC 与 HID 两个子类组合成一个复合设备,主循环只负责按键触发的 HID 上报。

数据路径: 评估板按键 → KEY_Press_Status_Read(main)→ Send_Buffer → USBD_CUSTOM_HID_SendReport(main)→ HID 端点上报 → 主机 HID 查看工具

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册复合类并上报按键状态。 main 用 USBD_Init 注册 USBD_HID_CDC_cb;主循环在设备已配置且上次上报完成时读按键并调用 USBD_CUSTOM_HID_SendReport。

src/main.csrc/main.c · main
    USBD_Init(&USB_dev, &USBD_desc, &USBD_HID_CDC_cb, &USER_cb);

    while (1)
    {
        if(PrevXferDone && (USB_dev.dev.device_status == USB_CONFIGURED))
        {
            Send_Buffer[0] = KEY_REPORT_ID;

3. 用用户回调承接设备状态变化。 usbd_user.c 用 USBD_User_cb_TypeDef 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。

src/usbd_user.csrc/usbd_user.c
USBD_User_cb_TypeDef USER_cb = 
{
    USBD_USER_Init,
    USBD_USER_DeviceReset,
    USBD_USER_DeviceConfigured,
    USBD_USER_DeviceSuspended,
    USBD_USER_DeviceResumed,
    USBD_USER_DeviceConnected,
    USBD_USER_DeviceDisconnected,
};

对应源码: src/main.c、src/usbd_user.c

5.30.3.3 CDC 回环设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC_LoopBack/

打开 USBHS 时钟与高速 PHY→注册 CDC 类回调→主机经 USB 发来数据→设备把收到的数据原样回送主机

项目 源码解读
功能 USB CDC 回环设备;
设备把主机经 USB 发来的数据原样回送,用于验证高速端点收发。
数据不经过串口等外部器件。
源码组成 src/main.c:时钟配置、USBD_Init 与回环主循环;
src/usbd_cdc_vcp.c:VCP_SendData、VCP_ReceiveData 与 VCP_fops;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
接收缓冲 Rxbuffer 长度为 CDC_DATA_MAX_PACKET_SIZE,receive_count 记录待回送字节数。
发送最大包长按模式取 USB_HS_MAX_PACKET_SIZE 或 USB_FS_MAX_PACKET_SIZE。
程序与数据路径 main 配置时钟并以 USBD_Init 注册 USBD_CDC_cb 与 USER_cb;
VCP_DataRx 把收到的字节数写入 receive_count;
主循环检测到其非零时先用 VCP_ReceiveData 从 CDC OUT 端点取数到 Rxbuffer,再用 VCP_SendData 从 CDC IN 端点发回,最后清零计数。
主要 API USBD_Init、VCP_SendData、VCP_ReceiveData、USBDEV_EP_Tx、USBDEV_EP_PrepareRx
验证方法 下载后把 USB 接到主机,系统识别为虚拟串口;
用串口助手发送一串字符,助手会立即收到相同内容。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例用 CDC 类构建一条不依赖外部器件的回环通道,把收到的 USB 数据经同一设备回送。

数据路径: CDC OUT 端点 → VCP_DataRx 记录 receive_count → main 检测到非零 → VCP_ReceiveData 经 USBDEV_EP_PrepareRx 取数到 Rxbuffer → VCP_SendData 经 USBDEV_EP_Tx 发回 → CDC IN 端点 → 主机

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 主循环检测到数据后原样回送。 main 注册 CDC 类后进入循环,只要 receive_count 非零就取数再发回,最后清零计数。

src/main.csrc/main.c · main
    USBD_Init(&USB_dev, &USBD_desc, &USBD_CDC_cb, &USER_cb);

    while (1)
    {
        /* Check to see if we have data yet */
        if (receive_count != 0)
        {
            /* receive character */
            VCP_ReceiveData(&USB_dev, Rxbuffer, receive_count);
            /* send myTxBuffer */
            VCP_SendData(&USB_dev, Rxbuffer, receive_count);

            /* INIT received byte count */
            receive_count = 0;
        }
    }

3. 收发函数直接操作端点。 usbd_cdc_vcp.c 的回调表只保留收发两项,VCP_ReceiveData 在 CDC OUT 端点上准备接收。

src/usbd_cdc_vcp.csrc/usbd_cdc_vcp.c
CDC_IF_Prop_TypeDef VCP_fops = 
{
    VCP_DataTx,
    VCP_DataRx
};
src/usbd_cdc_vcp.csrc/usbd_cdc_vcp.c · VCP_ReceiveData
void VCP_ReceiveData(USB_CORE_MODULE * pdev, uint8_t * pbuf, uint32_t buf_len)
{
    receive_flag = 0;
    USBDEV_EP_PrepareRx(pdev, CDC_OUT_EP, pbuf, buf_len);
}

VCP_SendData 在数据长度等于最大包长时会再补发一个零长度包,移植时须保留这一处理。

对应源码: src/main.c、src/usbd_cdc_vcp.c

5.30.3.4 HID 自定义设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/HID_Customer/

打开 USBHS 时钟与高速 PHY→注册自定义 HID 类回调→主机枚举为 HID 设备→按键触发两字节报告上报

项目 源码解读
功能 USB 自定义 HID 设备;
按键状态封装成 2 字节报告,经 HID 中断端点上报主机。
报告首字节为报告 ID。
源码组成 src/main.c:时钟配置、USBD_Init、按键初始化与上报主循环;
src/usbd_customhid_core.c、inc/usbd_customhid_core.h:自定义 HID 类;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
Send_Buffer[2] 保存报告,PrevXferDone 标记上一次上报是否完成。
HID 报告描述符位于自定义 HID 类文件中。
程序与数据路径 main 初始化 LED 与按键后配置时钟,以 USBD_Init 注册 USBD_CUSTOMHID_cb 与 USER_cb;
主循环在设备进入 USB_CONFIGURED 且上次上报完成时,把报告 ID 与按键电平写入 Send_Buffer,调用 USBD_CUSTOM_HID_SendReport 发出并清除 PrevXferDone。
主要 API USBD_Init、USBD_CUSTOM_HID_SendReport、KEY_Press_Status_Read
验证方法 下载运行后主机识别出一个 HID 设备;
在 HID 查看工具中打开该设备,按下评估板按键可看到报告字节由 0 变为 1,松开恢复为 0。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例用一个两字节报告描述按键状态,主循环只在按键状态变化时发出 HID 报告。

数据路径: 评估板按键 → KEY_Press_Status_Read(main)→ Send_Buffer → USBD_CUSTOM_HID_SendReport(main)→ HID 中断端点 → 主机 HID 查看工具

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 主循环上报按键报告。 main 注册自定义 HID 类后进入循环,在设备已配置且上次上报完成时写入报告并调用 USBD_CUSTOM_HID_SendReport。

src/main.csrc/main.c · main
    USBD_Init(&USB_dev, &USBD_desc, &USBD_CUSTOMHID_cb, &USER_cb);

    while (1)
    {
        if (PrevXferDone && (USB_dev.dev.device_status == USB_CONFIGURED))
        {
            Send_Buffer[0] = KEY_REPORT_ID;

3. 用用户回调承接设备状态变化。 usbd_user.c 用 USBD_User_cb_TypeDef 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。

src/usbd_user.csrc/usbd_user.c
USBD_User_cb_TypeDef USER_cb = {
  USBD_USER_Init,
  USBD_USER_DeviceReset,
  USBD_USER_DeviceConfigured,
  USBD_USER_DeviceSuspended,
  USBD_USER_DeviceResumed,

  USBD_USER_DeviceConnected,
  USBD_USER_DeviceDisconnected,
};

对应源码: src/main.c、src/usbd_user.c

5.30.3.5 HID 键盘设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/HID_Keyboard/

打开 USBHS 时钟与高速 PHY→注册键盘 HID 类回调→主机枚举为键盘设备→按键扫描后经 HID 端点发送按键码

项目 源码解读
功能 USB 键盘设备;
评估板按键映射为键盘按键,扫描到按下时经 HID 端点发送按键码。
按下与松开分别上报,支持消抖。
源码组成 src/main.c:时钟配置、KEY_Press_Status_Scan、USBD_Init 与扫描主循环;
src/usbd_user.c:用户回调,USBD_USER_Init 配置 20 ms 系统节拍;
src/usbd_keyboard_core.c、inc/usbd_keyboard_core.h:键盘 HID 类;
src/usbd_desc.c:描述符;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
USBD_USER_Init 以 SysTick_Config(SystemCoreClock / 50u) 产生 20 ms 节拍供消抖使用。
按键对象与对应字符由 Key_Info 与板级宏定义。
程序与数据路径 main 初始化 LED 与按键后配置时钟,以 USBD_Init 注册 USBD_HID_Keyboard_cb 与 USER_cb;
主循环在设备已配置时依次调用 KEY_Press_Status_Scan 扫描各按键;
扫描到按下后先延时 20 ms 消抖,再用 USBDEV_EP_Tx 从 HID IN 端点发送按键报文,松开时发送全零报文。
主要 API USBD_Init、USBDEV_EP_Tx、SysTick_Config、GPIO_ReadInputDataBit
验证方法 下载运行后主机枚举出一个键盘设备;
打开文本编辑器,按下评估板按键会输入对应字符(如 a、b、c、d)。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把评估板按键当作键盘按键,用一次按下与一次松开两个报文完成一次按键事件。

数据路径: 评估板按键 → KEY_Press_Status_Scan(main)→ Key_Info 查表 → USBDEV_EP_Tx 从 HID IN 端点发送报文 → 主机键盘输入

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 主循环扫描各按键。 main 注册键盘类后进入循环,仅在设备已配置时逐键调用扫描函数,按键与字符的对应关系由板级宏区分。

src/main.csrc/main.c · main
    USBD_Init(&USB_dev, &USBD_desc, &USBD_HID_Keyboard_cb, &USER_cb);

    while (1)
    {
        if(USB_dev.dev.device_status == USB_CONFIGURED)
        {
#ifdef BUTTON_WKUP_EVB
            KEY_Press_Status_Scan(UP_BUTTON_AND_WUKP);  // 'a'

3. 按下与松开分别发送报文。 KEY_Press_Status_Scan 先延时消抖,确认按下后填入按键码并发送,等按键松开后再发送全零报文。

src/main.csrc/main.c · KEY_Press_Status_Scan
        /* Eliminate jitter */
        systick_delay_ms(20); 
        
        if (GPIO_ReadInputDataBit(Key_Info[Key_Name].GPIOx, Key_Info[Key_Name].Pin) == Key_Info[Key_Name].State)
        {
            tmp_buf[2] = 0x04 + Key_Name - 0U;
            USBDEV_EP_Tx(&USB_dev, HID_IN_EP, tmp_buf, 8);
            
            while (GPIO_ReadInputDataBit(Key_Info[Key_Name].GPIOx, Key_Info[Key_Name].Pin) == Key_Info[Key_Name].State)
            {
            }
            
            /* Eliminate jitter */
            systick_delay_ms(20); 
            tmp_buf[2] = 0;
            USBDEV_EP_Tx(&USB_dev, HID_IN_EP, tmp_buf, 8);
        }

内层 while 会一直等到按键松开,因此该函数是阻塞的;移植时若按键较多,须避免长时间停留在扫描函数内。

对应源码: src/main.c

5.30.3.6 HID 鼠标设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/HID_Mouse/

打开 USBHS 时钟与高速 PHY→注册鼠标 HID 类回调→主机枚举为鼠标设备→系统节拍内按按键偏移量上报鼠标报告

项目 源码解读
功能 USB 鼠标设备;
评估板按键映射为鼠标移动方向,周期性上报 4 字节鼠标报告。
报告发送在系统节拍中断中完成。
源码组成 src/main.c:时钟配置、Key_ReadIOPin_continuous、get_mouse_pos、USBD_Init 与空主循环;
src/n32h49x_it.c:SysTick_Handler 内调用 get_mouse_pos 并用 usb_dev_mouse_txreport 上报;
src/usbd_mouse_core.c、inc/usbd_mouse_core.h:鼠标 HID 类;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
移动步长为 CURSOR_STEP(10),报告缓冲为 HID_Buffer[4]。
USBD_USER_Init 以 SysTick_Config 设置上报节拍。
程序与数据路径 main 初始化按键后配置时钟,以 USBD_Init 注册 USBD_MOUSE_cb 与 USER_cb,随后进入空循环;
get_mouse_pos 读按键方向把偏移量写入静态缓冲后返回指针;
SysTick_Handler 每拍调用 get_mouse_pos,有位移时以 usb_dev_mouse_txreport 经 HID 端点上报 4 字节。
主要 API USBD_Init、usb_dev_mouse_txreport、Key_ReadIOPin_continuous、USBDEV_EP_Tx
验证方法 下载运行后主机枚举出一个鼠标设备;
按下评估板方向按键,屏幕上的鼠标指针按对应方向移动。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把鼠标报告的产生与发送分开:位置计算放在普通函数,发送放在系统节拍中断里。

数据路径: 评估板按键 → Key_ReadIOPin_continuous(get_mouse_pos)→ HID_Buffer → SysTick_Handler 判断位移 → usb_dev_mouse_txreport 经 HID 端点 → 主机鼠标

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册鼠标类后进入中断驱动。 main 初始化按键、配置时钟并注册 USBD_MOUSE_cb,之后主循环为空,收发由中断完成。

src/main.csrc/main.c · main
    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBD_Init(&USB_dev, &USBD_desc, &USBD_MOUSE_cb, &USER_cb);

    while (1)
    {
    }

3. 由按键方向生成鼠标报告。 get_mouse_pos 按按键把 CURSOR_STEP 累加到 x 或 y,再写入静态缓冲并返回其指针。

src/main.csrc/main.c · get_mouse_pos
    switch (Key_ReadIOPin_continuous())
    {
        case BUTTON_UP:
            y -= (int8_t)CURSOR_STEP;
            break;
        case BUTTON_DOWN:
            y += (int8_t)CURSOR_STEP;
            break;
        case BUTTON_LEFT:
            x -= (int8_t)CURSOR_STEP;
            break;
        case BUTTON_RIGHT:
            x += (int8_t)CURSOR_STEP;
            break;
        default:
            break;
    }
    HID_Buffer[0] = (uint8_t)0;
    HID_Buffer[1] = (uint8_t)x;
    HID_Buffer[2] = (uint8_t)y;
    HID_Buffer[3] = (uint8_t)0;

    return HID_Buffer;

4. 在系统节拍中上报位移。 SysTick_Handler 每次进入都取一次当前位置,只有 x 或 y 非零时才发送报告。

src/n32h49x_it.csrc/n32h49x_it.c · SysTick_Handler
    buf = get_mouse_pos();
    if((buf[1] != 0u) ||(buf[2] != 0u))
    {
        usb_dev_mouse_txreport(&USB_dev, buf, 4u);
    }

用户回调 USBD_USER_Init 负责配置节拍周期,修改上报频率时调整该处。

对应源码: src/main.c、src/n32h49x_it.c

5.30.3.7 MSC 设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/MSC/

打开 USBHS 时钟与高速 PHY→注册 MSC 类回调→主机枚举为可移动磁盘→主机经 MSC 命令读写板载 SPI Flash

项目 源码解读
功能 USB 大容量存储设备;
把板载 SPI Flash 模拟成可移动磁盘供主机读写。
支持读容量、读扇区与写扇区等标准命令。
源码组成 src/main.c:时钟配置、USBD_Init 与空主循环;
src/usbd_storage.c:USBD_Flash_fops 存储回调与 SPI Flash 读写;
src/usbd_msc_core.c、inc/usbd_msc_core.h:MSC 类;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
扇区大小 512 字节,块数量按 16 Mbit Flash 计算。
存储介质为 W25Q128 SPI Flash,初始化时读 ID 校验。
程序与数据路径 main 配置时钟并以 USBD_Init 注册 USBD_MSC_cb 与 USER_cb,随后进入空循环;
主机枚举后发出 MSC 命令,USBD_Flash_fops 分派到 STORAGE_GetCapacity、STORAGE_Read、STORAGE_Write,后者经 sFLASH_ReadBuffer、sFLASH_WriteBuffer 与 sFLASH_EraseSector 访问 SPI Flash。
主要 API USBD_Init、USBD_STORAGE_cb_TypeDef、STORAGE_Read、STORAGE_Write、sFLASH_WriteBuffer
验证方法 下载后把 USB 接到主机,系统出现一个可移动磁盘;
向磁盘复制文件后重新加载,文件内容保持不变。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把 SPI Flash 封装成一组合的回调交给 MSC 类,主机读写最终落到 Flash 扇区。

数据路径: 主机 MSC 命令 → USBD_Flash_fops(usbd_storage.c)→ STORAGE_Read 或 STORAGE_Write → sFLASH_ReadBuffer、sFLASH_WriteBuffer、sFLASH_EraseSector → 板载 W25Q128

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册 MSC 类后进入空循环。 main 只做时钟配置与类注册,命令处理全部由 MSC 状态机在中断与任务中完成。

src/main.csrc/main.c · main
    USBHS_ConfigCLK();

    USBD_Init(&USB_dev, &USBD_desc, &USBD_MSC_cb, &USER_cb);

    while (1)
    {
    }

3. 把 Flash 扇区操作映射为存储读写。 STORAGE_Read 按扇区地址乘以 512 转成字节地址读取,STORAGE_Write 在扇区起始处先擦除再写入。

src/usbd_storage.csrc/usbd_storage.c · STORAGE_Read
int8_t STORAGE_Read (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    sFLASH_ReadBuffer(buf, blk_addr * 512u, blk_len * 512u);
    return 0;
}
src/usbd_storage.csrc/usbd_storage.c · STORAGE_Write
int8_t STORAGE_Write (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    if(blk_addr%8 == 0)
    {
        sFLASH_EraseSector(blk_addr*512);
    }
    sFLASH_WriteBuffer(buf, blk_addr * 512u, blk_len * 512u);
    return (0);
}

对应源码: src/main.c、src/usbd_storage.c

5.30.3.8 MSC 与 CDC 组合设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/MSC_CDC_Composite/

打开 USBHS 时钟与高速 PHY→注册 MSC 与 CDC 复合类回调→主机枚举出磁盘与虚拟串口→磁盘读写与串口收发各自工作

项目 源码解读
功能 USB MSC 与 CDC 组合设备;
同一设备同时提供可移动磁盘与虚拟串口。
两个功能各自独立,互不影响。
源码组成 src/main.c:时钟配置、USBD_Init 与空主循环;
src/usbd_msc_cdc_composite.c、inc/usbd_msc_cdc_composite.h:复合类回调;
src/usbd_cdc_core.c 与 src/usbd_msc_core.c:CDC 与 MSC 子类;
src/usbd_storage.c:Flash 存储回调;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
MSC 与 CDC 各占用独立接口与端点。
存储介质为 W25Q128 SPI Flash。
程序与数据路径 main 配置时钟并以 USBD_Init 注册 USBD_MSC_CDC_cb 与 USER_cb,随后进入空循环;
主机枚举出两个接口后,磁盘读写走 usbd_storage.c 的 Flash 回调,串口收发走 CDC 子类的中断收发。
主要 API USBD_Init、USBD_STORAGE_cb_TypeDef、VCP_DataTx、VCP_DataRx
验证方法 下载后把 USB 接到主机,系统同时出现一个可移动磁盘与一个虚拟串口;
磁盘可正常读写,串口可正常收发。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把 CDC 与 MSC 两个子类组合到一个设备上,主程序本身只负责注册与等待。

数据路径: 主机枚举 → USBD_MSC_CDC_cb(usbd_msc_cdc_composite.c)分派接口 → MSC 走 usbd_storage.c 的 Flash 回调、CDC 走子类中断收发 → 磁盘与串口各自工作

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册复合类后进入空循环。 main 只做时钟配置与类注册,两个功能的收发全部由中断与状态机驱动。

src/main.csrc/main.c · main
    USBHS_ConfigCLK();

    USBD_Init(&USB_dev, &USBD_desc, &USBD_MSC_CDC_cb, &USER_cb);

    while (1)
    {
    }

3. 用用户回调承接设备状态变化。 usbd_user.c 用 USBD_User_cb_TypeDef 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。

src/usbd_user.csrc/usbd_user.c
USBD_User_cb_TypeDef USER_cb = 
{
    USBD_USER_Init,
    USBD_USER_DeviceReset,
    USBD_USER_DeviceConfigured,
    USBD_USER_DeviceSuspended,
    USBD_USER_DeviceResumed,
    USBD_USER_DeviceConnected,
    USBD_USER_DeviceDisconnected,
};

对应源码: src/main.c、src/usbd_user.c

5.30.3.9 MSC 与 HID 组合设备

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/MSC_HID_Composite/

打开 USBHS 时钟与高速 PHY→注册 MSC 与 HID 复合类回调→主机枚举出磁盘与 HID 设备→磁盘读写与按键上报同时进行

项目 源码解读
功能 USB MSC 与 HID 组合设备;
同一设备同时提供可移动磁盘与自定义 HID。
按键按下时经 HID 端点上报状态。
源码组成 src/main.c:时钟配置、USBD_Init、按键初始化与 HID 上报主循环;
src/usbd_msc_hid_core.c、inc/usbd_msc_hid_core.h:复合类回调;
src/usbd_customhid_core.c 与 src/usbd_msc_core.c:HID 与 MSC 子类;
src/usbd_storage.c:Flash 存储回调;
src/usbd_desc.c、src/usbd_user.c:描述符与用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
MSC 与 HID 各占用独立接口与端点。
Send_Buffer[2] 与 PrevXferDone 用于 HID 上报。
程序与数据路径 main 初始化 LED 与按键后配置时钟,以 USBD_Init 注册 USBD_MSC_HID_cb 与 USER_cb;
主循环在设备已配置且上次上报完成时读按键并调用 USBD_CUSTOM_HID_SendReport,磁盘读写仍走存储回调。
主要 API USBD_Init、USBD_CUSTOM_HID_SendReport、STORAGE_Read、STORAGE_Write
验证方法 下载后主机同时出现一个可移动磁盘与一个 HID 设备;
磁盘读写正常,按下按键可在 HID 查看工具中看到报告变化。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例在同一个设备上同时暴露磁盘与自定义 HID,主循环只负责按键触发的 HID 上报。

数据路径: 评估板按键 → KEY_Press_Status_Read(main)→ Send_Buffer → USBD_CUSTOM_HID_SendReport(main)→ HID 端点;主机磁盘读写经 usbd_storage.c 落到 Flash

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册复合类并上报按键状态。 main 用 USBD_Init 注册 USBD_MSC_HID_cb;主循环在设备已配置且上次上报完成时发出 HID 报告。

src/main.csrc/main.c · main
    USBD_Init(&USB_dev, &USBD_desc, &USBD_MSC_HID_cb, &USER_cb);

    while (1)
    {
        if (PrevXferDone && (USB_dev.dev.device_status == USB_CONFIGURED))
        {
            Send_Buffer[0] = KEY_REPORT_ID;

3. 用用户回调承接设备状态变化。 usbd_user.c 用 USBD_User_cb_TypeDef 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。

src/usbd_user.csrc/usbd_user.c
USBD_User_cb_TypeDef USER_cb = 
{
    USBD_USER_Init,
    USBD_USER_DeviceReset,
    USBD_USER_DeviceConfigured,
    USBD_USER_DeviceSuspended,
    USBD_USER_DeviceResumed,
    USBD_USER_DeviceConnected,
    USBD_USER_DeviceDisconnected,
};

对应源码: src/main.c、src/usbd_user.c

5.30.3.10 CDC 主机

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/CDC/

打开 USBHS 时钟与高速 PHY→注册 CDC 主机类与用户回调→枚举外接虚拟串口设备→按键选择菜单发送文件或接收数据

项目 源码解读
功能 USB 主机 CDC 应用;
枚举外接虚拟串口设备,并在串口终端上给出发送文件与接收数据的菜单。
菜单通过评估板按键选择。
源码组成 src/main.c:时钟配置、USBH_Init 与主机任务循环;
src/usbh_user.c:USER_cb、接口类别判断与 CDC 菜单状态机;
src/usbh_cdc_core.c、inc/usbh_cdc_core.h:CDC 主机类;
src/usbh_fs.c:文件读取辅助;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,带宽 16 MHz;
枚举到通信类接口时把类回调切到 CDC。
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,以 USBH_Init 注册 USBH_CDC_cb 与 USER_cb;
主循环调用 USBH_Main_Process 推动枚举与类任务;
枚举完成后 USBH_USER_EnumerationDone 把接收回调指向 CDC_OutputData,USBH_USER_Application 轮询按键执行 CDC_Handle_Menu,发送菜单用 Fs_Open 读文件后调用 CDC_SendData。
主要 API USBH_Init、USBH_Main_Process、USBH_CDC_cb、CDC_SendData、CDC_StartReception
验证方法 下载后打开串口 115200,把 CDC 设备接入 OTG 口;
枚举完成后按提示选择发送或接收,可看到文件内容发往从设备或从设备收到数据。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把主机 CDC 的任务交给类回调,用户层只负责串口终端上的菜单交互。

数据路径: 评估板按键 → USBH_USER_UserInput(usbh_user.c)→ CDC_Handle_Menu → CDC_SendData 或 CDC_StartReception → USB 端点 → 外接串口设备

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册主机类并推动任务。 main 打印启动信息、初始化按键并配置时钟,以 USBH_Init 注册 CDC 类与用户回调,主循环反复执行 USBH_Main_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Host mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBH_Init(&USBHS_Core, &USB_Host, &USBH_CDC_cb, &USER_cb);

    while (1)
    {
        /* Host Task handler */
        USBH_Main_Process(&USBHS_Core, &USB_Host);
    }

3. 枚举到 CDC 接口后打印类别信息。 USBH_USER_Configuration_DescAvailable 依据接口类别打印已识别的类别,非 CDC 设备则打印不支持信息。

src/usbh_user.csrc/usbh_user.c · USBH_USER_Configuration_DescAvailable
    if ((*id).bInterfaceClass == COMMUNICATION_DEVICE_CLASS_CODE)
    {
        log_info((void *)MSG_CDC_CLASS);
    }

USBH_USER_Application 在 CDC 分支里调用 CDC_DEMO_ProbeKey 与 CDC_Handle_Menu,移植时把菜单动作换成自己的收发流程即可。

对应源码: src/main.c、src/usbh_user.c

5.30.3.11 CDC 与 MSC 组合主机

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/CDC_MSC/

打开 USBHS 时钟与高速 PHY→注册 CDC 主机类与用户回调→枚举外接设备并识别接口类别→切换到 MSC 类或保留 CDC 菜单

项目 源码解读
功能 USB 主机组合应用;
枚举外接设备后按接口类别选择虚拟串口或大容量存储处理。
CDC 分支显示收发菜单,MSC 分支挂载文件系统读写文件。
源码组成 src/main.c:时钟配置、USBH_Init 与主机任务循环;
src/usbh_user.c:USER_cb、接口类别判断与 CDC/MSC 状态切换;
src/usbh_cdc_core.c、inc/usbh_cdc_core.h:CDC 主机类;
inc/usbh_msc_core.h:MSC 主机类接口;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,晶体取 16 MHz;
main 先以 CDC 类回调注册,枚举后再按接口类别切换;
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,以 USBH_Init 注册 USBH_CDC_cb 与 USER_cb;
主循环调用 USBH_Main_Process 推动枚举;
USBH_USER_Configuration_DescAvailable 依据接口类别把 USB_Host.class_cb 改为 USBH_MSC_cb 并置 USB_HOST_USER_AppState = USH_USER_FS_INIT,或保留 CDC;
MSC 分支进入文件系统读写,CDC 分支执行菜单收发。
主要 API USBH_Init、USBH_Main_Process、USBH_CDC_cb、USBH_MSC_cb、USBH_USER_Configuration_DescAvailable
验证方法 下载后打开串口 115200,先接入 U 盘验证文件读写,再接入串口设备验证菜单收发;
两种情况都能在日志中看到对应的类别信息。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例在枚举阶段按接口类别切换类回调,因此单个工程可同时支持虚拟串口与 U 盘。

数据路径: 外接设备描述符 → USBH_USER_Configuration_DescAvailable(usbh_user.c)→ 设置 USB_Host.class_cb → USBH_Main_Process 分派到 USBH_CDC_cb 或 USBH_MSC_cb → 菜单收发或文件系统读写

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册主机类并推动任务。 main 打印启动信息、初始化按键并配置时钟,以 USBH_Init 注册 CDC 类与用户回调,主循环反复执行 USBH_Main_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Host mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBH_Init(&USBHS_Core, &USB_Host, &USBH_CDC_cb, &USER_cb);

    while (1)
    {
        /* Host Task handler */
        USBH_Main_Process(&USBHS_Core, &USB_Host);
    }

3. 按接口类别切换类回调。 USBH_USER_Configuration_DescAvailable 识别到通信类接口时保持 CDC 菜单,识别到 0x08 时改用 MSC 类并把应用状态置为文件系统初始化。

src/usbh_user.csrc/usbh_user.c · USBH_USER_Configuration_DescAvailable
    if ((*id).bInterfaceClass == COMMUNICATION_DEVICE_CLASS_CODE)
    {
        log_info((void *)MSG_CDC_CLASS);
        USB_Host.class_cb = &USBH_CDC_cb;
    }
    else if ((*id).bInterfaceClass == 0x08)
    {
        log_info((void *)MSG_MSC_CLASS);
        USB_Host.class_cb = &USBH_MSC_cb;
        USB_HOST_USER_AppState = USH_USER_FS_INIT;
    }
    else
    {
        log_info("USB Class NOT supported");
    }

main 里注册的类只是初始值,真正的类别取决于 USBH_USER_Configuration_DescAvailable 的判定,移植时按目标设备的接口类别调整分支即可。

对应源码: src/main.c、src/usbh_user.c

5.30.3.12 HID 主机

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/HID/

打开 USBHS 时钟与高速 PHY→注册 HID 主机类与用户回调→枚举外接鼠标或键盘→解析报告并打印按键或更新位置

项目 源码解读
功能 USB 主机 HID 应用;
枚举外接鼠标或键盘,解析输入报告。
鼠标报告用于更新位置并打印,键盘报告直接打印字符。
源码组成 src/main.c:时钟配置、USBH_Init 与主机任务循环;
src/usbh_user.c:USER_cb、USER_MOUSE_Init、USER_MOUSE_ProcessData、USER_KEYBRD_Init、USER_KEYBRD_ProcessData;
src/usbh_hid_core.c、inc/usbh_hid_core.h:HID 主机类;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,晶体取 16 MHz;
报告回调在枚举完成后按协议注册;
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,以 USBH_Init 注册 USBH_HID_cb 与 USER_cb;
主循环调用 USBH_Main_Process 推动枚举;
枚举完成后 USBH_USER_EnumerationDone 调用 USER_MOUSE_Init 注册鼠标输入回调;
类任务解析报告后经 USER_MOUSE_ProcessData 更新位置,键盘报告经 USER_KEYBRD_ProcessData 打印字符。
主要 API USBH_Init、USBH_Main_Process、USBH_HID_cb、USER_MOUSE_ProcessData、USER_KEYBRD_ProcessData
验证方法 下载后打开串口 115200,把 USB 鼠标或键盘接入 OTG 口;
枚举完成后移动鼠标可见坐标日志,敲击键盘可见字符输出。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把 HID 输入报告的解析交给用户回调,工程内同时提供鼠标与键盘两类处理。

数据路径: 外接 HID 设备 → USBH_HID_cb 解析报告 → USER_MOUSE_ProcessData 或 USER_KEYBRD_ProcessData(usbh_user.c)→ 更新位置或打印字符 → 串口日志

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册主机类并推动任务。 main 打印启动信息、初始化按键并配置时钟,以 USBH_Init 注册 HID 类与用户回调,主循环反复执行 USBH_Main_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Host mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBH_Init(&USBHS_Core, &USB_Host, &USBH_HID_cb, &USER_cb);

    while (1)
    {
        /* Host Task handler */
        USBH_Main_Process(&USBHS_Core, &USB_Host);
    }

3. 解析鼠标报告。 USER_MOUSE_ProcessData 在位移非零时更新位置,再逐位检查按键字段调用按下或释放处理。

src/usbh_user.csrc/usbh_user.c · USER_MOUSE_ProcessData
    static uint8_t b_state[3] = { 0, 0, 0 };

    if ((data->x != 0) && (data->y != 0))
    {
        Mouse_PositionUpdate(data->x, data->y);
    }

    for (idx = 0; idx < 3; idx++)
    {
        if (data->button & 1 << idx)
        {
            if (b_state[idx] == 0)
            {
                Mouse_ButtonPress(idx);
                b_state[idx] = 1;
            }
        }
        else
        {
            if (b_state[idx] == 1)
            {
                Mouse_ButtonRelease(idx);
                b_state[idx] = 0;
            }
        }
    }

4. 解析键盘报告。 USER_KEYBRD_ProcessData 直接把字符打印到日志,移植时替换为自有按键处理即可。

src/usbh_user.csrc/usbh_user.c · USER_KEYBRD_ProcessData
    log_info("%c", (char)data);

对应源码: src/main.c、src/usbh_user.c

5.30.3.13 HID 与 CDC 组合主机

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/HID_CDC/

打开 USBHS 时钟与高速 PHY→注册 CDC 主机类与用户回调→枚举外接设备并识别接口类别→切换到 HID 类或保留 CDC 菜单

项目 源码解读
功能 USB 主机组合应用;
枚举外接设备后按接口类别选择虚拟串口或 HID 处理。
CDC 分支显示收发菜单,HID 分支解析鼠标与键盘报告。
源码组成 src/main.c:时钟配置、USBH_Init 与主机任务循环;
src/usbh_user.c:USER_cb、接口类别判断与 HID 报告处理;
src/usbh_cdc_core.c、inc/usbh_cdc_core.h:CDC 主机类;
src/usbh_hid_core.c、inc/usbh_hid_core.h:HID 主机类;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,晶体取 16 MHz;
main 先以 CDC 类回调注册,枚举后再按接口类别切换;
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,以 USBH_Init 注册 USBH_CDC_cb 与 USER_cb;
主循环调用 USBH_Main_Process 推动枚举;
USBH_USER_Configuration_DescAvailable 识别到通信类接口时保留 CDC,识别到 0x03 时改用 USBH_HID_cb;
此后 HID 分支解析报告,CDC 分支执行菜单收发。
主要 API USBH_Init、USBH_Main_Process、USBH_CDC_cb、USBH_HID_cb、USBH_USER_Configuration_DescAvailable
验证方法 下载后打开串口 115200,先接入串口设备验证菜单收发,再接入鼠标或键盘验证报告解析;
两种情况都能在日志中看到对应的类别信息。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例在枚举阶段按接口类别切换类回调,因此单个工程可同时支持虚拟串口与 HID 输入设备。

数据路径: 外接设备描述符 → USBH_USER_Configuration_DescAvailable(usbh_user.c)→ 设置 USB_Host.class_cb → USBH_Main_Process 分派到 USBH_CDC_cb 或 USBH_HID_cb → 菜单收发或报告解析

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册主机类并推动任务。 main 打印启动信息、初始化按键并配置时钟,以 USBH_Init 注册 CDC 类与用户回调,主循环反复执行 USBH_Main_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Host mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBH_Init(&USBHS_Core, &USB_Host, &USBH_CDC_cb, &USER_cb);

    while (1)
    {
        /* Host Task handler */
        USBH_Main_Process(&USBHS_Core, &USB_Host);
    }

3. 按接口类别切换类回调。 USBH_USER_Configuration_DescAvailable 识别到通信类接口时保持 CDC 菜单,识别到 0x03 时改用 HID 类。

src/usbh_user.csrc/usbh_user.c · USBH_USER_Configuration_DescAvailable
    if ((*id).bInterfaceClass == COMMUNICATION_DEVICE_CLASS_CODE)
    {
        log_info((void *)MSG_CDC_CLASS);
        USB_Host.class_cb = &USBH_CDC_cb;
    }
    else if ((*id).bInterfaceClass == 0x03)
    {
        log_info((void *)MSG_HID_CLASS);
        USB_Host.class_cb = &USBH_HID_cb;
    }

main 里注册的类只是初始值,真正的类别取决于 USBH_USER_Configuration_DescAvailable 的判定,移植时按目标设备的接口类别调整分支即可。

对应源码: src/main.c、src/usbh_user.c

5.30.3.14 HID 与 MSC 组合主机

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/HID_MSC/

打开 USBHS 时钟与高速 PHY→注册 MSC 主机类与用户回调→枚举外接设备并识别接口类别→切换到 HID 类或进入文件系统读写

项目 源码解读
功能 USB 主机组合应用;
枚举外接设备后按接口类别选择大容量存储或 HID 处理。
MSC 分支挂载文件系统读写文件,HID 分支解析鼠标与键盘报告。
源码组成 src/main.c:时钟配置、USBH_Init 与主机任务循环;
src/usbh_user.c:USER_cb、接口类别判断与文件系统状态机;
src/usbh_msc_core.c、inc/usbh_msc_core.h:MSC 主机类;
src/usbh_hid_core.c、inc/usbh_hid_core.h:HID 主机类;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,晶体取 16 MHz;
main 先以 MSC 类回调注册,枚举后再按接口类别切换;
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,以 USBH_Init 注册 USBH_MSC_cb 与 USER_cb;
主循环调用 USBH_Main_Process 推动枚举;
USBH_USER_Configuration_DescAvailable 识别到 0x08 时改用 USBH_MSC_cb 并置 USB_HOST_USER_AppState = USH_USER_FS_INIT,识别到 0x03 时改用 USBH_HID_cb;
MSC 分支进入文件系统读写,HID 分支解析报告。
主要 API USBH_Init、USBH_Main_Process、USBH_MSC_cb、USBH_HID_cb、USBH_USER_Configuration_DescAvailable
验证方法 下载后打开串口 115200,先接入 U 盘验证文件读写,再接入鼠标或键盘验证报告解析;
两种情况都能在日志中看到对应的类别信息。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例在枚举阶段按接口类别切换类回调,因此单个工程可同时支持 U 盘与 HID 输入设备。

数据路径: 外接设备描述符 → USBH_USER_Configuration_DescAvailable(usbh_user.c)→ 设置 USB_Host.class_cb → USBH_Main_Process 分派到 USBH_MSC_cb 或 USBH_HID_cb → 文件系统读写或报告解析

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册主机类并推动任务。 main 打印启动信息、初始化按键并配置时钟,以 USBH_Init 注册 MSC 类与用户回调,主循环反复执行 USBH_Main_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Host mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBH_Init(&USBHS_Core, &USB_Host, &USBH_MSC_cb, &USER_cb);

    while (1)
    {
        /* Host Task handler */
        USBH_Main_Process(&USBHS_Core, &USB_Host);
    }

3. 按接口类别切换类回调。 USBH_USER_Configuration_DescAvailable 识别到 0x08 时保持 MSC 并置文件系统初始状态,识别到 0x03 时改用 HID 类。

src/usbh_user.csrc/usbh_user.c · USBH_USER_Configuration_DescAvailable
    if ((*id).bInterfaceClass == 0x08)
    {
        log_info((void *)MSG_MSC_CLASS);
        USB_Host.class_cb = &USBH_MSC_cb;
        USB_HOST_USER_AppState = USH_USER_FS_INIT;
    }
    else if ((*id).bInterfaceClass == 0x03)
    {
        log_info((void *)MSG_HID_CLASS);
        USB_Host.class_cb = &USBH_HID_cb;
    }

main 里注册的类只是初始值,真正的类别取决于 USBH_USER_Configuration_DescAvailable 的判定,移植时按目标设备的接口类别调整分支即可。

对应源码: src/main.c、src/usbh_user.c

5.30.3.15 MSC 主机

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/MSC/

打开 USBHS 时钟与高速 PHY→注册 MSC 主机类与用户回调→枚举外接 U 盘→挂载文件系统并读写或浏览文件

项目 源码解读
功能 USB 主机大容量存储应用;
枚举外接 U 盘并挂载 FatFs。
读写 N32.TXT 并在日志中打印容量与目录内容。
源码组成 src/main.c:时钟配置、USBH_Init 与主机任务循环;
src/usbh_user.c:USER_cb、USBH_USER_MSC_App 文件系统状态机与 Explore_Disk;
src/usbh_msc_core.c、inc/usbh_msc_core.h:MSC 主机类;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,晶体取 16 MHz;
文件系统对象 fatfs 静态分配;
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,以 USBH_Init 注册 USBH_MSC_cb 与 USER_cb;
主循环调用 USBH_Main_Process 推动枚举;
枚举完成后进入 USBH_USER_MSC_App,依次挂载文件系统并读取容量、调用 Explore_Disk 打印目录、再以 f_open 创建并写入 N32.TXT;
USBH_USER_DeInit 把应用状态复位为 USH_USER_FS_INIT。
主要 API USBH_Init、USBH_Main_Process、USBH_MSC_cb、f_mount、f_open、f_write
验证方法 下载后打开串口 115200,把 U 盘接入 OTG 口;
枚举完成后可见容量信息与写入结果,U 盘根目录出现 N32.TXT。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例把 U 盘的文件操作交给 FatFs,用户层状态机负责挂载、写入与浏览的时序。

数据路径: 外接 U 盘 → USBH_MSC_cb → USBH_USER_MSC_App(usbh_user.c)→ FatFs f_mount/f_open/f_write → USBH_MSC_cb 读写扇区 → U 盘

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 注册主机类并推动任务。 main 打印启动信息、初始化按键并配置时钟,以 USBH_Init 注册 MSC 类与用户回调,主循环反复执行 USBH_Main_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Host mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    USBH_Init(&USBHS_Core, &USB_Host, &USBH_MSC_cb, &USER_cb);

    while (1)
    {
        /* Host Task handler */
        USBH_Main_Process(&USBHS_Core, &USB_Host);
    }

3. 挂载文件系统并读取容量。 USBH_USER_MSC_App 在文件系统初始化状态调用 f_mount,失败时打印错误并返回,成功则打印容量。

src/usbh_user.csrc/usbh_user.c · USBH_USER_MSC_App
        case USH_USER_FS_INIT:
            if (f_mount(&fatfs, "", 0u) != FR_OK )//register the work area of the volume
            {
                log_info("> Cannot initialize File System.\n");
                return(-1);
            }
            log_info("> File System initialized.\n");
            capacity = (uint64_t)(USBH_MSC_Param.MSCapacity) * (uint64_t)(USBH_MSC_Param.MSPageLength);

4. 创建并写入文件。 写入状态提示按键后,以 FA_CREATE_ALWAYS | FA_WRITE 打开 N32.TXT 并调用 f_write 写出缓冲区内容。

src/usbh_user.csrc/usbh_user.c · USBH_USER_MSC_App
            if(f_open(&file, "0:N32.TXT",FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
            {
                /* Write buffer to file */
                bytesToWrite = (uint16_t)sizeof(writeTextBuff);
                res= f_write (&file, writeTextBuff, (UINT)bytesToWrite, (void *)&bytesWritten);

                if((bytesWritten == 0u) || (res != FR_OK)) /*EOF or Error*/
                {
                    log_info("> N32.TXT CANNOT be writen.\n");
                }
                else
                {
                    log_info("> 'N32.TXT' file created\n");
                }

                /*close file and filesystem*/
                f_close(&file);

写入完成后状态置为 USH_USER_FS_IDLE;读目录由 USH_USER_FS_READLIST 分支调用 Explore_Disk 完成,USBH_USER_DeInit 把应用状态复位为 USH_USER_FS_INIT。

对应源码: src/main.c、src/usbh_user.c

5.30.3.16 双角色切换

工程目录: projects/n32h49x_EVAL/examples/USBHS/USBHS_Dual_Role/Dual_Role/

打开 USBHS 时钟与高速 PHY→初始化双角色状态机并注册主机类→按键选择主机、设备或空闲角色→主循环按当前角色分派任务

项目 源码解读
功能 USB 双角色应用;
按键在主机、设备与空闲三种角色之间切换。
主机角色运行 U 盘文件系统,设备角色作为 MSC 从设备对外呈现。
源码组成 src/main.c:时钟配置、DRD_Init 与主循环;
src/dual_role_app.c:DRD_Init、DRD_Process、DRD_Application 状态机与菜单;
src/usbh_user.c:主机用户回调;
src/usbd_user.c:设备用户回调;
readme.txt、MDK-ARM/、EWARM/。
关键配置 USBHS 时钟源为 HSE,晶体取 16 MHz;
主机路径注册 USBH_MSC_cb 与 USER_Host_MSC_cb,设备路径注册 USBD_MSC_cb 与 USER_Device_MSC_cb;
日志从 USART1 以 115200 bps 输出。
程序与数据路径 main 初始化日志与按键后配置时钟,调用 DRD_Init 显示菜单并注册主机类,随后进入 DRD_Process 主循环;
DRD_Process 在主机角色下轮询连接并用 USBH_Main_Process 推动任务,其余角色交给 DRD_Application;
DRD_Application 依据 drd.state 与子状态进入 DRD_HOST_IDLE 等分支,重新 USBH_Init 或 USBD_Init 完成角色切换。
主要 API DRD_Init、DRD_Process、USBH_Init、USBD_Init、HOST_IsDeviceConnected
验证方法 下载后打开串口 115200,按提示用按键选择主机角色并接入 U 盘验证文件读写;
再切到设备角色,用 PC 端识别为可移动磁盘验证枚举。
使用提示 须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体作为 USBHS 时钟;
切换全速或高速模式须修改 USE_USB_HS_IN_FS 与 USE_USB_HS_IN_HS 宏。
核心代码解读与移植

示例用一个状态机同时承载主机与设备两条路径,角色切换时重新初始化对应的协议栈。

数据路径: 评估板按键 → DRD_ProbeKey → drd.state / drd.Host_state(dual_role_app.c)→ DRD_Application 分支 → USBH_Init 或 USBD_Init → 主机 USBH_Main_Process 或设备 MSC 任务

1. 配置 USBHS 时钟与高速 PHY。 USBHS_ConfigCLK 选择 HSE 作为时钟源,打开 USBHS 与 PHY 时钟,再写入 PHY 收发时序。

src/main.csrc/main.c · USBHS_ConfigCLK
    /* Set USBHS timing tune */
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0xF << 4)))  | (0xB << 4);  //TX vref TUNE
    RCC->USBHSCTRL1 = (RCC->USBHSCTRL1 & (~(0x7 << 12))) | (0x5 << 12); //TX Rise TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 12))) | (0x0 << 14); //TX Res TUNE
    RCC->USBHSCTRL2 = (RCC->USBHSCTRL2 & (~(0x3 << 16))) | (0x1 << 16); //TX preempamp TUNE

2. 初始化双角色并进入主循环。 main 打印启动信息、初始化按键并配置时钟,调用 DRD_Init 建立默认角色,主循环反复执行 DRD_Process。

src/main.csrc/main.c · main
    log_init();
    log_info(" USBHS Dual Role mode test start\r\n");

    Hardware_Board_Key_Init();
    USBHS_ConfigCLK();

    DRD_Init();
    while (1)
    {
        DRD_Process();
    }

3. 初始化状态机并注册主机类。 DRD_Init 先刷新菜单,再初始化 USB BSP 与主机栈,默认以 MSC 类启动。

src/dual_role_app.csrc/dual_role_app.c · DRD_Init
void DRD_Init(void)
{
    log_info("> Initializing dual role demo....\r\n");
    DRD_SelectItem(DRD_main_menu, 0);
    log_info("> Demo Initialized \r\n");
    log_info("> Use KeyButton to Select dual role run mode.\r\n");
    USB_BSP_Init();
    USBH_Init(&USBHS_Core, &USB_Host, &USBH_MSC_cb, &USER_Host_MSC_cb);

    USB_BSP_mDelay(100);

    DRD_UNLOCK();
}

4. 按角色分派任务。 DRD_Process 在主机角色下先判断设备连接再推动主机任务,随后始终调用 DRD_Application 处理菜单与角色切换。

src/dual_role_app.csrc/dual_role_app.c · DRD_Process
void DRD_Process(void)
{
    if (drd.state == DRD_HOST)
    {
        if (HOST_IsDeviceConnected(&USBHS_Core))
        {
            /* Host Task handler */
            USBH_Main_Process(&USBHS_Core, &USB_Host);
        }
    }
    DRD_Application();
}

DRD_Application 的子状态分支里,主机空闲态会重新 USBH_Init 使能高速主机,设备空闲态则调用 USBD_Init 挂载 MSC 从设备;移植时按目标角色替换对应的类回调与初始化参数即可。

对应源码: src/main.c、src/dual_role_app.c

5.30.4 注意与排障

场景 检查与建议 来源
外部晶体频率 USBHS 时钟来自 HSE,须使用 16 MHz、19.2 MHz、20 MHz、24 MHz、26 MHz 或 32 MHz 外部晶体;
频率不在该范围内时,PHY 无法建立稳定的 480 Mbps 收发时钟。
用户手册
VBUS 供电 USBHS 不能自行输出 5 V VBUS 电压;
作主机对外供电时须外接电荷泵或电源开关,并由 HPCS 的 PPWR 位控制输出。
用户手册
总线复位时长 触发 USB 复位时 PRST 位须保持足够时间;
高速模式不少于 50 ms,全速与低速模式不少于 10 ms。
用户手册
模式切换等待 切换 FDMODE 或 FHMODE 后须等待不少于 25 ms 再访问 PHY,否则读写结果不稳定。 用户手册
FIFO 与端点配置 收发 FIFO 刷新(TXFFLSH、RXFFLSH)后须等待硬件自动清位再进行下一步;
接收 FIFO 须预留 10 个字的空间给 SETUP 事务。
用户手册
中断处理顺序 读取 DAINT 或 HACHINT 后先确定中断源,再清除对应的具体中断标志,避免漏处理。 用户手册
错误恢复与远程唤醒 发生 ERERRF 时只能通过软断开(SFTDIS)恢复;
远程唤醒后须在 1~15 ms 内清除 RMWKUP 位。
用户手册
国民技术开发套件文档中心

按芯片系列在线查阅国民技术 MCU 开发套件的芯片资料、SDK、驱动 API 与 Demo 示例说明。

© 2026 国民技术股份有限公司 版权所有粤ICP备11069485号本站使用不含 Cookie 的匿名访问统计