STM32G474调用内部函数跳转Bootloader失败问题排查
STM32G474跳转Bootloader失败问题排查与解决
问题描述
我在STM32G431芯片上实现了一段跳转至Bootloader的函数,可成功完成跳转。系统仅启用SPI1用于进入Bootloader模式后的通信,且确保此代码不在中断中执行(通过主函数检查标志位后执行)。将这段代码移植到STM32G474后却无法生效,设备直接回到程序内存,且未到达最后的while(1)循环。请问STM32G474是否存在与STM32G431不同的特殊寄存器设置?
原测试代码
/** * @brief Function to reset STM32 into bootloader mode * @note This function will not return as it triggers a system reset * @note Flash start address for STM32G431: 0x08000000 */ void jump_to_bootloader(void) { #define conBootloadAddress 0x1FFF0000 void (*SysMemBootJump)(void); /* 1. Disable all interrupts */ __disable_irq(); /* 2. Disable all peripheral clocks */ HAL_SPI_DeInit(&hspi1); MX_GPIO_DeInit(); /* 3. Disable PLL and reset to default clock */ HAL_RCC_DeInit(); // This handles PLL disable /* 4. Clear ALL pending interrupts */ HAL_NVIC_DisableIRQ(SPI1_IRQn); HAL_NVIC_ClearPendingIRQ(SPI1_IRQn); for (int i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++) { NVIC->ICER[i] = 0xFFFFFFFF; NVIC->ICPR[i] = 0xFFFFFFFF; } /* 5. Disable SysTick */ SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; /* 6. Re-enable interrupts (needed for bootloader) */ __enable_irq(); /* 7. Force system reset for clean bootloader entry */ HAL_FLASH_OB_Launch(); // This triggers a system reset /* 8. Jump to bootloader */ SysMemBootJump = (void (*)(void)) (*((uint32_t*) (conBootloadAddress + 4))); __set_MSP(*(uint32_t*) conBootloadAddress); SysMemBootJump(); while (1); }
问题分析与解决
1. 核心错误:HAL_FLASH_OB_Launch()的误用
HAL_FLASH_OB_Launch()的作用是触发选项字节加载并立即执行系统复位。如果未预先修改选项字节的启动模式为系统Bootloader,复位后芯片会默认从用户Flash启动,直接回到你的应用程序——这就是代码无法执行到后续跳转逻辑和while(1)的原因。你在STM32G431上的成功大概率是误操作导致的特殊情况,并非标准跳转流程。
2. STM32G474与G431的Bootloader差异
两者的系统Bootloader基地址均为0x1FFF0000,寄存器逻辑无本质差异,无需针对G474修改特殊寄存器配置。G474仅在Bootloader支持的通信接口数量上更多,SPI1的适配逻辑通用。
3. 修正后的跳转代码
移除HAL_FLASH_OB_Launch()调用,直接执行内存跳转逻辑,同时保留资源清理步骤:
/** * @brief Function to jump to STM32 system bootloader * @note This function will not return * @note Flash start address for STM32G4x: 0x08000000 * @note System Bootloader address for STM32G474: 0x1FFF0000 */ void jump_to_bootloader(void) { #define conBootloadAddress 0x1FFF0000 void (*SysMemBootJump)(void); /* 1. Disable all interrupts */ __disable_irq(); /* 2. Disable and deinitialize used peripherals */ HAL_SPI_DeInit(&hspi1); MX_GPIO_DeInit(); /* 3. Reset RCC to default state (disable PLL, use HSI) */ HAL_RCC_DeInit(); /* 4. Clear ALL NVIC pending and enabled interrupts */ for (uint32_t i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++) { NVIC->ICER[i] = 0xFFFFFFFF; NVIC->ICPR[i] = 0xFFFFFFFF; } /* 5. Disable SysTick timer */ SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; /* 6. Set MSP to Bootloader's initial stack pointer */ __set_MSP(*(uint32_t*) conBootloadAddress); /* 7. Jump to Bootloader reset handler */ SysMemBootJump = (void (*)(void)) (*((uint32_t*) (conBootloadAddress + 4))); SysMemBootJump(); /* Never reach here */ while (1); }
4. 额外注意事项
- 确保代码执行在主循环上下文:通过标志位触发时,要确保无中断嵌套或挂起的中断请求。
- 若需永久设置Bootloader启动,可修改选项字节的启动模式,但这不是临时跳转的常规方案。
内容的提问来源于stack exchange,提问作者Raynhardt Van Zyl
相关产品推荐
相关产品推荐

