Skip to main content
Graduate II
September 8, 2025
Question

Struggling with custom bootloader on STM32L433 (only simple applications work)

  • September 8, 2025
  • 3 replies
  • 764 views

I have written a custom bootloader that is running on a Nucleo-L433RC-P board. 

The application uses USB CDC to call the bootloader and the bootloader uses USB DFU to download the application. 

I wanted to use USB to call the custom bootloader, rather than having to use a physical switch on the BOOT0 pin to call the internal bootloader

I can successfully download a simple application which just flashes an LED using the USB DFU bootloader.

However, when I download my actual project, it appears to download successfully but does not run.

My actual project is more complicated and uses the following peripherals:

  • RTC
  • SPI2 and DAC1 (using DMA)
  • SPI1
  • TIM6
  • TIM7
  • I2C1
  • I2C2
  • USB
  • Shutdown and standby using WKUP and RTC wake sources

 I used the following guide to write the custom bootloader and simple application...

The following web tool is used to download the application...

I have spent days trying to debug why my actual project will not work, yet the simple application does. 

Now I am at a complete loss and don't know how to proceed.

The implementation is shown below.

Bootloader USB configuration...

freeflyer_0-1757342267651.png

Changes to bootloader linker script...

freeflyer_1-1757342699168.png

Bootloader main.c...

#include "main.h"
#include "usb_device.h"
typedef void (*pFunction)(void);
#define DFU_BOOT_FLAG 0xDEADBEEF
RTC_HandleTypeDef hrtc;
uint32_t dfu_boot_flag;
pFunction JumpToApplication;
uint32_t JumpAddress;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_RTC_Init(void);

int main(void)
{
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_USB_DEVICE_Init();
	MX_RTC_Init();
	dfu_boot_flag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
	if (dfu_boot_flag != DFU_BOOT_FLAG) 
	{
		if (((*(__IO uint32_t*) USBD_DFU_APP_DEFAULT_ADD) & 0x2FFC0000) == 0x20000000) 
		{
			JumpAddress = *(__IO uint32_t*) (USBD_DFU_APP_DEFAULT_ADD + 4);
			JumpToApplication = (pFunction) JumpAddress;
			__set_MSP(*(__IO uint32_t*) USBD_DFU_APP_DEFAULT_ADD);
			JumpToApplication();
		}
	}
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, 0); // So next boot won't be affected
	uint32_t now = 0, next_blink = 100;
	while (1) 
	{
		now = uwTick;
		if (now >= next_blink) 
		{
			HAL_GPIO_TogglePin(STATUS_LED_GPIO_Port, STATUS_LED_Pin);
			next_blink = now + 100;
		}
	}
}

 

Changes to bootloader usbd_dfu_if.c.... 

freeflyer_2-1757342753713.png

freeflyer_3-1757342784711.png

freeflyer_4-1757342827484.png

freeflyer_5-1757342878065.png

freeflyer_6-1757342899543.png

freeflyer_7-1757342919571.png

freeflyer_8-1757342938396.png

freeflyer_9-1757342965108.png

 

Changes to bootloader usbd_dfu_if.h.... 

freeflyer_10-1757342998876.png

 

Changes to application linker script...

freeflyer_11-1757343040925.png

 

Simple application main.c...

 

#include "main.h"
#include "usb_device.h"

#define DFU_BOOT_FLAG 0xDEADBEEF
#define DFU_BOOT_REQ 0xAA

RTC_HandleTypeDef hrtc;uint32_t *dfu_boot_flag;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_RTC_Init(void);

int main(void)
{
	uint32_t now = 0, next_blink = 1000;
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_USB_DEVICE_Init();
	MX_RTC_Init();
	HAL_PWR_EnableBkUpAccess();

	while (1) 
	{
		now = uwTick;
		if (now >= next_blink) 
		{
		HAL_GPIO_TogglePin(STATUS_LED_GPIO_Port, STATUS_LED_Pin);
		next_blink = now + 1000;
		}
	}
}

#define USB_RX_BUF_SIZE 512
volatile uint8_t usb_rx_buffer[USB_RX_BUF_SIZE];
volatile uint32_t usb_rx_count = 0; // number of bytes in buffer
void USB_CDC_RxHandler(uint8_t* Buf, uint32_t Len)
{
	if ((usb_rx_count + Len) < USB_RX_BUF_SIZE) 
	{
		memcpy((uint8_t *)&usb_rx_buffer[usb_rx_count], Buf, Len);
		usb_rx_count += Len;
	}
	if (usb_rx_buffer[0] == DFU_BOOT_REQ)
	{
		HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, DFU_BOOT_FLAG);
		HAL_NVIC_SystemReset();
	}
}

 

Changes to application system_stm32l4xx.c...

freeflyer_12-1757343081312.png

Changes to application usbd_cdc_if.c...

freeflyer_13-1757343139374.png

 

 

 

    This topic has been closed for replies.

    3 replies

    Super User
    September 8, 2025

    I think you should reconsider using the internal bootloader. It removes so much code complexity.

    You can jump to it directly, rather than reconfiguring any BOOT0 pins. Is there another reason why it can't work?

    How to jump to system bootloader from application ... - STMicroelectronics Community

    freeflyerAuthor
    Graduate II
    September 8, 2025

    Thanks TDK

    I was wondering if I could call the internal bootloader using USB

    Looking at the responses you linked, there appears to be a lot of errors and issues so it still does not seem straight forward, but Ill give it a go.

    One question though, can you only use STM32CubeProgrammer when using the internal bootloader ?

    Or can any USB DFU tool be used, like the web version I was using ?

     

    Super User
    September 8, 2025

    > Looking at the responses you linked

    Don't let the responses *** you. It costs nothing to say something doesn't work and provide no evidence. The most common error (apart from not using the posted code) is that the chip needs to be in an as-reset state.

    If you find something isn't working, post enough details and it will get sorted out.

    > One question though, can you only use STM32CubeProgrammer when using the internal bootloader?

    > Or can any USB DFU tool be used, like the web version I was using ?

    You can use any tool that adheres to the DFU protocol. The two sides don't know what's on the other end but they need to speak the same language.

    USB DFU protocol used in the STM32 bootloader - Application note

     

    Edit: I just noticed the word f o o l is censored. This seems a bit much. I would even call it foolish.

    Edit2: ***

    Explorer
    September 8, 2025

    Does your bootloader relocate the VTOR after the jump?

    If you do not modify the startup_stm32xxxx_init.c file or, as I always do, place SCB->VTOR = to the address of the app in memory, any try of using interrupts would fail! 

     

    I see you indeed modify the startup file but for sake I would also add the  SCB->VTOR = xxx before the call to HAL_Init just to be super sure.

    Super User
    September 9, 2025
    void USB_CDC_RxHandler(uint8_t* Buf, uint32_t Len)
    {
    	// accumulate incoming USB data into the buffer
    	if ((usb_rx_count + Len) < USB_RX_BUF_SIZE) {
    		memcpy((uint8_t *)&usb_rx_buffer[usb_rx_count], Buf, Len);
    		usb_rx_count += Len;
    	}
    	if (usb_rx_buffer[0] == DFU_BOOT_REQ)
    	{
    		JumpToBootloader();

    You can't jump to the bootloader from within an IRQ context. That IRQ will not be able to fire again and pre-empt itself. The USB bootloader relies on USB interrupts to function correctly.

    Instead, set a flag in the IRQ handler and jump to the bootloader in the main thread.

    freeflyerAuthor
    Graduate II
    September 9, 2025

    Thanks TDK, I did as you suggested and now the USB DFU device appears when the bootloader is called

    	while (1) {
    		now = uwTick;
    
    		if (now >= next_blink) {
    			HAL_GPIO_TogglePin(STATUS_LED_GPIO_Port, STATUS_LED_Pin);
    			next_blink = now + 100;
    		}
    
    		if (flgReqBootloader)
    		{
    			flgReqBootloader = 0;
    			JumpToBootloader();
    		}

     

    void USB_CDC_RxHandler(uint8_t* Buf, uint32_t Len)
    {
    	// accumulate incoming USB data into the buffer
    	if ((usb_rx_count + Len) < USB_RX_BUF_SIZE) {
    		memcpy((uint8_t *)&usb_rx_buffer[usb_rx_count], Buf, Len);
    		usb_rx_count += Len;
    	}
    	if (usb_rx_buffer[0] == DFU_BOOT_REQ)
    	{
    		flgReqBootloader = 1;
    	}
    }

     

    However, when I try to flash a simple application .bin file (that just flashes an LED) I get the following errors...

     

    freeflyer_0-1757442446322.png

    The log shows the following...

     

    19:21:21 : STM32CubeProgrammer API v2.20.0 | MacOS-64Bits
    19:21:34 : USB speed : Full Speed (12MBit/s)
    19:21:34 : Manuf. ID : STMicroelectronics
    19:21:34 : Product ID : STM32 BOOTLOADER
    19:21:34 : SN : 207C30AC5533
    19:21:34 : DFU protocol: 1.1
    19:21:34 : Board : --
    19:21:34 : Device ID : 0x0435
    19:21:34 : UPLOADING OPTION BYTES DATA ...
    19:21:34 : Bank : 0x00
    19:21:34 : Address : 0x1fff7800
    19:21:34 : Size : 36 Bytes
    19:21:34 : UPLOADING ...
    19:21:34 : Size : 1024 Bytes
    19:21:34 : Address : 0x8000000
    19:21:34 : Read progress:
    19:21:34 : Data read successfully
    19:21:34 : Time elapsed during the read operation is: 00:00:00.031
    19:22:00 : Opening and parsing file: STApplication.bin
    19:22:00 : Memory Programming ...
    19:22:00 : File : STApplication.bin
    19:22:00 : Size : 6.13 KB
    19:22:00 : Address : 0x08000000
    19:22:00 : Download in Progress:
    19:22:00 : File download complete
    19:22:01 : Time elapsed during download operation: 00:00:00.086
    19:22:01 : Verifying...
    19:22:01 : Read progress:
    19:22:01 : Error: Data mismatch found at address 0x08000004 (byte = 0x1D instead of 0x0D)
    19:22:01 : Time elapsed during verifying operation: 00:00:00.057
    19:22:01 : Error: Download verification failed
    19:22:01 : RUNNING Program ...
    19:22:01 : Address: : 0x08000000
    19:22:01 : Start operation achieved successfully
    19:22:01 : Warning: Connection to device 0x435 is lost
    19:22:01 : Disconnected from device.

     

     

    UPDATE;

    Unticking "Skip flash erase before porgramming" fixed it...

    freeflyer_1-1757442842844.png

     

    So the simple application seems to work, now I need to implement it in my actual more complicated project.