STM32 built-in bootloader
Once we have finished our main working horse algorithm (well, I know it never finished), the next thing we facing is future support. All software has bugs, and importantly we want to be able to update our device every time when some of the bugs has been fixed.
STM did arguably good job providing built-in bootloader that can use multiple interfaces. Here I describe how to use the USB-based update mechanism dubbed DFU. DFU is an open prototcol that is well supported by many IDEs including STM32CubeProgrammer. Furthermore literally every programming language has DFU libraries, so it is fairly easy to write your own uploader if needed.
I start with STM32H7 series and I use the NUCLEO-H743ZI2 board for concreteness.
To run the built-in bootloader, we need to do two things. First, we need to set the stack pointer to the area of RAM that bootloader expected to see. Secong, we need to change the PC register value to the entry address of the bootloader. Sounds easy, right?
It worth noticing that STM recommends also to do the following steps before jumpint to the bootloader:
- Disable all peripheral clocks
- Disable used PLL
- Disable interrupts
- Clear pending interrupts
Here is the code that effectively does it.
typedef void (*pFunction)(void);
pFunction SysMemBootJump;
uint32_t JumpAddress;
void jump_to_dfu(void)
{
/* STM32H7 system BootLoader address. Other MCUs might have a different one! */
__IO uint32_t BootAddr = 0x1FF09800;
/* Disable all interrupts */
__disable_irq();
/* Jump to system BootLoader -- prepare pointers */
JumpAddress = *(__IO uint32_t *) (BootAddr + 4);
SysMemBootJump = (pFunction) JumpAddress;
/* Clear Interrupt Enable Register & Interrupt Pending Register */
for (int i=0;i<8;i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
/* De-init clocks/PLLs. Extrimely important on some MCUs. Without this line the MCU instantly reboots once jumped to the bootloader */
HAL_RCC_DeInit();
/* Initialize system BootLoader's Stack Pointer */
__set_MSP(*(__IO uint32_t *) BootAddr);
/* Jump to the system BootLoader */
SysMemBootJump();
}
- Your watchdog timer can reboot the MCU during update. Once you enabled it, there is no way to stop it (other than reboot)
- The firmware (for example your RTOS) can enable some protection mechanisms that won't let you to write certain memeory locations or registeres.
/* place to the memory region that does not get initialised automatically */
__attribute__((section(".ccram_buffer"))) uint32_t magic_code;
/* Need to run bootloader now! */
magic_code = 1234567;
NVIC_SystemReset();
int main(void)
{
....
/* USER CODE BEGIN SysInit */
if (magic_code == 1234567) {
MX_GPIO_Init();
magic_code = 0;
jump_to_dfu();
}
....
.dma_buffer : /* Space before ':' is critical */
{
*(.ccram_buffer)
} >RAM_D3
#ifdef STM32G474xx
__IO uint32_t BootAddr = 0x1FFF0000; // STM32G47
#elif STM32H743xx
__IO uint32_t BootAddr = 0x1FF09800; // STM32H74
#elif STM32H7A3xx
__IO uint32_t BootAddr = 0x1FF09800; // STM32H7A
#else
#error Define a correct bootloader address
#endif
Comments
Post a Comment