STM32F103从Bootloader跳转后在osKernelStart()处死机,直接烧录至对应地址却可正常运行
STM32F103从Bootloader跳转后在osKernelStart()处死机,直接烧录至对应地址却可正常运行
这种情况我之前也碰到过几次,核心问题基本都出在跳转前后的硬件状态清理不彻底或者向量表切换时机不对上,毕竟单独烧录时系统是从冷启动走完整初始化流程,但从Bootloader跳转是热启动,很多遗留状态会干扰RTOS的启动。结合你的描述和代码,我给你梳理几个排查和解决方向:
一、先解决向量表切换的核心问题
你的应用虽然在system_stm32f1xx.c里配置了向量表偏移,但这个配置是在应用的SystemInit()里执行的——而从Bootloader跳转时,你先调用了应用的复位 handler,再执行SystemInit(),这意味着在SystemInit()执行前,CPU的中断向量还是指向Bootloader的地址。CMSIS-RTOS v1启动时会用到SysTick、PendSV这些中断,一旦触发就会跑回Bootloader的中断服务程序,直接导致卡死。
修改跳转函数,提前切换向量表:
在设置应用MSP之后、调用复位handler之前,手动把向量表地址切换到应用的FLASH起始地址:
#define FLASH_APPLICATION_LOCATION 0x0800C800U void OTATask::_otaGotoApplication() { // 第一步:禁用全局中断,防止跳转过程中触发中断打乱流程 __disable_irq(); // 关闭所有外设中断并清除挂起的中断标志,避免遗留中断触发 for (uint32_t i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++) { NVIC->ICER[i] = 0xFFFFFFFF; // 禁用所有中断通道 NVIC->ICPR[i] = 0xFFFFFFFF; // 清除所有挂起的中断请求 } // 重置SysTick,彻底清理Bootloader的SysTick状态 SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; // 重置RCC和HAL,确保硬件状态回到初始 HAL_RCC_DeInit(); HAL_DeInit(); // 设置应用的主栈指针(MSP) __set_MSP(*(volatile uint32_t*) FLASH_APPLICATION_LOCATION); // 关键:手动切换向量表到应用的FLASH起始地址 SCB->VTOR = FLASH_APPLICATION_LOCATION; // 获取并跳转到应用的复位handler void (*app_reset_handler)(void) = (void (*)(void))(*((volatile uint32_t*) (FLASH_APPLICATION_LOCATION + 4U))); app_reset_handler(); }
二、检查应用的RTOS初始化流程
确保你的应用初始化顺序完全正确,RTOS相关的初始化不能提前:
int main(void) { // 1. 先初始化HAL HAL_Init(); // 2. 配置系统时钟(必须和Bootloader一致,你已经确认过这点) SystemClock_Config(); // 3. 初始化外设(比如你的LED GPIO) MX_GPIO_Init(); // 4. 初始化RTOS内核 osKernelInitialize(); // 5. 创建所有任务 osThreadDef(ledTask, StartLEDTask, osPriorityNormal, 0, 128); ledTaskHandle = osThreadCreate(osThread(ledTask), NULL); // 6. 最后启动RTOS内核 osKernelStart(); // 这里理论上永远不会执行到 while (1) { } }
三、额外排查点
- 确认FLASH_APPLICATION_LOCATION宏的值:必须和链接脚本里的
FLASH ORIGIN完全一致(即0x0800C800),如果宏定义错了,跳转地址不对直接就会死机。 - 检查RTOS的SysTick配置:CMSIS-RTOS v1默认依赖SysTick做时间片调度,你可以在应用的
HAL_Init()之后手动强制配置SysTick:HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000); - 验证栈空间:虽然你的链接脚本分配了48K RAM,但要确保应用的栈大小配置足够,RTOS任务的栈大小(比如你设置的128)也要匹配实际需求(不过你的LED任务很小,128足够)。
按上面的步骤修改后,应该就能解决跳转后osKernelStart()卡死的问题了。
备注:内容来源于stack exchange,提问作者Nhat Micls
相关产品推荐
相关产品推荐

