STM32F405RGT6 Bootloader跳转后卡死问题排查求助
问题
使用STM32F405RGT6芯片,Bootloader跳转函数如下:
static void Boot_Jump_To_App(uint32_t app_addr) { uint32_t app_msp = *((uint32_t *)app_addr); uint32_t app_reset = *((uint32_t *)(app_addr + 4U)); BootEntry_t app_entry = (BootEntry_t)app_reset; /* Stop SysTick used by bootloader */ SysTick->CTRL = 0U; SysTick->LOAD = 0U; SysTick->VAL = 0U; SCB->ICSR = SCB_ICSR_PENDSVCLR_Msk | SCB_ICSR_PENDSTCLR_Msk | SCB_ICSR_PENDSVCLR_Msk; /* Disable and clear all NVIC IRQs to avoid pending bootloader interrupts */ for (uint32_t i = 0; i < 8U; i++) { NVIC->ICER[i] = 0xFFFFFFFFU; NVIC->ICPR[i] = 0xFFFFFFFFU; } /* Optional but often helpful for clean handoff */ HAL_RCC_DeInit(); HAL_DeInit(); __disable_irq(); /* Point vector table to application */ SCB->VTOR = app_addr; __DSB(); __ISB(); /* Load app stack and branch */ __set_MSP(app_msp); __DSB(); __ISB(); /* Keep IRQs disabled here; app startup will enable when ready */ //app_entry(); __ASM volatile ("bx %0" : : "r"(app_entry)); while(1); }
为调试目的,将跳转目标设为Bootloader自身(app_addr = 0x08000000)。通过CMSIS-DAP调试发现app_msp、SP、app_reset、app_entry均正常。在__ASM volatile ("bx %0" : : "r"(app_entry));处设断点可正常暂停,但点击运行后无响应,停止调试后发现停在Disassembly的MemManage_Handler,Stack+local显示为HardFault_Handler。
reset_handler地址为0x0800019D,与0x8000004地址的内容一致,Bootloader的.map文件显示0x0800019c属于startup_stm32f405xx.o(.text)段。程序在卡死前未进入systeminit(),使用Keil5搭配ARM编译器,Bootloader仅使用UART外设实现printf串口输出,请问该如何调试此问题?
调试建议
- 检查栈指针合法性:虽然
app_msp读取正常,但跳转后栈可能被破坏。在__set_MSP(app_msp)后,直接查看SP寄存器值是否等于app_msp,同时确认0x08000000处的栈顶地址是否在芯片合法RAM范围内(STM32F405的RAM起始为0x20000000,大小192KB,栈顶需落在这个区间)。 - 排查HAL初始化残留问题:
HAL_RCC_DeInit()和HAL_DeInit()可能没有完全复位所有外设状态。尝试注释这两个函数,直接跳转测试,看是否还会触发HardFault。如果注释后正常,说明DeInit过程中某个外设寄存器操作导致了异常。 - 验证VTOR设置有效性:在
SCB->VTOR = app_addr后,读取SCB->VTOR寄存器确认值是否正确。另外,STM32F4的VTOR需要设置为向量表起始地址,且必须对齐到0x200的倍数(0x08000000符合要求),但要确保芯片的FLASH访问权限没有被错误配置。 - 检查跳转指令的状态:
bx指令要求目标地址的最低位为1(表示Thumb模式),app_reset是0x0800019D,最低位为1,符合Thumb模式要求,但可以在汇编窗口确认跳转时的PSR寄存器的T位是否为1,确保处于Thumb状态。 - 启用HardFault调试机制:在Bootloader中添加HardFault异常捕获函数,保存
SCB->CFSR、SCB->HFSR等寄存器的值,通过串口打印出来。这些寄存器可以明确是内存管理错误(MemManage)还是总线错误,定位具体异常原因。示例代码:
void HardFault_Handler(void) { uint32_t cfsr = SCB->CFSR; uint32_t hfsr = SCB->HFSR; printf("HardFault! CFSR: 0x%08X, HFSR: 0x%08X\r\n", cfsr, hfsr); while(1); }
- 检查编译器优化和链接配置:Keil中如果开启了过高的优化等级,可能导致跳转代码被错误优化。尝试将跳转函数的优化等级设为
-O0(无优化),同时检查链接脚本中向量表的配置是否正确,确保0x08000000处确实是向量表起始。 - 模拟冷启动流程:跳转回自身时,相当于二次启动,而Bootloader的初始化代码可能没有处理二次初始化的情况。比如某些全局变量已经被初始化过,二次执行
startup_stm32f405xx.s中的初始化代码时,可能出现内存覆盖问题。可以在跳转前手动重置所有全局变量到初始值,或者修改启动代码,允许重复初始化。
内容的提问来源于stack exchange,提问作者li_jessen
相关产品推荐
相关产品推荐

