Skip to main content
Explorer
November 9, 2025
Solved

Run app to jump bootloader stm32G

  • November 9, 2025
  • 1 reply
  • 191 views

Hello everyone, for a few days I have been struggling with skipping and running the bootloader on stm32g474ret6 via usart.

I have an STM, 12MHz crystal, and an FTDI chip on my own PCB, with RTS/CTS disconnected for now. After sending a command via USART, the control LED lights up, but the bootloader doesn't start. What am I doing wrong?

#include <stdint.h>
#include "stm32g4xx.h"
#include "stm32g4xx_hal.h"

#define SYS_MEM_BASE 0x1FFF0000

void JumpToBootloader(void)
{
 __disable_irq();


 SysTick->CTRL = 0;


 for (int i = 0; i < 8; i++) {
 NVIC->ICER[i] = 0xFFFFFFFF;
 NVIC->ICPR[i] = 0xFFFFFFFF;
 }
 __DSB(); __ISB();


 HAL_DeInit();
 HAL_RCC_DeInit();


 __HAL_RCC_HSI_ENABLE();
 while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET);
 __HAL_RCC_SYSCLK_CONFIG(RCC_SYSCLKSOURCE_HSI);

 __DSB(); __ISB();


 uint32_t msp = *(__IO uint32_t *)(SYS_MEM_BASE + 0x0);
 uint32_t reset = *(__IO uint32_t *)(SYS_MEM_BASE + 0x4);


 SCB->VTOR = SYS_MEM_BASE;
 __DSB(); __ISB();


 __set_MSP(msp);
 void (*BootJump)(void) = (void (*)(void))reset;
 BootJump();

 while (1) { }
}

ob.png

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file : main.c
 * @brief : Main 
 ******************************************************************************
 * @attention
 * Copyright (c) 2025 ST.
 * This software is provided AS-IS.
 ******************************************************************************
 */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "fdcan.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "bootjump.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
static uint8_t rx1; 
static char cmd_buf[64]; 
static uint8_t cmd_len = 0; 
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
static inline void UART1_StartRxIT(void);
static void Handle_Command_Line(const char *line);
static void UART1_Diag(void);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

static inline void UART1_StartRxIT(void)
{
 if (HAL_UART_Receive_IT(&huart1, &rx1, 1) != HAL_OK) {
 __HAL_UART_CLEAR_OREFLAG(&huart1);
 __HAL_UART_CLEAR_FEFLAG(&huart1);
 __HAL_UART_CLEAR_NEFLAG(&huart1);
 (void)HAL_UART_Receive_IT(&huart1, &rx1, 1);
 }
}


static void Handle_Command_Line(const char *line)
{
 // cut CR/LF/itp
 size_t n = strlen(line);
 while (n && (line[n-1] == '\r' || line[n-1] == '\n' || line[n-1] == ' ' || line[n-1] == '\t')) {
 n--;
 }

 
 char tmp[sizeof(cmd_buf)];
 size_t m = (n < sizeof(tmp) - 1) ? n : (sizeof(tmp) - 1);
 for (size_t i = 0; i < m; i++) {
 char c = line[i];
 if (c >= 'a' && c <= 'z') c = (char)(c - 32);
 tmp[i] = c;
 }
 tmp[m] = 0;

 
 if ((strcmp(tmp, "UPDATE") == 0) || (strcmp(tmp, "BOOT") == 0)) {

 const char *msg =
 "OK: entering ROM bootloader jumping now.\r\n"
 "Reconnect as 115200, 8E1 and send 0x7F.\r\n";
 (void)HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 200);

 // LED na PA8
 for (int i = 0; i < 3; ++i) {
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
 HAL_Delay(120);
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
 HAL_Delay(120);
 }
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
 HAL_Delay(200);

 
 JumpToBootloader();
 return;
 }

 
 if (m > 0) {
 const char *unk = "Use only: UPDATE\r\n";
 (void)HAL_UART_Transmit(&huart1, (uint8_t*)unk, strlen(unk), 100);
 }
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 if (huart->Instance != USART1) return;

 uint8_t c = rx1;

 
 static uint8_t last_cr = 0;
 if (c=='\r' || c=='\n') {
 if (c=='\r') last_cr=1;
 else if (c=='\n' && last_cr){ last_cr=0; UART1_StartRxIT(); return; }

 (void)HAL_UART_Transmit(&huart1,(uint8_t*)"\r\n",2,20);
 cmd_buf[cmd_len]=0;
 Handle_Command_Line(cmd_buf);
 cmd_len=0;
 UART1_StartRxIT();
 return;
 }
 last_cr=0;

 
 if (cmd_len < sizeof(cmd_buf)-1) {
 cmd_buf[cmd_len++] = (char)c;
 (void)HAL_UART_Transmit(&huart1,&c,1,10); // echo
 } else {
 cmd_len=0;
 const char *msg="\r\nERR: line too long\r\n";
 (void)HAL_UART_Transmit(&huart1,(uint8_t*)msg,strlen(msg),50);
 }

 UART1_StartRxIT();
}

static void UART1_Diag(void)
{
 uint32_t cr3 = READ_REG(USART1->CR3);
 if (cr3 & (USART_CR3_CTSE | USART_CR3_RTSE)) {
 for (int i=0;i<5;i++){ HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8); HAL_Delay(80); }
 CLEAR_BIT(USART1->CR3, USART_CR3_CTSE | USART_CR3_RTSE);
 }
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
 if (huart->Instance == USART1) {
 __HAL_UART_CLEAR_OREFLAG(huart);
 __HAL_UART_CLEAR_FEFLAG(huart);
 __HAL_UART_CLEAR_NEFLAG(huart);
 UART1_StartRxIT();
 }
}
/* USER CODE END 0 */


int main(void)
{
 HAL_Init();
 SystemClock_Config();

 

 
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_FDCAN1_Init();
 MX_FDCAN2_Init();
 MX_USART1_UART_Init();
 UART1_Diag();

 const char *hello =
 "READY: USART1. Type 'UPDATE' to enter ROM bootloader.\r\n";
 (void)HAL_UART_Transmit(&huart1,(uint8_t*)hello,strlen(hello),200);

 UART1_StartRxIT();

 while (1)
 {
 
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
 }
}


void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); }

 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); }
}

/* USER CODE BEGIN 4 */
/* USER CODE END 4 */

void Error_Handler(void)
{
 __disable_irq();
 while (1) { }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
 (void)file; (void)line;
}
#endif /* USE_FULL_ASSERT */

 

    This topic has been closed for replies.
    Best answer by TDK

    Don't jump from within an interrupt context. Set a flag and jump from the main thread instead.

    Don't disable interrupts without re-enabling them.

    Working jump to bootloader code is here:

    How to jump to system bootloader from application ... - STMicroelectronics Community

    1 reply

    TDKAnswer
    Super User
    November 10, 2025

    Don't jump from within an interrupt context. Set a flag and jump from the main thread instead.

    Don't disable interrupts without re-enabling them.

    Working jump to bootloader code is here:

    How to jump to system bootloader from application ... - STMicroelectronics Community

    Abus-kAuthor
    Explorer
    November 11, 2025

    Thank you for letting me enter the bootloader. I didn't get to a single registered error :) But the problem, which is again related to exiting the bootloader, is that the bootloader doesn't respond to GO commands at all. I tried nvic reset, and the vector array at address 0x08000200 doesn't work either, as if something connected. According to user 'kfrosh', trying to systick after RCC_deinit doesn't work either. I thought the UART was working, but when the bootloader is enabled, it communicates via UART via the stm32prg without any problems.

    //reset 
    #include "stm32g4xx.h"
    #include <stdint.h>
    
    extern uint32_t _estack;
    
    
    __attribute__((naked, noreturn))
    static void Force_Reset_Handler(void)
    {
     __asm volatile(
     "LDR r0, =0xE000ED0C \n" /* SCB->AIRCR */
     "LDR r1, =0x05FA0004 \n" /* VECTKEY(0x5FA<<16) | SYSRESETREQ */
     "STR r1, [r0] \n"
     "B . \n" /* czekamy na reset */
     );
     __builtin_unreachable();
    }
    
    /* 0: MSP, 1: "Reset_Handler" Force_Reset_Handler */
    __attribute__((used, section(".force_reset_vtable"), aligned(4)))
    const void * const force_reset_vtable[2] =
    {
     (const void*)&_estack,
     (const void*)Force_Reset_Handler 
    };
    #include "stm32g4xx_hal.h"
    
    #define SYS_MEM_BASE (0x1FFF0000UL) 
    
    typedef struct {
     uint32_t msp;
     void (*reset)(void);
    } RomVect;
    
    #define ROM_VECT ((RomVect *)SYS_MEM_BASE)
    void JumpToBootloader(void)
    {
     
     if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0U) {
     
     return;
     }
     
     uint32_t nblocks = ((SCnSCB->ICTR & 0xFU) + 1U);
     for (uint32_t i = 0; i < nblocks; ++i) {
     NVIC->ICER[i] = 0xFFFFFFFFu; // disable enables
     NVIC->ICPR[i] = 0xFFFFFFFFu; // clear pendings
     }
     __DSB(); __ISB();
    
     
     HAL_StatusTypeDef st = HAL_RCC_DeInit();
     (void)st;
     
     HAL_DeInit();
     __DSB(); __ISB();
     
     SysTick->CTRL = 0;
     SysTick->VAL = 0;
     SysTick->LOAD = 0;
     
     __disable_irq();
     
     __HAL_RCC_SYSCFG_CLK_ENABLE();
     __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); // SYSCFG->MEMRMP = 1
     __DSB(); __ISB();
     
     SCB->VTOR = SYS_MEM_BASE;
     __DSB(); __ISB();
     
     __set_MSP(ROM_VECT->msp);
     RomVect *rom = ROM_VECT;
     rom->reset();
    
     while (1) { /* no return */ }
    }

     

    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "dma.h"
    #include "fdcan.h"
    #include "usart.h"
    #include "gpio.h"
    
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <string.h>
    #include <stdio.h>
    #include "bootjump.h"
    /* USER CODE END Includes */
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    /* USER CODE END PTD */
    
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    /* USER CODE END PD */
    
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
    /* USER CODE END PM */
    
    /* Private variables ---------------------------------------------------------*/
    /* USER CODE BEGIN PV */
    static uint8_t rx1; 
    static char cmd_buf[64]; 
    static uint8_t cmd_len = 0; 
    
    
    static volatile uint8_t g_line_ready = 0; 
    static volatile uint8_t g_boot_req = 0; 
    /* USER CODE END PV */
    
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    /* USER CODE BEGIN PFP */
    static inline void UART1_StartRxIT(void);
    static void Handle_Command_Line(const char *line);
    static void UART1_Diag(void);
    /* USER CODE END PFP */
    
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    
    static inline void UART1_StartRxIT(void)
    {
     if (HAL_UART_Receive_IT(&huart1, &rx1, 1) != HAL_OK) {
     __HAL_UART_CLEAR_OREFLAG(&huart1);
     __HAL_UART_CLEAR_FEFLAG(&huart1);
     __HAL_UART_CLEAR_NEFLAG(&huart1);
     (void)HAL_UART_Receive_IT(&huart1, &rx1, 1);
     }
    }
    
    
    static void Handle_Command_Line(const char *line)
    {
     
     size_t n = strlen(line);
     while (n && (line[n-1] == '\r' || line[n-1] == '\n' || line[n-1] == ' ' || line[n-1] == '\t')) n--;
    
     
     char tmp[sizeof(cmd_buf)];
     size_t m = (n < sizeof(tmp) - 1) ? n : (sizeof(tmp) - 1);
     for (size_t i = 0; i < m; i++) {
     char c = line[i];
     if (c >= 'a' && c <= 'z') c = (char)(c - 32);
     tmp[i] = c;
     }
     tmp[m] = 0;
    
     
     if ((strcmp(tmp, "UPDATE") == 0) || (strcmp(tmp, "BOOT") == 0)) {
    
     const char *msg =
     "OK: entering ROM bootloader\r\n"
     "Reconnect as 115200, 8E1 and send 0x7F.\r\n";
     (void)HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 200);
    
     // blink LED na PA8
     for (int i = 0; i < 3; ++i) {
     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
     HAL_Delay(120);
     //HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
     //HAL_Delay(120);
     }
     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
     HAL_Delay(200);
    
     
     g_boot_req = 1;
     return;
     }
    
     
     if (m > 0) {
     const char *unk = "Use only: UPDATE\r\n";
     (void)HAL_UART_Transmit(&huart1, (uint8_t*)unk, strlen(unk), 100);
     }
    }
    
    
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
     if (huart->Instance != USART1) return;
    
     uint8_t c = rx1;
    
     // CR/LF 
     static uint8_t last_cr = 0;
     if (c=='\r' || c=='\n') {
     if (c=='\r') last_cr=1;
     else if (c=='\n' && last_cr){ last_cr=0; UART1_StartRxIT(); return; }
    
     (void)HAL_UART_Transmit(&huart1,(uint8_t*)"\r\n",2,20);
     if (cmd_len < sizeof(cmd_buf)) cmd_buf[cmd_len] = 0;
     else cmd_buf[sizeof(cmd_buf)-1] = 0;
    
     g_line_ready = 1; 
     UART1_StartRxIT();
     return;
     }
     last_cr=0;
    
     
     if (cmd_len < sizeof(cmd_buf)-1) {
     cmd_buf[cmd_len++] = (char)c;
     (void)HAL_UART_Transmit(&huart1,&c,1,10); // echo
     } else {
     cmd_len=0;
     const char *msg="\r\nERR: line too long\r\n";
     (void)HAL_UART_Transmit(&huart1,(uint8_t*)msg,strlen(msg),50);
     }
    
     UART1_StartRxIT();
    }
    
    static void UART1_Diag(void)
    {
     uint32_t cr3 = READ_REG(USART1->CR3);
     if (cr3 & (USART_CR3_CTSE | USART_CR3_RTSE)) {
     for (int i=0;i<5;i++){ HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8); HAL_Delay(80); }
     CLEAR_BIT(USART1->CR3, USART_CR3_CTSE | USART_CR3_RTSE);
     }
    }
    
    void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
    {
     if (huart->Instance == USART1) {
     __HAL_UART_CLEAR_OREFLAG(huart);
     __HAL_UART_CLEAR_FEFLAG(huart);
     __HAL_UART_CLEAR_NEFLAG(huart);
     UART1_StartRxIT();
     }
    }
    /* USER CODE END 0 */
    
    int main(void)
    {
     HAL_Init();
     SystemClock_Config();
    
     
     MX_GPIO_Init();
     MX_DMA_Init();
     MX_FDCAN1_Init();
     MX_FDCAN2_Init();
     MX_USART1_UART_Init();
     UART1_Diag();
    
     const char *hello =
     "READY: USART1. Type 'UPDATE' to enter ROM bootloader.\r\n";
     (void)HAL_UART_Transmit(&huart1,(uint8_t*)hello,strlen(hello),200);
    
     UART1_StartRxIT();
    
     while (1)
     {
    	 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
     
     if (g_line_ready) {
     __disable_irq(); 
     g_line_ready = 0;
     __enable_irq();
    
     Handle_Command_Line(cmd_buf);
    
     __disable_irq();
     cmd_len = 0; 
     __enable_irq();
     }
    
     
     if (g_boot_req) {
     g_boot_req = 0;
     JumpToBootloader(); 
     }
    
     
     //HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
     }
    }
    
    
    void SystemClock_Config(void)
    {
     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
     HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
    
     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
     RCC_OscInitStruct.HSIState = RCC_HSI_ON;
     RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
     if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); }
    
     RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
     |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
     RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
     RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
     RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
     if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); }
    }
    
    /* USER CODE BEGIN 4 */
    /* USER CODE END 4 */
    
    void Error_Handler(void)
    {
     __disable_irq();
     while (1) { }
    }
    
    #ifdef USE_FULL_ASSERT
    void assert_failed(uint8_t *file, uint32_t line)
    {
     (void)file; (void)line;
    }
    #endif /* USE_FULL_ASSERT */