Skip to main content
Werner Dähn
Associate III
June 1, 2018
Solved

STM32F4: jump to MCU bootloader

  • June 1, 2018
  • 13 replies
  • 4869 views
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
This topic has been closed for replies.
Best answer by Tesla DeLorean
...
 .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. */
...

13 replies

Thomas Rapp
Associate
June 1, 2018
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
Werner Dähn
Associate III
June 1, 2018
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?

Thomas Rapp
Associate
June 4, 2018
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^^).

Tobi as
Associate
September 10, 2018

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.

Tesla DeLorean
Guru
September 10, 2018

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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
Werner Dähn
Associate III
September 10, 2018

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

I would like to get that working still.

Tesla DeLorean
Guru
September 10, 2018

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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
Werner Dähn
Associate III
September 10, 2018

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.

Tesla DeLorean
Tesla DeLoreanBest answer
Guru
September 10, 2018
...
 .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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
Werner Dähn
Associate III
September 11, 2018

Yes, that does work perfectly. Thank you so much!

I have seen your solution at multiple places but never understood what to do in detail. Now it is clear - afterwards.

I love micro controller programming. ;)

Tobi as
Associate
September 11, 2018

Hi,

sorry it doesn't run in my project. I use the IDE from Atollic and haven't the file "startup_stm32f4xx.s.

If I used "__set_PRIMASK(0x20001000);" in the following code, then it runs when I don't initialize USB.

If I set "__set_PRIMASK(0x20001000);" in the following code as comment, then I get an error when I don't initialize USB.

If I initialize USB, then I becomme an error regardless of "__set_PRIMASK(0x20001000);".

void jumpToBootloader(void) {
 void (*SysMemBootJump)(void);
 volatile uint32_t addr = 0x1FFF0000;
 
 RCC_DeInit();
 
 SysTick->CTRL = 0;
 SysTick->LOAD = 0;
 SysTick->VAL = 0;
 
 SYSCFG->MEMRMP = 0x01;
 
 SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));
 
 __disable_irq();
 __set_PRIMASK(1); // Disable interrupts
// __set_PRIMASK(0x20001000); // Set the main stack pointer to its default value
 __set_MSP(*(uint32_t *)addr);
 
 SysMemBootJump();
 while(1);
}

Have you a idea? Thanks for your help.

Tesla DeLorean
Guru
September 11, 2018

Stop disabling interrupts on the processor, as I've repeatedly said there's no magic on the otherside to enable them, the processor usually resets to get into the ROM based system loader, and interrupts are NOT disabled then.

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