How to implement the USB device composite class USB DFU + HID
Introduction
This article is a step-by-step guide on how to implement an STM32 application as a USB device. This device should combine human interface device (HID) and device firmware upgrade (DFU) functionalities into a composite USB device. This allows the device to function as both an HID (for example, a mouse in our case) and a DFU device for firmware updates. We’re using the STM32 legacy library. Additionally, tips are provided to make it work on Windows operating systems.
Prerequisites
1. Project setup
You can directly clone the project from STM32-hotspot/CKB-STM32-HID-DFU-H5 or follow the steps below to set your project.
1.1 Create a project
Start by creating a new project using the STM32CubeIDE by clicking [File → New → STM32 Project]. Use the [Board Selector] tab and select the [NUCLEO-H563ZI].

Under the board project options, you can keep the USER BUTTON configuration.
Once the project creation is done, go into [Connectivity] section and enable the USB peripheral [USB_OTG_FS] peripheral in [Device Only mode].
Under [NVIC Settings], enable USB FS global interrupt.

Then, under [Code Generator], copy only the necessary libraries files into the project folder.

Under [System Core], under [RCC], HSE oscillators is configured in Bypass mode.

The instruction cache ICACHE should be disabled in our project.

The last step before generating the code is to update the system clock frequency, the USB peripheral is fed with an HSI48. So, activation of CRS Sync Source USB is highly recommended.

Due to USB data rate and packet memory interface requirements, the APB2 clock must have a minimum frequency of 12 MHz to avoid data overrun/underrun problems.
1.2 Develop your own project
After generating the code, add composite builder class, DFU class, and HID class source files manually to the project. Since the H5 product is native to AzureRTOS, CubeMX may not generate middleware layer automatically. In the following table, you’ll find the necessary middleware files if they don’t already exist:
| File name | Source | Destination |
| usbd_core.h | Core/Inc | CKB-STM32-HID-DFU-H5/Middlewares/Core /Inc/ |
| usbd_ctlreq.h | ||
| usbd_ioreq.h | ||
| usbd_core.c | Core/Src | CKB-STM32-HID-DFU-H5/Middlewares/Core/Src/ |
| usbd_ctlreq.c | ||
| usbd_ioreq.c | ||
| usbd_hid.h | Class/HID | CKB-STM32-HID-DFU-H5/Middlewares/Class |
| usbd_hid.c | ||
| usbd_dfu.h | Class/DFU | |
| usbd_dfu.c | ||
| usbd_composite_builder.h | Class/CompositeBuilder | |
| usbd_composite_builder.c |
Regarding source files, you can copy following template files and edit them
| File name | Source | Destination |
| usbd_def.h | usbd_def.h | CKB-STM32-HID-DFU-H5/USB Device/App |
| usbd_desc.h | usbd_desc_template.h | |
| usbd_desc.c | usbd_desc_template.c | |
| usbd_conf.h | usbd_conf_template.h | CKB-STM32-HID-DFU-H5/USB Device/Target |
| usbd_conf.c | usbd_conf_template.c |
Under [Properties → C/C++ General → Paths and Symbols], ensure to add related file source under [Source Location].
Under [Includes], add middleware header files.

Now, you should be able to compile without issues.
2. Code
Next step to facilitate the use of this article, you can simply replace the existent USB Device folder including App and Target with the one in the project on GitHub.
Scroll down to the definition of MX_USB_PCD_Init() function and you can find the following code already generated to initialize the USB peripheral.
void MX_USB_PCD_Init(void)
{
/* USER CODE BEGIN USB_Init 0 */
/* USER CODE END USB_Init 0 */
/* USER CODE BEGIN USB_Init 1 */
/* USER CODE END USB_Init 1 */
hpcd_USB_DRD_FS.Instance = USB_DRD_FS;
hpcd_USB_DRD_FS.Init.dev_endpoints = 8;
hpcd_USB_DRD_FS.Init.speed = USBD_FS_SPEED;
hpcd_USB_DRD_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd_USB_DRD_FS.Init.Sof_enable = DISABLE;
hpcd_USB_DRD_FS.Init.low_power_enable = DISABLE;
hpcd_USB_DRD_FS.Init.lpm_enable = DISABLE;
hpcd_USB_DRD_FS.Init.battery_charging_enable = DISABLE;
hpcd_USB_DRD_FS.Init.vbus_sensing_enable = DISABLE;
hpcd_USB_DRD_FS.Init.bulk_doublebuffer_enable = DISABLE;
hpcd_USB_DRD_FS.Init.iso_singlebuffer_enable = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_DRD_FS) != HAL_OK)
{
Error_Handler();
}
}
2.1 Main application
In the main loop, between /* USER CODE BEGIN 2 */ and /* USER CODE END2 */, you can insert the following code:
/* Initialize the USB Device Library */
if(USBD_Init(&hUsbDeviceFS, &Class_Desc, 0) != USBD_OK)
Error_Handler();
/* Store the DFU Class */
DFU_InstID = hUsbDeviceFS.classId;
/* Register the DFU Class */
if(USBD_RegisterClassComposite(&hUsbDeviceFS, USBD_DFU_CLASS, CLASS_TYPE_DFU, NULL) != USBD_OK)
Error_Handler();
/* Store HID Instance Class ID */
HID_InstID = hUsbDeviceFS.classId;
if(USBD_RegisterClassComposite(&hUsbDeviceFS, USBD_HID_CLASS, CLASS_TYPE_HID, &HID_EpAdd_Inst) != USBD_OK)
Error_Handler();
USBD_CMPSIT_SetClassID(&hUsbDeviceFS, CLASS_TYPE_DFU, 0);
/* Add DFU Interface Class */
USBD_DFU_RegisterMedia(&hUsbDeviceFS, &USBD_DFU_Flash_fops);
if(USBD_Start(&hUsbDeviceFS) != USBD_OK)
Error_Handler();2.2 Implement HID functionality
- Set the HID endpoint address and size in the usbd_conf.h file.
#define HID_EPIN_ADDR 0x81U
#define HID_EPIN_SIZE 0x04U // HID_EPIN_SIZE 4 bytes for simple HID device- Define the HID report buffer in main.c:












