5.30 USBHS Guide

The N32H49x series integrates a high-speed USB (USBHS) controller that supports host, device, and dual-role operation at up to 480 Mbps. The controller embeds a high-speed PHY and supports the control, bulk, interrupt, and isochronous transfer types; the host side provides 16 channels, and the device side provides multiple endpoints with a dedicated 4 KB data RAM and DMA transfer. This chapter covers the files of the USBHS driver, the source of its interfaces, and its 16 examples. Read it together with the “USB high-speed host/device” chapter of the user manual for the register bit definitions and protocol interaction.

5.30.1 Related files

Relative path Purpose
firmware/n32h49x_usbhs_driver/driver/inc/usbhs_core.h Declares the controller-level public functions and core data structures of USBHS, including general initialization and endpoint transfers, host channel operations, and device endpoint operations shared by the host, device, and dual-role programs
firmware/n32h49x_usbhs_driver/driver/src/usbhs_core.c Implements the controller-level operations: clock and PHY enable, core reset, host channel and device endpoint initialization, packet read and write, and global interrupt control
projects/n32h49x_EVAL/examples/USBHS/ USBHS example root, containing 9 device examples in the USBHS_Device group, 6 host examples in the USBHS_Host group, and 1 dual-role example in the USBHS_Dual_Role group, 16 examples in total
projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC/ Layout of this device CDC example project: MDK-ARM/, EWARM/, inc/, src/, and readme.txt
projects/n32h49x_EVAL/bsp/ Board support files shared by the examples, providing keys, LEDs, log printing, and millisecond delay

5.30.2 API reference

The N32H49x USBHS interfaces are provided by firmware/n32h49x_usbhs_driver/ and do not depend on the standard peripheral library. The directory is organized into three parts. driver/ is the controller-level interface: usbhs_defines.h and usbhs_regs.h give the register and constant definitions, and usbhs_core.h declares the core operations shared by the host and device, such as USB_BasicInit, USB_CoreInit, USB_EnableGlobalInt, USB_ReadPacket, USB_WritePacket, USB_StopHost, and USB_StopDevice, together with the host channel operations USB_HCH_* and the device endpoint operations USB_EP*. device/ wraps the device stack and class drivers on top of the controller-level interface; its core is USBD_Init, USBD_Class_cb_TypeDef, and USBD_User_cb_TypeDef in usbd_core.h, and the CDC, HID, and MSC class drivers live in the corresponding subdirectories under device/class/. host/ wraps the host stack and class drivers on top of the controller-level interface; its core is USBH_Init, USBH_Main_Process, USBH_Class_cb_TypeDef, and USBH_User_cb_TypeDef in usbh_core.h, and the CDC, HID, and MSC class drivers live in the corresponding subdirectories under host/class/. The examples use these interfaces by registering class callbacks and user callbacks through USBD_Init or USBH_Init; the function prototypes and parameters follow the declarations in the corresponding headers.

5.30.3 Demo descriptions

This section describes the 16 USBHS examples: the device group covers CDC, HID, MSC, and their composite devices, the host group covers CDC, HID, and MSC enumeration and file access, and the dual-role group switches between the host and device roles within one project.

5.30.3.1 CDC device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC/

Enable the USBHS clock and high-speed PHY→Register the CDC device class and user callbacks→The host enumerates a virtual COM port→Forward data both ways between USB and USART1

Item Source analysis
Function USB CDC virtual COM port device;
data received over USB is forwarded to USART1, and data received on USART1 is sent back to the host over USB.
The host recognizes it as a standard communication device.
Source files src/main.c: clock configuration, USBD_Init, and the main loop;
src/usbd_cdc_vcp.c: the VCP_fops callback table and the USART1 bridge;
src/usbd_desc.c and inc/usbd_desc.h: device, configuration, and string descriptors;
src/usbd_user.c: user callbacks;
readme.txt: environment and usage;
MDK-ARM/ and EWARM/: project files.
Key configuration USBHS clock source is HSE at 16 MHz, with 16/32 MHz frequency options;
the PHY transmit and receive timing is adjusted through USBHSCTRL1 and USBHSCTRL2.
The CDC communication port is USART1 at 115200 baud, 8 data bits, no parity, and 1 stop bit;
a macro switches between full-speed and high-speed mode.
Program and data path main calls USBHS_ConfigCLK to enable the USBHS clock and PHY, then registers the descriptors, USBD_CDC_cb, and USER_cb with USBD_Init and enters an empty loop;
transfers are interrupt-driven: the USB endpoint interrupt writes data into APP_Rx_Buffer and triggers VCP_DataTx, while the USART1 receive interrupt passes data to USB through VCP_DataRx.
Main APIs USBD_Init, CDC_IF_Prop_TypeDef, VCP_Init, VCP_DataTx, VCP_DataRx, USBDEV_EP_Tx
Verification After download, connect USB to the host and the operating system recognizes a virtual COM port;
send data from a serial tool and the connected USART1 receives the same data;
data sent from the USART1 side also appears in the serial tool.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example configures USBHS as a high-speed device and connects USB and USART1 into a two-way channel through the CDC class callbacks.

Data path: USBHS_ConfigCLK (main.c) -> USBD_Init (main.c) -> USBD_CDC_cb and VCP_fops (usbd_cdc_vcp.c) -> USB endpoint data is handed to USART1 through VCP_DataRx and written into APP_Rx_Buffer through VCP_DataTx -> host

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing from the board-level tuning results.

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

When the clock source is changed to an external crystal, adjust only the argument of RCC_ConfigUSBHSClk and fine-tune the PHY timing against the measured waveform.

2. Register the CDC class and wait for interrupt-driven transfers. main first configures the clock, then registers the descriptors, USBD_CDC_cb, and USER_cb with USBD_Init; afterwards all transfers are driven by interrupts.

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

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

    while (1)
    {
    }

3. Bridge USB and USART1 with the callback table. usbd_cdc_vcp.c uses CDC_IF_Prop_TypeDef to aggregate the init, de-init, control request, and transmit and receive callbacks, and VCP_Ctrl handles the line coding request from the host.

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
};

When porting, replace the USART inside VCP_Init, VCP_DataTx, and VCP_DataRx with the target serial port.

Corresponding source files: src/main.c, src/usbd_cdc_vcp.c

5.30.3.2 CDC and HID composite device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC_HID_Composite/

Enable the USBHS clock and high-speed PHY→Register the CDC and HID composite class callbacks→The host enumerates a virtual COM port and an HID device→A key press reports over HID while the COM port transfers data

Item Source analysis
Function USB CDC and HID composite device;
the same device exposes a virtual COM port and a custom HID interface.
A key press reports its state over the HID endpoint.
Source files src/main.c: clock configuration, USBD_Init, key initialization, and the HID reporting loop;
src/usbd_hid_cdc_composite.c and inc/usbd_hid_cdc_composite.h: the composite class callbacks;
src/usbd_cdc_core.c and src/usbd_customhid_core.c: the CDC and HID subclasses;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
CDC and HID each occupy their own interface and endpoints.
Send_Buffer[2] holds the HID report and PrevXferDone marks whether the previous report finished.
Program and data path main first initializes the LEDs and keys, calls USBHS_ConfigCLK, then registers USBD_HID_CDC_cb and USER_cb with USBD_Init;
when the device reaches USB_CONFIGURED and the previous report finished, the main loop reads the key state into Send_Buffer, sends it through the HID endpoint with USBD_CUSTOM_HID_SendReport, and clears PrevXferDone.
Main APIs USBD_Init, USBD_CUSTOM_HID_SendReport, KEY_Press_Status_Read, USB_CONFIGURED
Verification After download, the host enumerates a virtual COM port and an HID device;
press an on-board key and the report value changes in an HID viewer;
the COM port side can exchange data with the CDC function.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example combines the CDC and HID subclasses into one composite device; the main loop only handles the key-triggered HID report.

Data path: on-board key -> KEY_Press_Status_Read (main) -> Send_Buffer -> USBD_CUSTOM_HID_SendReport (main) -> HID endpoint report -> host HID viewer

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the composite class and report the key state. main registers USBD_HID_CDC_cb with USBD_Init; when the device is configured and the previous report finished, the main loop reads the key and calls 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. Handle device state changes with the user callbacks. usbd_user.c uses USBD_User_cb_TypeDef to aggregate the reset, configured, suspend, and resume callbacks; all are empty in the example, and the application can insert its own handling here.

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,
};

Corresponding source files: src/main.c, src/usbd_user.c

5.30.3.3 CDC loopback device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/CDC_LoopBack/

Enable the USBHS clock and high-speed PHY→Register the CDC class callbacks→The host sends data over USB→The device echoes the received data back to the host

Item Source analysis
Function USB CDC loopback device;
the device echoes data sent by the host over USB, which verifies high-speed endpoint transfers.
The data does not pass through a serial port or any external device.
Source files src/main.c: clock configuration, USBD_Init, and the loopback loop;
src/usbd_cdc_vcp.c: VCP_SendData, VCP_ReceiveData, and VCP_fops;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
the receive buffer Rxbuffer is CDC_DATA_MAX_PACKET_SIZE long and receive_count records the bytes to echo.
The maximum send packet size is USB_HS_MAX_PACKET_SIZE or USB_FS_MAX_PACKET_SIZE depending on the mode.
Program and data path main configures the clock and registers USBD_CDC_cb and USER_cb with USBD_Init;
VCP_DataRx stores the received byte count into receive_count;
when the main loop sees it non-zero, it first reads the data from the CDC OUT endpoint into Rxbuffer with VCP_ReceiveData, then sends it back from the CDC IN endpoint with VCP_SendData, and finally clears the count.
Main APIs USBD_Init, VCP_SendData, VCP_ReceiveData, USBDEV_EP_Tx, USBDEV_EP_PrepareRx
Verification After download, connect USB to the host and the system recognizes a virtual COM port;
send a string from a serial tool and the tool receives the same content immediately.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example builds a loopback channel on the CDC class without any external device, sending received USB data back through the same device.

Data path: CDC OUT endpoint -> VCP_DataRx records receive_count -> main detects it non-zero -> VCP_ReceiveData reads into Rxbuffer through USBDEV_EP_PrepareRx -> VCP_SendData sends back through USBDEV_EP_Tx -> CDC IN endpoint -> host

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Echo the data when the main loop detects it. After registering the CDC class, main enters a loop that reads and sends back the data whenever receive_count is non-zero, then clears the 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. The transfer functions drive the endpoints directly. The callback table in usbd_cdc_vcp.c keeps only the transmit and receive entries, and VCP_ReceiveData prepares reception on the CDC OUT endpoint.

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);
}

When the data length equals the maximum packet size, VCP_SendData sends one extra zero-length packet; keep this handling when porting.

Corresponding source files: src/main.c, src/usbd_cdc_vcp.c

5.30.3.4 HID custom device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/HID_Customer/

Enable the USBHS clock and high-speed PHY→Register the custom HID class callbacks→The host enumerates an HID device→A key press reports a two-byte report

Item Source analysis
Function USB custom HID device;
the key state is packed into a 2-byte report and sent to the host over the HID interrupt endpoint.
The first byte of the report is the report ID.
Source files src/main.c: clock configuration, USBD_Init, key initialization, and the reporting loop;
src/usbd_customhid_core.c and inc/usbd_customhid_core.h: the custom HID class;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
Send_Buffer[2] holds the report and PrevXferDone marks whether the previous report finished.
The HID report descriptor is in the custom HID class file.
Program and data path main initializes the LEDs and keys, configures the clock, and registers USBD_CUSTOMHID_cb and USER_cb with USBD_Init;
when the device reaches USB_CONFIGURED and the previous report finished, the main loop writes the report ID and key level into Send_Buffer, sends it with USBD_CUSTOM_HID_SendReport, and clears PrevXferDone.
Main APIs USBD_Init, USBD_CUSTOM_HID_SendReport, KEY_Press_Status_Read
Verification After download, the host recognizes an HID device;
open it in an HID viewer and press an on-board key to see the report byte change from 0 to 1, then return to 0 on release.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example describes the key state with a two-byte report and sends an HID report only when the key state changes.

Data path: on-board key -> KEY_Press_Status_Read (main) -> Send_Buffer -> USBD_CUSTOM_HID_SendReport (main) -> HID interrupt endpoint -> host HID viewer

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Report the key in the main loop. After registering the custom HID class, main enters a loop that writes the report and calls USBD_CUSTOM_HID_SendReport when the device is configured and the previous report finished.

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. Handle device state changes with the user callbacks. usbd_user.c uses USBD_User_cb_TypeDef to aggregate the reset, configured, suspend, and resume callbacks; all are empty in the example, and the application can insert its own handling here.

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,
};

Corresponding source files: src/main.c, src/usbd_user.c

5.30.3.5 HID keyboard device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/HID_Keyboard/

Enable the USBHS clock and high-speed PHY→Register the keyboard HID class callbacks→The host enumerates a keyboard device→Key scanning sends the key code over the HID endpoint

Item Source analysis
Function USB keyboard device;
the on-board keys map to keyboard keys, and a detected press sends the key code over the HID endpoint.
Press and release are reported separately, with debouncing.
Source files src/main.c: clock configuration, KEY_Press_Status_Scan, USBD_Init, and the scanning loop;
src/usbd_user.c: user callbacks, where USBD_USER_Init sets up a 20 ms system tick;
src/usbd_keyboard_core.c and inc/usbd_keyboard_core.h: the keyboard HID class;
src/usbd_desc.c: descriptors;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
USBD_USER_Init produces a 20 ms tick with SysTick_Config(SystemCoreClock / 50u) for debouncing.
The key objects and their characters are defined by Key_Info and the board macros.
Program and data path main initializes the LEDs and keys, configures the clock, and registers USBD_HID_Keyboard_cb and USER_cb with USBD_Init;
when the device is configured, the main loop calls KEY_Press_Status_Scan for each key in turn;
on a detected press it first delays 20 ms for debouncing, then sends the key report from the HID IN endpoint with USBDEV_EP_Tx, and sends an all-zero report on release.
Main APIs USBD_Init, USBDEV_EP_Tx, SysTick_Config, GPIO_ReadInputDataBit
Verification After download, the host enumerates a keyboard device;
open a text editor and press an on-board key to type the corresponding character (such as a, b, c, or d).
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example treats the on-board keys as keyboard keys and completes one key event with a press report and a release report.

Data path: on-board key -> KEY_Press_Status_Scan (main) -> Key_Info table lookup -> USBDEV_EP_Tx sends the report from the HID IN endpoint -> host keyboard input

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Scan each key in the main loop. After registering the keyboard class, main enters a loop and calls the scan function for each key only when the device is configured; the key-to-character mapping is distinguished by the board macros.

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. Send a report for press and release. KEY_Press_Status_Scan first delays for debouncing, then after confirming the press fills in the key code and sends it, and sends an all-zero report after the key is released.

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);
        }

The inner while waits until the key is released, so the function is blocking; if there are many keys when porting, avoid staying inside the scan function for too long.

Corresponding source files: src/main.c

5.30.3.6 HID mouse device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/HID_Mouse/

Enable the USBHS clock and high-speed PHY→Register the mouse HID class callbacks→The host enumerates a mouse device→The system tick reports the mouse report from the key offsets

Item Source analysis
Function USB mouse device;
the on-board keys map to mouse movement directions, and a 4-byte mouse report is sent periodically.
The report is sent in the system tick interrupt.
Source files src/main.c: clock configuration, Key_ReadIOPin_continuous, get_mouse_pos, USBD_Init, and the empty main loop;
src/n32h49x_it.c: SysTick_Handler calls get_mouse_pos and reports with usb_dev_mouse_txreport;
src/usbd_mouse_core.c and inc/usbd_mouse_core.h: the mouse HID class;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
the movement step is CURSOR_STEP (10) and the report buffer is HID_Buffer[4].
USBD_USER_Init sets the reporting tick with SysTick_Config.
Program and data path main initializes the keys, configures the clock, and registers USBD_MOUSE_cb and USER_cb with USBD_Init, then enters an empty loop;
get_mouse_pos reads the key direction, writes the offset into a static buffer, and returns its pointer;
SysTick_Handler calls get_mouse_pos every tick and, when there is movement, reports 4 bytes from the HID endpoint with usb_dev_mouse_txreport.
Main APIs USBD_Init, usb_dev_mouse_txreport, Key_ReadIOPin_continuous, USBDEV_EP_Tx
Verification After download, the host enumerates a mouse device;
press an on-board direction key and the mouse pointer on the screen moves in that direction.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example separates mouse report generation from sending: the position calculation is in an ordinary function and the sending is in the system tick interrupt.

Data path: on-board key -> Key_ReadIOPin_continuous (get_mouse_pos) -> HID_Buffer -> SysTick_Handler checks the movement -> usb_dev_mouse_txreport over the HID endpoint -> host mouse

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the mouse class and go interrupt-driven. main initializes the keys, configures the clock, and registers USBD_MOUSE_cb; afterwards the main loop is empty and transfers are done by interrupts.

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. Generate the mouse report from the key direction. get_mouse_pos adds CURSOR_STEP to x or y according to the key, then writes the static buffer and returns its pointer.

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. Report the movement in the system tick. SysTick_Handler takes the current position on every entry and sends the report only when x or y is non-zero.

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);
    }

The USBD_USER_Init user callback configures the tick period; adjust it to change the reporting frequency.

Corresponding source files: src/main.c, src/n32h49x_it.c

5.30.3.7 MSC device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/MSC/

Enable the USBHS clock and high-speed PHY→Register the MSC class callbacks→The host enumerates a removable disk→The host reads and writes the on-board SPI Flash through MSC commands

Item Source analysis
Function USB mass storage device;
the on-board SPI Flash is presented to the host as a removable disk.
It supports standard commands such as read capacity, read sectors, and write sectors.
Source files src/main.c: clock configuration, USBD_Init, and the empty main loop;
src/usbd_storage.c: the USBD_Flash_fops storage callbacks and the SPI Flash access;
src/usbd_msc_core.c and inc/usbd_msc_core.h: the MSC class;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
the sector size is 512 bytes and the block count is computed for a 16 Mbit Flash.
The storage medium is a W25Q128 SPI Flash whose ID is read and checked at initialization.
Program and data path main configures the clock and registers USBD_MSC_cb and USER_cb with USBD_Init, then enters an empty loop;
after enumeration the host issues MSC commands, and USBD_Flash_fops dispatches them to STORAGE_GetCapacity, STORAGE_Read, and STORAGE_Write, the last of which accesses the SPI Flash through sFLASH_ReadBuffer, sFLASH_WriteBuffer, and sFLASH_EraseSector.
Main APIs USBD_Init, USBD_STORAGE_cb_TypeDef, STORAGE_Read, STORAGE_Write, sFLASH_WriteBuffer
Verification After download, connect USB to the host and a removable disk appears;
copy a file to the disk, reload it, and the file content is unchanged.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example wraps the SPI Flash into a set of callbacks handed to the MSC class, so host read and write requests end up on Flash sectors.

Data path: host MSC command -> USBD_Flash_fops (usbd_storage.c) -> STORAGE_Read or STORAGE_Write -> sFLASH_ReadBuffer, sFLASH_WriteBuffer, sFLASH_EraseSector -> on-board W25Q128

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the MSC class and enter the empty loop. main only configures the clock and registers the class; command handling is entirely done by the MSC state machine in the interrupts and tasks.

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

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

    while (1)
    {
    }

3. Map Flash sector operations to storage reads and writes. STORAGE_Read multiplies the sector address by 512 to get the byte address, and STORAGE_Write erases at the start of a sector before writing.

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);
}

Corresponding source files: src/main.c, src/usbd_storage.c

5.30.3.8 MSC and CDC composite device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/MSC_CDC_Composite/

Enable the USBHS clock and high-speed PHY→Register the MSC and CDC composite class callbacks→The host enumerates a disk and a virtual COM port→Disk access and COM port transfer work independently

Item Source analysis
Function USB MSC and CDC composite device;
the same device provides a removable disk and a virtual COM port at the same time.
The two functions are independent of each other.
Source files src/main.c: clock configuration, USBD_Init, and the empty main loop;
src/usbd_msc_cdc_composite.c and inc/usbd_msc_cdc_composite.h: the composite class callbacks;
src/usbd_cdc_core.c and src/usbd_msc_core.c: the CDC and MSC subclasses;
src/usbd_storage.c: the Flash storage callbacks;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
MSC and CDC each occupy their own interface and endpoints.
The storage medium is a W25Q128 SPI Flash.
Program and data path main configures the clock and registers USBD_MSC_CDC_cb and USER_cb with USBD_Init, then enters an empty loop;
after the host enumerates the two interfaces, disk access goes through the Flash callbacks in usbd_storage.c and COM port transfers go through the interrupt transfers of the CDC subclass.
Main APIs USBD_Init, USBD_STORAGE_cb_TypeDef, VCP_DataTx, VCP_DataRx
Verification After download, connect USB to the host and a removable disk and a virtual COM port appear together;
the disk can be read and written and the COM port can exchange data.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example combines the CDC and MSC subclasses into one device, and the main program itself only registers and waits.

Data path: host enumeration -> USBD_MSC_CDC_cb (usbd_msc_cdc_composite.c) dispatches the interfaces -> MSC goes through the Flash callbacks in usbd_storage.c and CDC goes through the subclass interrupt transfers -> disk and COM port work independently

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the composite class and enter the empty loop. main only configures the clock and registers the class; the transfers of both functions are entirely driven by interrupts and state machines.

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

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

    while (1)
    {
    }

3. Handle device state changes with the user callbacks. usbd_user.c uses USBD_User_cb_TypeDef to aggregate the reset, configured, suspend, and resume callbacks; all are empty in the example, and the application can insert its own handling here.

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,
};

Corresponding source files: src/main.c, src/usbd_user.c

5.30.3.9 MSC and HID composite device

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Device/MSC_HID_Composite/

Enable the USBHS clock and high-speed PHY→Register the MSC and HID composite class callbacks→The host enumerates a disk and an HID device→Disk access and key reporting run at the same time

Item Source analysis
Function USB MSC and HID composite device;
the same device provides a removable disk and a custom HID at the same time.
A key press reports its state over the HID endpoint.
Source files src/main.c: clock configuration, USBD_Init, key initialization, and the HID reporting loop;
src/usbd_msc_hid_core.c and inc/usbd_msc_hid_core.h: the composite class callbacks;
src/usbd_customhid_core.c and src/usbd_msc_core.c: the HID and MSC subclasses;
src/usbd_storage.c: the Flash storage callbacks;
src/usbd_desc.c and src/usbd_user.c: descriptors and user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
MSC and HID each occupy their own interface and endpoints.
Send_Buffer[2] and PrevXferDone are used for the HID report.
Program and data path main initializes the LEDs and keys, configures the clock, and registers USBD_MSC_HID_cb and USER_cb with USBD_Init;
when the device is configured and the previous report finished, the main loop reads the key and calls USBD_CUSTOM_HID_SendReport, while disk access still goes through the storage callbacks.
Main APIs USBD_Init, USBD_CUSTOM_HID_SendReport, STORAGE_Read, STORAGE_Write
Verification After download, a removable disk and an HID device appear together on the host;
disk access works and pressing a key changes the report in an HID viewer.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example exposes a disk and a custom HID on the same device, and the main loop only handles the key-triggered HID report.

Data path: on-board key -> KEY_Press_Status_Read (main) -> Send_Buffer -> USBD_CUSTOM_HID_SendReport (main) -> HID endpoint; host disk access goes through usbd_storage.c to the Flash

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the composite class and report the key state. main registers USBD_MSC_HID_cb with USBD_Init; when the device is configured and the previous report finished, the main loop sends the HID report.

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. Handle device state changes with the user callbacks. usbd_user.c uses USBD_User_cb_TypeDef to aggregate the reset, configured, suspend, and resume callbacks; all are empty in the example, and the application can insert its own handling here.

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,
};

Corresponding source files: src/main.c, src/usbd_user.c

5.30.3.10 CDC host

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/CDC/

Enable the USBHS clock and high-speed PHY→Register the CDC host class and user callbacks→Enumerate the attached virtual COM port device→The key menu selects sending a file or receiving data

Item Source analysis
Function USB host CDC application;
it enumerates the attached virtual COM port device and presents a menu for sending a file and receiving data on the serial terminal.
The menu is selected with the on-board keys.
Source files src/main.c: clock configuration, USBH_Init, and the host task loop;
src/usbh_user.c: USER_cb, the interface class checks, and the CDC menu state machine;
src/usbh_cdc_core.c and inc/usbh_cdc_core.h: the CDC host class;
src/usbh_fs.c: file read helper;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE at 16 MHz;
the class callback switches to CDC when a communication class interface is enumerated.
The log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, and registers USBH_CDC_cb and USER_cb with USBH_Init;
the main loop calls USBH_Main_Process to drive enumeration and the class task;
after enumeration USBH_USER_EnumerationDone points the receive callback at CDC_OutputData, and USBH_USER_Application polls the keys and runs CDC_Handle_Menu, which reads a file with Fs_Open and then calls CDC_SendData to send the menu.
Main APIs USBH_Init, USBH_Main_Process, USBH_CDC_cb, CDC_SendData, CDC_StartReception
Verification After download, open the serial port at 115200 and connect the CDC device to the OTG port;
when enumeration finishes, choose send or receive as prompted, and the file content is sent to the device or data is received from it.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example leaves the host CDC task to the class callbacks, and the user layer only handles the menu interaction on the serial terminal.

Data path: on-board key -> USBH_USER_UserInput (usbh_user.c) -> CDC_Handle_Menu -> CDC_SendData or CDC_StartReception -> USB endpoint -> attached serial device

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the host class and drive the task. main prints the startup message, initializes the keys, configures the clock, and registers the CDC class and user callbacks with USBH_Init; the main loop repeatedly runs 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. Print the class message after enumerating a CDC interface. USBH_USER_Configuration_DescAvailable prints the recognized class from the interface class, and a non-CDC device prints an unsupported message.

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

In the CDC branch, USBH_USER_Application calls CDC_DEMO_ProbeKey and CDC_Handle_Menu; replace the menu actions with your own transfer flow when porting.

Corresponding source files: src/main.c, src/usbh_user.c

5.30.3.11 CDC and MSC composite host

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/CDC_MSC/

Enable the USBHS clock and high-speed PHY→Register the CDC host class and user callbacks→Enumerate the attached device and identify its interface class→Switch to the MSC class or keep the CDC menu

Item Source analysis
Function USB host composite application;
after enumerating the attached device it selects virtual COM port or mass storage handling by interface class.
The CDC branch shows the transfer menu, and the MSC branch mounts the file system and reads and writes files.
Source files src/main.c: clock configuration, USBH_Init, and the host task loop;
src/usbh_user.c: USER_cb, the interface class checks, and the CDC and MSC state switching;
src/usbh_cdc_core.c and inc/usbh_cdc_core.h: the CDC host class;
inc/usbh_msc_core.h: the MSC host class interface;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE with a 16 MHz crystal;
main registers with the CDC class callback first, then switches by interface class after enumeration;
the log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, and registers USBH_CDC_cb and USER_cb with USBH_Init;
the main loop calls USBH_Main_Process to drive enumeration;
USBH_USER_Configuration_DescAvailable changes USB_Host.class_cb to USBH_MSC_cb and sets USB_HOST_USER_AppState = USH_USER_FS_INIT, or keeps CDC, by interface class;
the MSC branch enters file system access and the CDC branch runs the menu transfers.
Main APIs USBH_Init, USBH_Main_Process, USBH_CDC_cb, USBH_MSC_cb, USBH_USER_Configuration_DescAvailable
Verification After download, open the serial port at 115200, connect a USB flash drive to check file access, then connect a serial device to check the menu transfers;
both cases show the corresponding class message in the log.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example switches the class callback at enumeration time, so a single project can support both a virtual COM port and a USB flash drive.

Data path: attached device descriptor -> USBH_USER_Configuration_DescAvailable (usbh_user.c) -> set USB_Host.class_cb -> USBH_Main_Process dispatches to USBH_CDC_cb or USBH_MSC_cb -> menu transfers or file system access

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the host class and drive the task. main prints the startup message, initializes the keys, configures the clock, and registers the CDC class and user callbacks with USBH_Init; the main loop repeatedly runs 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. Switch the class callback by interface class. When USBH_USER_Configuration_DescAvailable finds a communication class interface it keeps the CDC menu, and when it finds 0x08 it switches to the MSC class and sets the application state to file system init.

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");
    }

The class registered in main is only the initial value; the actual class comes from the USBH_USER_Configuration_DescAvailable decision, so adjust the branches to the interface class of the target device when porting.

Corresponding source files: src/main.c, src/usbh_user.c

5.30.3.12 HID host

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/HID/

Enable the USBHS clock and high-speed PHY→Register the HID host class and user callbacks→Enumerate the attached mouse or keyboard→Parse the reports and print keys or update the position

Item Source analysis
Function USB host HID application;
it enumerates an attached mouse or keyboard and parses the input reports.
The mouse report updates and prints the position, and the keyboard report prints the character directly.
Source files src/main.c: clock configuration, USBH_Init, and the host task loop;
src/usbh_user.c: USER_cb, USER_MOUSE_Init, USER_MOUSE_ProcessData, USER_KEYBRD_Init, and USER_KEYBRD_ProcessData;
src/usbh_hid_core.c and inc/usbh_hid_core.h: the HID host class;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE with a 16 MHz crystal;
the report callback is registered by protocol after enumeration;
the log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, and registers USBH_HID_cb and USER_cb with USBH_Init;
the main loop calls USBH_Main_Process to drive enumeration;
after enumeration USBH_USER_EnumerationDone calls USER_MOUSE_Init to register the mouse input callback;
the class task parses the report, then USER_MOUSE_ProcessData updates the position and USER_KEYBRD_ProcessData prints the keyboard character.
Main APIs USBH_Init, USBH_Main_Process, USBH_HID_cb, USER_MOUSE_ProcessData, USER_KEYBRD_ProcessData
Verification After download, open the serial port at 115200 and connect a USB mouse or keyboard to the OTG port;
when enumeration finishes, moving the mouse shows coordinate logs and typing on the keyboard outputs characters.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example leaves the parsing of HID input reports to the user callbacks, and the project provides handling for both a mouse and a keyboard.

Data path: attached HID device -> USBH_HID_cb parses the report -> USER_MOUSE_ProcessData or USER_KEYBRD_ProcessData (usbh_user.c) -> update the position or print the character -> serial log

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the host class and drive the task. main prints the startup message, initializes the keys, configures the clock, and registers the HID class and user callbacks with USBH_Init; the main loop repeatedly runs 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. Parse the mouse report. USER_MOUSE_ProcessData updates the position when the movement is non-zero, then checks each button field bit by bit and calls the press or release handler.

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. Parse the keyboard report. USER_KEYBRD_ProcessData prints the character directly to the log; replace it with your own key handling when porting.

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

Corresponding source files: src/main.c, src/usbh_user.c

5.30.3.13 HID and CDC composite host

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/HID_CDC/

Enable the USBHS clock and high-speed PHY→Register the CDC host class and user callbacks→Enumerate the attached device and identify its interface class→Switch to the HID class or keep the CDC menu

Item Source analysis
Function USB host composite application;
after enumerating the attached device it selects virtual COM port or HID handling by interface class.
The CDC branch shows the transfer menu and the HID branch parses the mouse and keyboard reports.
Source files src/main.c: clock configuration, USBH_Init, and the host task loop;
src/usbh_user.c: USER_cb, the interface class checks, and the HID report handling;
src/usbh_cdc_core.c and inc/usbh_cdc_core.h: the CDC host class;
src/usbh_hid_core.c and inc/usbh_hid_core.h: the HID host class;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE with a 16 MHz crystal;
main registers with the CDC class callback first, then switches by interface class after enumeration;
the log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, and registers USBH_CDC_cb and USER_cb with USBH_Init;
the main loop calls USBH_Main_Process to drive enumeration;
USBH_USER_Configuration_DescAvailable keeps CDC for a communication class interface and switches to USBH_HID_cb for 0x03;
afterwards the HID branch parses reports and the CDC branch runs the menu transfers.
Main APIs USBH_Init, USBH_Main_Process, USBH_CDC_cb, USBH_HID_cb, USBH_USER_Configuration_DescAvailable
Verification After download, open the serial port at 115200, connect a serial device to check the menu transfers, then connect a mouse or keyboard to check report parsing;
both cases show the corresponding class message in the log.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example switches the class callback at enumeration time, so a single project can support both a virtual COM port and an HID input device.

Data path: attached device descriptor -> USBH_USER_Configuration_DescAvailable (usbh_user.c) -> set USB_Host.class_cb -> USBH_Main_Process dispatches to USBH_CDC_cb or USBH_HID_cb -> menu transfers or report parsing

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the host class and drive the task. main prints the startup message, initializes the keys, configures the clock, and registers the CDC class and user callbacks with USBH_Init; the main loop repeatedly runs 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. Switch the class callback by interface class. USBH_USER_Configuration_DescAvailable keeps the CDC menu for a communication class interface and switches to the HID class for 0x03.

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;
    }

The class registered in main is only the initial value; the actual class comes from the USBH_USER_Configuration_DescAvailable decision, so adjust the branches to the interface class of the target device when porting.

Corresponding source files: src/main.c, src/usbh_user.c

5.30.3.14 HID and MSC composite host

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/HID_MSC/

Enable the USBHS clock and high-speed PHY→Register the MSC host class and user callbacks→Enumerate the attached device and identify its interface class→Switch to the HID class or enter file system access

Item Source analysis
Function USB host composite application;
after enumerating the attached device it selects mass storage or HID handling by interface class.
The MSC branch mounts the file system and reads and writes files, and the HID branch parses the mouse and keyboard reports.
Source files src/main.c: clock configuration, USBH_Init, and the host task loop;
src/usbh_user.c: USER_cb, the interface class checks, and the file system state machine;
src/usbh_msc_core.c and inc/usbh_msc_core.h: the MSC host class;
src/usbh_hid_core.c and inc/usbh_hid_core.h: the HID host class;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE with a 16 MHz crystal;
main registers with the MSC class callback first, then switches by interface class after enumeration;
the log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, and registers USBH_MSC_cb and USER_cb with USBH_Init;
the main loop calls USBH_Main_Process to drive enumeration;
USBH_USER_Configuration_DescAvailable switches to USBH_MSC_cb and sets USB_HOST_USER_AppState = USH_USER_FS_INIT for 0x08, and switches to USBH_HID_cb for 0x03;
the MSC branch enters file system access and the HID branch parses reports.
Main APIs USBH_Init, USBH_Main_Process, USBH_MSC_cb, USBH_HID_cb, USBH_USER_Configuration_DescAvailable
Verification After download, open the serial port at 115200, connect a USB flash drive to check file access, then connect a mouse or keyboard to check report parsing;
both cases show the corresponding class message in the log.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example switches the class callback at enumeration time, so a single project can support both a USB flash drive and an HID input device.

Data path: attached device descriptor -> USBH_USER_Configuration_DescAvailable (usbh_user.c) -> set USB_Host.class_cb -> USBH_Main_Process dispatches to USBH_MSC_cb or USBH_HID_cb -> file system access or report parsing

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the host class and drive the task. main prints the startup message, initializes the keys, configures the clock, and registers the MSC class and user callbacks with USBH_Init; the main loop repeatedly runs 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. Switch the class callback by interface class. USBH_USER_Configuration_DescAvailable keeps MSC and sets the file system initial state for 0x08, and switches to the HID class for 0x03.

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;
    }

The class registered in main is only the initial value; the actual class comes from the USBH_USER_Configuration_DescAvailable decision, so adjust the branches to the interface class of the target device when porting.

Corresponding source files: src/main.c, src/usbh_user.c

5.30.3.15 MSC host

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Host/MSC/

Enable the USBHS clock and high-speed PHY→Register the MSC host class and user callbacks→Enumerate the attached USB flash drive→Mount the file system and read, write, or browse files

Item Source analysis
Function USB host mass storage application;
it enumerates an attached USB flash drive and mounts FatFs.
It reads and writes N32.TXT and prints the capacity and directory contents to the log.
Source files src/main.c: clock configuration, USBH_Init, and the host task loop;
src/usbh_user.c: USER_cb, the USBH_USER_MSC_App file system state machine, and Explore_Disk;
src/usbh_msc_core.c and inc/usbh_msc_core.h: the MSC host class;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE with a 16 MHz crystal;
the fatfs file system object is allocated statically;
the log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, and registers USBH_MSC_cb and USER_cb with USBH_Init;
the main loop calls USBH_Main_Process to drive enumeration;
after enumeration it enters USBH_USER_MSC_App, which mounts the file system and reads the capacity, calls Explore_Disk to print the directory, and then creates and writes N32.TXT with f_open;
USBH_USER_DeInit resets the application state to USH_USER_FS_INIT.
Main APIs USBH_Init, USBH_Main_Process, USBH_MSC_cb, f_mount, f_open, f_write
Verification After download, open the serial port at 115200 and connect a USB flash drive to the OTG port;
when enumeration finishes the capacity information and write result appear, and N32.TXT appears in the flash drive root directory.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example leaves the file operations on the USB flash drive to FatFs, and the user-layer state machine handles the timing of mount, write, and browse.

Data path: attached USB flash drive -> USBH_MSC_cb -> USBH_USER_MSC_App (usbh_user.c) -> FatFs f_mount, f_open, f_write -> USBH_MSC_cb reads and writes sectors -> USB flash drive

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Register the host class and drive the task. main prints the startup message, initializes the keys, configures the clock, and registers the MSC class and user callbacks with USBH_Init; the main loop repeatedly runs 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. Mount the file system and read the capacity. USBH_USER_MSC_App calls f_mount in the file system init state, prints an error and returns on failure, and prints the capacity on success.

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. Create and write the file. After prompting a key press, the write state opens N32.TXT with FA_CREATE_ALWAYS | FA_WRITE and calls f_write to write out the buffer content.

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);

After writing, the state becomes USH_USER_FS_IDLE; directory browsing is done by the USH_USER_FS_READLIST branch through Explore_Disk, and USBH_USER_DeInit resets the application state to USH_USER_FS_INIT.

Corresponding source files: src/main.c, src/usbh_user.c

5.30.3.16 Dual-role switching

Example directory: projects/n32h49x_EVAL/examples/USBHS/USBHS_Dual_Role/Dual_Role/

Enable the USBHS clock and high-speed PHY→Initialize the dual-role state machine and register the host class→The key selects the host, device, or idle role→The main loop dispatches tasks for the current role

Item Source analysis
Function USB dual-role application;
the keys switch between the host, device, and idle roles.
The host role runs the USB flash drive file system and the device role presents as an MSC device.
Source files src/main.c: clock configuration, DRD_Init, and the main loop;
src/dual_role_app.c: DRD_Init, DRD_Process, the DRD_Application state machine, and the menus;
src/usbh_user.c: host user callbacks;
src/usbd_user.c: device user callbacks;
readme.txt, MDK-ARM/, and EWARM/.
Key configuration USBHS clock source is HSE with a 16 MHz crystal;
the host path registers USBH_MSC_cb and USER_Host_MSC_cb, and the device path registers USBD_MSC_cb and USER_Device_MSC_cb;
the log is output from USART1 at 115200 baud.
Program and data path main initializes the log and keys, configures the clock, calls DRD_Init to show the menu and register the host class, and then enters the DRD_Process main loop;
in the host role DRD_Process polls the connection and drives the task with USBH_Main_Process, and the other roles are handled by DRD_Application;
DRD_Application enters branches such as DRD_HOST_IDLE according to drd.state and its sub-state and completes the role switch by re-running USBH_Init or USBD_Init.
Main APIs DRD_Init, DRD_Process, USBH_Init, USBD_Init, HOST_IsDeviceConnected
Verification After download, open the serial port at 115200 and use the keys as prompted to select the host role and connect a USB flash drive to check file access;
then switch to the device role and connect to a PC to check that it is recognized as a removable disk.
Use note The USBHS clock requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
to switch between full-speed and high-speed mode, modify the USE_USB_HS_IN_FS and USE_USB_HS_IN_HS macros.
Core-code walkthrough and porting

This example uses one state machine to carry both the host and the device paths, re-initializing the corresponding stack on a role switch.

Data path: on-board key -> DRD_ProbeKey -> drd.state / drd.Host_state (dual_role_app.c) -> DRD_Application branch -> USBH_Init or USBD_Init -> host USBH_Main_Process or device MSC task

1. Configure the USBHS clock and high-speed PHY. USBHS_ConfigCLK selects HSE as the clock source, enables the USBHS and PHY clocks, and writes the PHY transmit and receive timing.

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. Initialize the dual role and enter the main loop. main prints the startup message, initializes the keys, configures the clock, calls DRD_Init to set up the default role, and the main loop repeatedly runs 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. Initialize the state machine and register the host class. DRD_Init refreshes the menu first, then initializes the USB BSP and the host stack, starting with the MSC class by default.

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. Dispatch tasks by role. DRD_Process first checks the device connection and drives the host task in the host role, then always calls DRD_Application to handle the menu and role switching.

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();
}

In the sub-state branches of DRD_Application, the host idle state re-runs USBH_Init to enable the high-speed host and the device idle state calls USBD_Init to attach the MSC device; replace the class callbacks and initialization arguments for the target role when porting.

Corresponding source files: src/main.c, src/dual_role_app.c

5.30.4 Notes and troubleshooting

Scenario Check & action Source
External crystal frequency The USBHS clock comes from HSE and requires a 16 MHz, 19.2 MHz, 20 MHz, 24 MHz, 26 MHz, or 32 MHz external crystal;
outside this range the PHY cannot establish a stable 480 Mbps transmit and receive clock.
User manual
VBUS supply USBHS cannot output the 5 V VBUS voltage by itself;
when used as a host to power a downstream device, add an external charge pump or power switch and drive it through the PPWR bit of HPCS.
User manual
Bus reset duration When a USB reset is triggered, the PRST bit must be held long enough;
at least 50 ms in high-speed mode and at least 10 ms in full-speed and low-speed mode.
User manual
Mode switch wait After switching FDMODE or FHMODE, wait at least 25 ms before accessing the PHY, otherwise the read and write results are unstable. User manual
FIFO and endpoint configuration After a transmit or receive FIFO flush (TXFFLSH, RXFFLSH), wait for the hardware to clear the bits automatically before the next step;
the receive FIFO must reserve 10 words for SETUP transactions.
User manual
Interrupt handling order After reading DAINT or HACHINT, first determine the interrupt source, then clear the specific interrupt flag to avoid missing a handler. User manual
Error recovery and remote wakeup When ERERRF occurs, recovery is only possible through a soft disconnect (SFTDIS);
after a remote wakeup, clear the RMWKUP bit within 1 to 15 ms.
User manual
Nations Technologies DevKit Docs

Datasheets, SDK, driver APIs and demo guides for Nations Technologies MCU development kits, organized by chip series.

© 2026 Nations Technologies Inc. All rights reserved.粤ICP备11069485号This site uses cookie-free anonymous analytics.