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 收发时序。
/* 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,之后收发全部由中断完成。
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 处理主机下发的线路编码请求。
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 收发时序。
/* 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。
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 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。
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 收发时序。
/* 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 非零就取数再发回,最后清零计数。
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 端点上准备接收。
CDC_IF_Prop_TypeDef VCP_fops =
{
VCP_DataTx,
VCP_DataRx
};
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 收发时序。
/* 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。
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 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。
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 收发时序。
/* 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(&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 先延时消抖,确认按下后填入按键码并发送,等按键松开后再发送全零报文。
/* 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 收发时序。
/* 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,之后主循环为空,收发由中断完成。
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,再写入静态缓冲并返回其指针。
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 非零时才发送报告。
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 收发时序。
/* 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 状态机在中断与任务中完成。
USBHS_ConfigCLK();
USBD_Init(&USB_dev, &USBD_desc, &USBD_MSC_cb, &USER_cb);
while (1)
{
}
3. 把 Flash 扇区操作映射为存储读写。 STORAGE_Read 按扇区地址乘以 512 转成字节地址读取,STORAGE_Write 在扇区起始处先擦除再写入。
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;
}
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 收发时序。
/* 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 只做时钟配置与类注册,两个功能的收发全部由中断与状态机驱动。
USBHS_ConfigCLK();
USBD_Init(&USB_dev, &USBD_desc, &USBD_MSC_CDC_cb, &USER_cb);
while (1)
{
}
3. 用用户回调承接设备状态变化。 usbd_user.c 用 USBD_User_cb_TypeDef 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。
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 收发时序。
/* 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 报告。
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 聚合复位、配置、挂起与恢复等回调,示例中均为空实现,应用可在此插入自己的处理。
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 收发时序。
/* 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。
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 设备则打印不支持信息。
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 收发时序。
/* 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。
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 类并把应用状态置为文件系统初始化。
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 收发时序。
/* 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。
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 在位移非零时更新位置,再逐位检查按键字段调用按下或释放处理。
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 直接把字符打印到日志,移植时替换为自有按键处理即可。
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 收发时序。
/* 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。
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 类。
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 收发时序。
/* 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。
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 类。
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 收发时序。
/* 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。
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,失败时打印错误并返回,成功则打印容量。
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 写出缓冲区内容。
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 收发时序。
/* 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。
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 类启动。
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 处理菜单与角色切换。
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 位。 |
用户手册 |