Jump to Bootloader in STM32 through application i.e using Boot 0 and Boot 1 Pins in Boot mode from User flash

57.6k views Asked by At

I have a requirement for firmware upgrade. I am planning to use USB DFU class. But command for firmware upgrade will come from PC application in my case . so i need to switch to bootloader which is there in System Memory. As initially i am running application so it is getting booted from User flash i.e i have Boot0 and Boot 1 pins configured for User flash. As DFU bootloader is there in System flash ,now for that Boot0 and Boot1 pins settings need to be changed . is there a way like Boot 0 and Boot 1 settings remain same as User Flash memory and in the application we jump to System Memory?

5

There are 5 answers

3
JF002 On BEST ANSWER

Boot0/1 pins are sampled only when the processor starts, in order to check if it should load the user code from memory or if it should load the bootloader. The state of these pins has no effect of the bootloader afterwards.

I've been faced to a similar request, and found 2 ways to load the bootloader on-demand.

First, you can "JUMP" from user-code to the bootloader. For example, you could jump to the bootloader when a button is pressed.

But... this is far more complicated than a simple JUMP instruction : some registers and devices must be reconfigured correctly to work with the bootloader, you have to ensure that no IRQ will be triggered during the JUMP,... In fact, you have to reconfigure the processor as if it was just started after reset. You can find some information about this technic : on this video from ST.

I managed to do this kind of things on STM32F1xx project. However, on a more complex project based on STM32F4, this would become really difficult... I would have to stop all devices (timers, communication interface, ADC, DAC,...), ensure that no IRQ would be triggered, reconfigure all the clocks,...

Instead, I decided to implement this second solution: When I want to jump to the bootloader, I write a byte in one of the backup register and then issue a soft-reset. Then, when the processor will restart, at the very beginning of the program, it will read this register. This register contains the value indicating that it should reboot in bootloader mode. Then, the jump to the bootloader is much easier, as presented in the youtube video.

0
Dave Hylands On

In MicroPython there is a pyb.bootloader() function which is used to enter into DFU mode.

The C code which implements that can be found in their source repository.

I've used the STM32F4 version extensively (the #else block), and the F7 variant a few times (although its been a while).

I'll put the body of the function here since the above links could become stale if that file changes:

// Activate the bootloader without BOOT* pins.
STATIC NORETURN mp_obj_t machine_bootloader(void) {
    pyb_usb_dev_deinit();
    storage_flush();

    HAL_RCC_DeInit();
    HAL_DeInit();

#if defined(MCU_SERIES_F7)
    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x1FF00000));
    __ASM volatile ("movw r3, #0x0000\nmovt r3, #0x1FF0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x1FF00004))();
#else
    __HAL_REMAPMEMORY_SYSTEMFLASH();

    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x00000000));
    __ASM volatile ("movs r3, #0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x00000004))();
#endif

    while (1);
}

The pyb_usb_dev_deinit() function shuts down USB, and storage_flush writes out any cached filesystem data. The HAL functions come from the STM32Cube HAL files.

If you use a newer version of dfu-util (IIRC 0.8 or newer) then you can specify the -s :leave command line option to have your newly flashed program executed at the end of flashing. Combining with the above I go through flash/test cycles without having to touch the board, and only use BOOT0/RESET when the firmware hard-crashes.

There is also a python DFU flasher called pydfu.py: https://github.com/micropython/micropython/blob/master/tools/pydfu.py which is a little faster than dfu-util.

0
j123b567 On

You can simulate the bootloader condition. Connect capacitor and paralel resistor from BOOT pin to ground. Connect another free pin to BOOT pin. Capacitor can be charged by external pin and is discharged by resistor. I don't remember exact values you can calculate/experiment them (important is time constant of RC circuit).

Charge this capatitor by setting external pin to 1, perform software reset by NVIC_SystemReset. After reset, it will run bootloader. Resistor connected to Capacitor will perform discharging. After firmware update, you can reset the device and it will run to your application.

We are using this in some applications and it works well. Drawback of this solution is that you need external circuitry, but it is very easy to implement and it is universal for all STM32 devices.

0
Labi Shrestha On

I was struggling with this issue for a while trying to jump from a FreeRTOS application on a STM32L4A6xx to USB OTG DFU. After a lot of trial and error, I was able to make it work, so thought I'd post it here since I couldn't find clear instructions on this anywhere else.

Note This code is for STM32L4, following the same pattern should work for other ones.

Also when you flash the image with STM32CubeProgrammer, make sure to check 'Run Application' checkbox, otherwise it tends stay in bootloader mode.

void JumpToBootloader(void)
{
     HAL_SuspendTick();

     /* Clear Interrupt Enable Register & Interrupt Pending Register */
     for (int i=0;i<5;i++)
     {
         NVIC->ICER[i]=0xFFFFFFFF;
         NVIC->ICPR[i]=0xFFFFFFFF;
     }

     HAL_FLASH_Unlock();

     HAL_FLASH_OB_Unlock();

     // RM0351 Rev 7 Page 93/1903
     // AN2606 Rev 44 Page 23/372
     CLEAR_BIT(FLASH->OPTR, FLASH_OPTR_nBOOT0);
     SET_BIT(FLASH->OPTR, FLASH_OPTR_nBOOT1);
     CLEAR_BIT(FLASH->OPTR, FLASH_OPTR_nSWBOOT0);

     SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

     while(READ_BIT(FLASH->SR, FLASH_SR_BSY));

     HAL_FLASH_OB_Launch();
}
0
Flip On

To jump to a new image is not that difficult. I have done it successfully as part of a power on self test.

  1. You have to pass the address of where your second image (the bootloader in this case) resides in flash to the linker when you link the second image. You could possibly use position independent code instead, but that has other issues.
  2. You have to, obviously, flash program the second image starting from the same address as you gave the linker.
  3. Set the jump function: void (* const jumpFunction)(void) = (void (*)(void))(APPLICATION_ADDRESS + 4ul + 1ul); The offset by four is to get past the stack pointer, the offset by one is for Thumbmode.
  4. Specify the new stack starting pointer: __set_MSP((uint32_t)*APPLICATION_ADDRESS), the first four bytes from the second image will contain the new stack pointer.
  5. Jump by calling the function: jumpFunction();
  6. In the second program, the default initialisation will try and set the vector table offset (VTOR) SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;. You have to change this to SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET;

I have a POST program at FLASH_BASE that uses the core coupled SRAM for the its stack and then runs memory checks on the main SRAM, checks the authenticity of the main program and then jumps to the main program.

I can still debug the main program as if nothing changed.

NB! I have only recently done this myself. There are a few things I need to verify. One concern is what would happen with a software reset. If called from the second program it will go, I think, to the reset routine of the second program, not the first.