Skip to main content
Associate III
September 15, 2026
Solved

Using DFU and MSC

  • September 15, 2026
  • 16 replies
  • 118 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

Best answer by TDK

Since PB10 is an output pin for the bootloader, a pulldown resistor isn’t going to help here. You will need to change the hardware or use a custom bootloader.

16 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

Associate III
September 16, 2026

Okay, 

So I implemented to code from the forum post.
With boot0 high.
And I did some pathing, VBUS is now being attached to the USB 5V.
 

Hse is connected, the thing I’m investigating now is, do I need to do the clock configuration and dfu execution or not.
In the afternoon, I’ll look into this.

Kind regards, 

William

 

TDK
September 16, 2026

To use the USB DFU bootloader in system memory, you need to jump to the bootloader from an as-reset state. Nothing initialized. The bootloader detects the USB connection and will use it. You are looking at a flowchart for what the bootloader does.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Associate III
September 16, 2026

Hi, 
Ah okay, I misinterpreted.

Something weird is happening.
Right now the controller has to apply 3V3 to a on/off circuit, there is a internal and external pull down to gnd.
Its a battery powered device and that way it can turn itself off.

But when I reach this part:

    /* De-initialize GPIO */
//make sure this pin is low
HAL_GPIO_WritePin(MCU_KEEPALIVE_GPIO_Port, MCU_KEEPALIVE_Pin, GPIO_PIN_RESET);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_All);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_All);
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_All);
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_All);
HAL_GPIO_DeInit(GPIOH, GPIO_PIN_All);

The 3V3 shuts off.
Even if I comment these lines out and it executes:

		/* Call the function to jump to boot loader location */
SysMemBootJump();

It still turns off the 3V3.

So right now I’m gone attach a scope and investigate this.
But I’ll update the post when I get some more results

Kind regards, 

William

TDK
September 16, 2026

If 3V3 is being applied via a GPIO output, you (in general) can’t rely on that behavior to remain within the bootloader. It only does what it does. If it’s being applied on a pin that you’re deinitializing, that would explain why it drops.

You can view AN2606 to see the pins the bootloader explicitly initializes. But other pins may also be touched.

"If you feel a post has answered your question, please click ""Accept as Solution""."
TDK
TDKBest answer
September 16, 2026

Since PB10 is an output pin for the bootloader, a pulldown resistor isn’t going to help here. You will need to change the hardware or use a custom bootloader.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Associate III
September 18, 2026

Hey,

Sorry for the late answer.
I swapped the pins to one that is not used by the bootloader and I was able to perform a firmware update.

Thank you very much for all the help.

 

Kind regards, 

 

William