cancel
Showing results for 
Search instead for 
Did you mean: 

[Tutorial] Restart STM32F in DFU mode

EKoec
Associate II

There are some older posts on the net about how to achieve a reboot in DFU mode but none of them worked for me. So I want to share my solution on how to add this functionality to your embedded firmware. First off all, I am using CUBEIDE with a STM32F412, but this method should work with all controllers. What you need:

1) The memory address of the bootloader of your MCU

2) The start address of your RAM

3) Some magic number of your choice (my favorites are 0xFEEDFEED, 0xDECAFBAD, 0xDEADBEEF, ...)

1) and 2) can be found in the AN2606 on the ST page. For my controller the relevant part looks like this:

Screenshot_2024-02-15_11-14-31.png

 

So the address of the bootloader is 0x1FFF000 and the start address of the RAM is 0x20000000.

Now add a function to your source code that sets the magic number you chose and resets the controller. It could look something like this:

 

void reboot_into_dfu()
{
  *((uint32_t *)0x20000000) = 0xDECAFBAD;

  NVIC_SystemReset();
}

 

Replace 0x2000000 with the RAM start address of your controller and 0xDECAFBAD with the magic number of your choice.

The next step is to modify the startup procedure. For this you have to open the file startup_stm32xxx.s (xxx referring to the name of your controller), which is located at Project > Core > Startup (as of CubeIDE v1.14, 2024). There you will find some Assembly cod, that should look something like this:

 

[...]
Reset_Handler:  
  ldr   sp, =_estack       /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */
  ldr r0, =_sdata
  ldr r1, =_edata
  ldr r2, =_sidata
  movs r3, #0
  b LoopCopyDataInit

CopyDataInit:
  ldr r4, [r2, r3]
  str r4, [r0, r3]
  adds r3, r3, #4

LoopCopyDataInit:
  adds r4, r0, r3
  cmp r4, r1
  bcc CopyDataInit
[...]

 

Now we will add a check if the magic number is set right after the Reset_Handler: label and jump to the DFU bootloader, if so. The code then looks like this:

 

[...]
Reset_Handler:  
/* begin check for flag */
  ldr r0, =0x20000000
  ldr r1, =0xDECAFBAD
  ldr r2, [r0, #0]
  str r0, [r0, #0]
  cmp r2, r1
  beq Reboot_Loader
/* end check for flag */

/* Copy the data segment initializers from flash to SRAM */
  ldr   sp, =_estack       /* set stack pointer */
  ldr r0, =_sdata
  ldr r1, =_edata
  ldr r2, =_sidata
  movs r3, #0
  b LoopCopyDataInit

/* begin jump to DFU */
Reboot_Loader:
  ldr r0, =0x1FFF0000
  ldr sp, [r0, #0]
  ldr r0, [r0, #4]
  bx r0
/* end jump to DFU */

CopyDataInit:
  ldr r4, [r2, r3]
  str r4, [r0, r3]
  adds r3, r3, #4

LoopCopyDataInit:
  adds r4, r0, r3
  cmp r4, r1
  bcc CopyDataInit
[...]

 

Again replace 0x2000000 with the RAM start address of your controller and 0xDECAFBAD with the magic number of your choice.

So now once the MCU starts up, it checks if the magic number at the RAM start address is present. If so it clears the magic number and enters the DFU mode. Otherwise it boots up the system normally.

And if you want to reset your MCU by software after applying the update, have a look at pydfu from Micropython .

4 REPLIES 4
Imen.D
ST Employee

Hello @EKoec 

Thanks for sharing your knowledge in the Community.

This will help other community members with the same issue to find the answer faster.

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen

Hello @EKoec 

This is great. This saves me from thinking about adding some specialized external circuitry to push boot and reset pins to force mcu in bootloader mode :grinning_face:. 

Thank you very much for this solution. 

Best,

Mladen

RPdB
Associate

@EKoec 

This works excellently on STM32G431x. However it does not work at all on STM32G474x.

It appears to boot into the main program every time. If I try to force bootloader with the below it locks up the CPU. This too works perfectly on the G431x.

Reset_Handler:
  b Reboot_Loader

/* default init code... */

/* begin jump to DFU */
Reboot_Loader:
  ldr r0, =0x1FFF0000
  ldr sp, [r0, #0]
  ldr r0, [r0, #4]
  bx r0
/* end jump to DFU */

 

Jumping to bootloader from within main program on G474x strangely works fine, but is not how I want to do it.

    void (*SysMemBootJump)(void);
    SysMemBootJump = (void (*)(void))(*((uint32_t *)(0x1FFF0000 + 4)));
    __set_MSP(*(uint32_t *)0x1FFF0000);
    SysMemBootJump();

 

Any ideas on why the assembly code doesn't work at all for the G474x?

EKoec
Associate II

Maybe the memory get's wiped on reboot? Try to set a break point at ResetHandler: and step through it with the debugger. I could also imagine some kind of access violation or whatsoever so that the controller reboots again. But I haven't worked with these controllers before so I'm just guessing here. Sorry.