Skip to main content
Associate III
September 15, 2026
Question

Using DFU and MSC

  • September 15, 2026
  • 5 replies
  • 47 views

Hello there, 

I’m working on a device that in its normal operation uses the MSC.

But I want my clients to also be able to update the device via USB using DFU.

#define USBD_PID_FS     57105
#define USBD_PRODUCT_STRING_FS     "STM32 DownLoad Firmware Update"
#define USBD_CONFIGURATION_STRING_FS     "DFU Config"
#define USBD_INTERFACE_STRING_FS     "DFU Interface"

#define USBD_PID_FS     22314
#define USBD_PRODUCT_STRING_FS     "STM32 Mass Storage"
#define USBD_CONFIGURATION_STRING_FS     "MSC Config"
#define USBD_INTERFACE_STRING_FS     "MSC Interface"



I found these differences and made a work around, CubeProgrammer is detecting a DFU device when a button is pressed:

void MX_USB_DEVICE_Init(void)
{
/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
//check to enter DFU mode
if(HAL_GPIO_ReadPin(USR_BUT_ENTER_GPIO_Port, USR_BUT_ENTER_Pin) == GPIO_PIN_RESET){
if (USBD_Init(&hUsbDeviceFS, &DFU_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_DFU) != USBD_OK)
{
Error_Handler();
}
if (USBD_DFU_RegisterMedia(&hUsbDeviceFS, &USBD_DFU_fops_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}

return;
}
/* USER CODE END USB_DEVICE_Init_PreTreatment */

/* Init Device Library, add supported class and start the library. */
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC) != USBD_OK)
{
Error_Handler();
}
if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}

/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */

/* USER CODE END USB_DEVICE_Init_PostTreatment */
}

 

And I smashed together a implementation like this:

/*
* usbd_desc_dfu.c
*
* Created on: Sep 15, 2026
* Author: william.vanraemdonck
*/

#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_conf.h"

#define USBD_LANGID_STRING 1033
#define USBD_MANUFACTURER_STRING "STMicroelectronics"


#define DFU_VID 0x0483
#define DFU_PID 0xDF11
#define DFU_PRODUCT_STR "STM32 DownLoad Firmware Update"
#define DFU_CONFIG_STR "DFU Config"
#define DFU_INTERFACE_STR "DFU Interface"
#define DFU_MANUF_STR USBD_MANUFACTURER_STRING
#define DFU_LANGID USBD_LANGID_STRING

static uint8_t *DFU_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
static uint8_t *DFU_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
static uint8_t *DFU_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
static uint8_t *DFU_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
static uint8_t *DFU_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
static uint8_t *DFU_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
static uint8_t *DFU_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);

USBD_DescriptorsTypeDef DFU_Desc = {
DFU_DeviceDescriptor,
DFU_LangIDStrDescriptor,
DFU_ManufacturerStrDescriptor,
DFU_ProductStrDescriptor,
DFU_SerialStrDescriptor,
DFU_ConfigStrDescriptor,
DFU_InterfaceStrDescriptor
};

__ALIGN_BEGIN static uint8_t DFU_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = {
0x12, USB_DESC_TYPE_DEVICE,
0x00, 0x02, /* bcdUSB 2.00 */
0x00, 0x00, 0x00, /* class/subclass/protocol: per-interface */
USB_MAX_EP0_SIZE,
LOBYTE(DFU_VID), HIBYTE(DFU_VID),
LOBYTE(DFU_PID), HIBYTE(DFU_PID),
0x00, 0x02, /* bcdDevice 2.00 */
USBD_IDX_MFC_STR, USBD_IDX_PRODUCT_STR, USBD_IDX_SERIAL_STR,
USBD_MAX_NUM_CONFIGURATION
};

__ALIGN_BEGIN static uint8_t DFU_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = {
USB_LEN_LANGID_STR_DESC, USB_DESC_TYPE_STRING,
LOBYTE(DFU_LANGID), HIBYTE(DFU_LANGID)
};

__ALIGN_BEGIN static uint8_t DFU_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END;

static uint8_t *DFU_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ UNUSED(speed); *length = sizeof(DFU_DeviceDesc); return DFU_DeviceDesc; }

static uint8_t *DFU_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ UNUSED(speed); *length = sizeof(DFU_LangIDDesc); return DFU_LangIDDesc; }

static uint8_t *DFU_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ UNUSED(speed); USBD_GetString((uint8_t *)DFU_MANUF_STR, DFU_StrDesc, length); return DFU_StrDesc; }

static uint8_t *DFU_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ UNUSED(speed); USBD_GetString((uint8_t *)DFU_PRODUCT_STR, DFU_StrDesc, length); return DFU_StrDesc; }

static uint8_t *DFU_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ UNUSED(speed); USBD_GetString((uint8_t *)DFU_CONFIG_STR, DFU_StrDesc, length); return DFU_StrDesc; }

static uint8_t *DFU_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ UNUSED(speed); USBD_GetString((uint8_t *)DFU_INTERFACE_STR, DFU_StrDesc, length); return DFU_StrDesc; }

static uint8_t *DFU_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
/* reuse the CubeMX one so the serial stays consistent */
return FS_Desc.GetSerialStrDescriptor(speed, length);
}

Now when I try programming the device over DFU it says in the terminal from cube programmer:
Error: Data read failed
And the loading bar just goes back and forward but staying at 0%.

Is there anything else I have look over or should still do?

Kind regards, 

William

5 replies

MM..1
Super User
September 15, 2026

DFU isnt so simple, but is implemeted in system memory bootloader, then you only require run it from app and read AN2606

Associate III
September 15, 2026

Hi, 
Oh I didn’t see that app note. I’ll get to reading.

From what I understood, to update Boot0 will have to be high on restart or power up.
Would it matter it it came online at the same time as 3V3 were the MCU is powered from.

Kind regards, 

William

 

TDK
September 15, 2026

An easier method is to jump to the system bootloader directly from user code when required, rather than setting BOOT0 and restarting. You will likely need to deinitialize USB before the jump.

 

"If you feel a post has answered your question, please click ""Accept as Solution""."
ST Technical Moderator
September 15, 2026

Hi ​@William_RnD 

You should provide more details about the STM32 device you are using, because some parts do not support USB DFU in the system bootloader. Also, please be careful if your application remaps pins, since the bootloader may not use the same pin routing. It is therefore important to check AN2606 for the exact bootloader support and pin requirements for your device. Providing more context about your application and board will help us give you a more accurate answer.

If there are no such constraints, then you may simply jump to the system memory bootloader directly from the application, as suggested by TDK.

To give better visibility on the answered topics, please click on "Best answer" on the reply which solved your issue or answered your question.Best regards,FBL
Associate III
September 16, 2026

Hello everyone, 

I’m using the STM32F401RCT
I checked the AN2606:
 

My USB_OTG_FS_VBUS pin is being used for a different purpose.
Before I start messing with the boot0 pin.

I’ll try the guide mentioned by TDK first.
How to jump to system bootloader from application code on STM32 microcontrollers | Community

I’ll post an update when I worked my way through it with my findings.
I do have a screen and user IO so I think I could find a way to implement a update sequence or some kind.

Kind regards, 

William