2025-11-09 3:41 PM
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) { }
}/* 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 */
Solved! Go to Solution.
2025-11-09 4:12 PM
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
2025-11-09 4:12 PM
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