Difference between revisions of "STM32 Jump to System Memory Bootloader"

From Stm32World Wiki
Jump to navigation Jump to search
Line 54: Line 54:
  
 
<pre>
 
<pre>
 
 
   /* USER CODE BEGIN 1 */
 
   /* USER CODE BEGIN 1 */
  
Line 68: Line 67:
  
 
   /* USER CODE END 1 */
 
   /* USER CODE END 1 */
 +
</pre>
 +
 +
Of course the result of that would be an application that always executed the bootloader, and that is not really useful, nor is it what we wanted to do.
  
</pre>
+
What we need is a way to store a flag, which will survive a system restart of the [[MCU]].  This is a little bit tricky since the user memory (RSS) is set to zero during restart.  There are however
  
 
== Source ==
 
== Source ==

Revision as of 06:13, 16 March 2022

All STM32 MCUs has a builtin bootloader stored in so-called system memory. The system memory is a ROM (read-only memory) which is created during the production of the MCU and can never be changed. When the MCU startup the Boot0 pin is asserted and if high (pulled up to VCC) the MCU will execute the bootloader.

The "problem" (or challenge)

But what if we would like to execute this bootloader programmatically based on some other event for example the press of a user button or a command in a serial console. While this is entirely possible it is surprisingly difficult. The problem is that the built-in bootloader make a lot of assumptions and it is necessary to make certain all these assumptions are met before jumping to it. In general that would look something like:

  • Find system memory location for specific STM32 in AN2606
  • Set RCC to default values (the same as on startup) [Internal clock, no PLL, etc.)
  • Disable SysTick interrupt and reset it to default
  • Disable all interrupts
  • Map system memory to 0x00000000 location
  • Set jump location to memory location + 4 bytes offset
  • Set main stack pointer to value stored at system memory location address
  • Call virtual function assigned before

Depending on which peripherals is being used, the above can be quite complicated and changes in code can easily screw this up.

The easy solution

Fortunately, there is in fact an easier way to do this. If we look at the code generated by STM32CubeMX the startup is like this:

int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

...

By the time the main function is being called, only the memory has been initialized (BSS has been set to 0x00 throughout), and if we were to do the jump immediately (in the USER CODE BEGIN 1 section) that would work just fine. That would look something like this:

  /* USER CODE BEGIN 1 */

#define BOOTLOADER_ADDRESS 0x1FFF0000
typedef  void (*pFunction)(void);
pFunction JumpToApplication;
uint32_t JumpAddress;

/* Jump to system memory bootloader */
JumpAddress = *(__IO uint32_t*) (BOOTLOADER_ADDRESS + 4);
JumpToApplication = (pFunction) JumpAddress;
JumpToApplication();

  /* USER CODE END 1 */

Of course the result of that would be an application that always executed the bootloader, and that is not really useful, nor is it what we wanted to do.

What we need is a way to store a flag, which will survive a system restart of the MCU. This is a little bit tricky since the user memory (RSS) is set to zero during restart. There are however

Source

The source for this example can be found on our github.

Miscellaneous Links