cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4: jump to MCU bootloader

Werner Dähn
Associate II
Posted on June 01, 2018 at 07:54

I think I got the concept of how to jump to the MCU built-in bootloader but I never got it working. And often information is opposing each other.

My requirement is simple and very common, hence I hope somebody has code at hand. I am fed up with trying and trying.

I have a USB CDC connection, and enter commands there. One command should be 'boot to MCU bootloader' which would disconnect the USB and make the MCU appear in the DfuSeDemo tool. As if I manually disconnected the the board, pressed the Boot0 button and power it while button pressed.

Source Code can be found here: 

https://github.com/wernerdaehn/CC3D-CableCam-Controller

  

Nothing special, standard memory layout etc.

#stm32f4-dfu-usb-bootloader
1 ACCEPTED SOLUTION

Accepted Solutions
...
    .text
    .thumb
 
    .align 2
    .thumb_func
    .globl   Reboot_Loader
    .type    Reboot_Loader, %function
Reboot_Loader:
    // Device specific, if in doubt RTFM, here F2/F4
 
    LDR     R0, =0x40023844 // RCC_APB2ENR
    LDR     R1, =0x00004000 // ENABLE SYSCFG CLOCK
    STR     R1, [R0, #0]
    LDR     R0, =0x40013800 // SYSCFG_MEMRMP
    LDR     R1, =0x00000001 // MAP ROM AT ZERO
    STR     R1, [R0, #0]
    LDR     R0, =0x1FFF0000 // ROM BASE
    LDR     SP,[R0, #0]     // SP @ +0
    LDR     R0,[R0, #4]     // PC @ +4
    BX      R0
     .pool // sourcer32@gmail.com
    .size Reboot_Loader, . - Reboot_Loader
 
    .align 2
    .thumb_func
    .globl    Reset_Handler
    .type    Reset_Handler, %function
Reset_Handler:
 
    LDR     R0, =0x2001FFF0 // End of SRAM for your CPU
    LDR     R1, =0xDEADBEEF
    LDR     R2, [R0, #0]
    STR     R0, [R0, #0] // Invalidate
    CMP     R2, R1
    BEQ     Reboot_Loader
 
/*     Loop to copy data from read only memory to RAM. The ranges
 *      of copy from/to are specified by following symbols evaluated in
 *      linker script.
 *      __etext: End of code section, i.e., begin of data sections to copy from.
 *      __data_start__/__data_end__: RAM address range that data should be
 *      copied to. Both must be aligned to 4 bytes boundary.  */
...

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..

View solution in original post

16 REPLIES 16
Thomas Rapp
Associate II
Posted on June 01, 2018 at 09:59

Hi Werner,

have you already stumbled accross this quite well documented bootloader example?

https://github.com/akospasztor/stm32-bootloader

 

bootloader.c contains the method '

Bootloader_JumpToSysMem

()' which shall do exactly what you're looking for.

I myself am currently working on a bootloader based on this implementation. While I haven't personally tested the jump to the system memory yet, the jump to the application (out of the IAP bootloader, see method 'Bootloader_JumpToApplication()' ) which is basically the same, works like a charm.

However, I've noticed that the 'while(1);'-loop at the very end of the method seems to be important. Some compiler magic seems to happen so that it is essential although from my understanding it is never reached.

For further assistance it would be very helpful if you posted the relevant parts of your code, not only the GitHub link (as it's some thousand lines of code, I've no time to dig into 😉 )

Best regards,

Thomas
Posted on June 01, 2018 at 21:10

Yes, that's a good starting point. I have used that example and it seems to work, except for the USB. In Windows I get a 'device cannot be recognized' error.

So I added some code to disable USB first via various ways. Disconnect, deinit, pull the USB lines low so it appears to Windows as if the cable got plugged. Likely any of these methods are fine but I use them all for now and have a large wait for Windows to recognize the line is down.

HAL_PCD_DevDisconnect( hUsbDeviceFS.pData );

USBD_Stop(&hUsbDeviceFS);

USBD_DeInit(&hUsbDeviceFS);

/**USB_OTG_FS GPIO Configuration

PA11 ------> USB_OTG_FS_DM

PA12 ------> USB_OTG_FS_DP

*/

GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);

HAL_Delay(5000); // Wait for 5 seconds so that it looks to Windows as if the USB cable has been unplugged.

Now windows gets a proper USB disconnect event, then the code waits the five seconds. Then the device seems to jump into the bootloader code but Windows shows after 20 more seconds the message 'device cannot be recognized'. Device manager says 'Unknown device, error when requesting the device descriptor' or something like that.

'Unbekanntes USB-Gerät (Fehler beim Anfordern einer Gerätebeschreibung)'

Something clearly does happen on the USB lines, else Windows would not react at all. But the bootloader is not responding to the requests from Windows USB Host.

What could be the reason of that?

Posted on June 04, 2018 at 09:44

Hm okay,

could you by any means verify that the actual jump to the bootloader went well, e.g. by trying to communicate with the bootloader via one of the other supported interfaces (UART, CAN, I2C)?

If the actual jump works, I'd suggest to first look for other users' experiences adapting your search keywords and if that doesn't help repost your problem with a title and description more focused on the USB aspect.

I'm sorry I can't help you further with the problems regarding the USB connection as I've absolutely no experience regarding it (communicating with my STM32 only via UART, CAN and JTAG^^).

Posted on June 05, 2018 at 10:08

Yes, bootloader jump works. If I do not enable USB CDC by commenting out the USBD_Init() call in the main.c class, then the bootloader comes to life. Actually, USB_Init() is okay as well, it is registering the CDC class, that makes the difference.

Should not be the case since I call USBD_DeInit() and that does deinit the CDC class as well before closing the rest of the USB. Obviously the is a side effect I don't know how to isolate and what to do with it.

Tobi as
Associate II

Hello Werner,

have you solved the problem? I have the same problem and I have no idea.

If I start my function jumpToBootloader before I initialize USB CDC, then it works.

If I initialize USB CDC before I call the function jumpToBootloader, then it doesn't work. But a communication with the MCU doesn't work now.

void jumpToBootloader(void) {

void (*SysMemBootJump)(void);

   volatile uint32_t addr = 0x1FFF0000;

#if defined(USE_HAL_DRIVER)

   HAL_RCC_DeInit();

#endif /* defined(USE_HAL_DRIVER) */

#if defined(USE_STDPERIPH_DRIVER)

   RCC_DeInit();

#endif /* defined(USE_STDPERIPH_DRIVER) */

   SysTick->CTRL = 0;

   SysTick->LOAD = 0;

   SysTick->VAL = 0;

   __disable_irq();

   SYSCFG->MEMRMP = 0x01;

   SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));

   __set_PRIMASK(1);      // Disable interrupts

   __set_PRIMASK(0x20001000);      // Set the main stack pointer to its default value

   __set_MSP(0x20001000);

   SysMemBootJump();

   while(1);

}

Sorry that I can't help you.

If you disable interrupts expect to have to enable them on the other side, writing the stack pointer to PRIMASK also seems inadvisable. ​

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Werner Dähn
Associate II

Clive, do you have any hint what we are missing?

I would like to get that working still.

To boot into DFuSe from the console, I've used this for years.

It enables the clocks need, maps the ROM, and uses the correct SP/PC values, doesn't disable interrupts, and does so under near reset conditions.

; On the startup.s side
 
Reset_Handler   PROC
    EXPORT  Reset_Handler             [WEAK]
    IMPORT  SystemInit
    IMPORT  __main
 
    LDR     R0, =0x2001FFF0 ; End of SRAM for your CPU
    LDR     R1, =0xDEADBEEF
    LDR     R2, [R0, #0]
    STR     R0, [R0, #0] ; Invalidate
    CMP     R2, R1
    BEQ     Reboot_Loader
 
    LDR     R0, =SystemInit
    BLX     R0
    LDR     R0, =__main
    BX      R0
    ENDP
 
Reboot_Loader   PROC
    EXPORT  Reboot_Loader
 
    ; Device specific, if in doubt RTFM, here F2/F4
 
    LDR     R0, =0x40023844 ; RCC_APB2ENR
    LDR     R1, =0x00004000 ; ENABLE SYSCFG CLOCK
    STR     R1, [R0, #0]
    LDR     R0, =0x40013800 ; SYSCFG_MEMRMP
    LDR     R1, =0x00000001 ; MAP ROM AT ZERO
    STR     R1, [R0, #0]
    LDR     R0, =0x1FFF0000 ; ROM BASE
    LDR     SP,[R0, #0]     ; SP @ +0
    LDR     R0,[R0, #4]     ; PC @ +4
    BX      R0
 
    ENDP ; sourcer32@gmail.com
 
 
/* On the C side */
 
    printf("Entering Boot Loader..\r\n");
 
    *((unsigned long *)0x2001FFF0) = 0xDEADBEEF; // End of RAM
 
    NVIC_SystemReset();

You'll need to adapt to your specific tool chain and library choices.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Werner Dähn
Associate II

I have a feeling of what you want me to do but cannot match it precisely.

If using gcc as toolchain (EmBitz as IDE) and the startup.s is the attached, what changes would I need to make?

Sorry for being slow.