Skip to main content
Associate
November 9, 2025
Solved

Run app to jump bootloader stm32G

  • November 9, 2025
  • 1 reply
  • 192 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 */

 

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

TDK
TDKBest answer
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

"If you feel a post has answered your question, please click ""Accept as Solution""."
Abus-kAuthor
Associate
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 */