场景
基于
STM32做IAP升级测试
问题现象
从
Boot层跳转后,APP出现卡死,并进入到HardFault
调试过程
- 调用了
HAL_Delay就会出现上述现象,不调用则不会卡死,怀疑是中断没打开,在main前面添加了__enable_irq(),现象一样(解释:其实不调用也会卡死,只是测试过程中只留意了while(1)前的部分代码,并且这段代码很短,执行结束SysTick都没有触发中断,在while(1)前调用HAL_Delay其实是加长了代码执行时间,从而导致卡死现象提前发生) - 由于目前的
boot层使用的时基是sysTick,而App层使用的时基是TIM1,理论上应该是没有关系的,但是还是把时基换成一致试试 - 先将
boot层和App层的时基都设置为TIM1,发现App正常(解释:boot层未使用SysTick,并未开启SysTick中断,在APP层,有无中断服务函数都不受影响) - 先将
boot层和App层的时基都设置为SysTick,发现App又出现卡死现象(解释:由于boot层打开了SysTick中断,并未关闭,导致在跳转到APP层后,一旦出发SysTick中断就会卡死) - 经调试,猜测是
SysTick配置有问题 - 经检查,发现
App中竟然没有SysTick的中断服务函数,回过头去找CubeMX,果然没有勾选Generate IRQ Handler

- 勾选后,
App正常(这里没有勾选的原因是,在做IAP之前,系统是上了FreeRTOS的,但是再调试过程中发现IAP功能没实现,于是就将FreeRTOS去掉,只跑裸机,所以这里就有了一个隐患) - 此前,都是基于裸机的调试
- 现在在
App中加入了FreeRTOS,并使用FreeRTOS内部实现的SysTick中断服务函数后,又出现上述现象(解释:在启用调度器之前,boot层开启了SysTick中断,并没有关闭) - 更改
App中断向量表,单独烧录到MCU中,App能正常运行 - 然后恢复到第
9步的状态,再次调试发现,App在FreeRTOS的xPortSysTickHandler()->xTaskIncrementTick->xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );卡死,并且查看内存发现这里的变量的值有些奇怪,并且指针不在RAM访问范围0x20000000-0x20010000内:



- 结合
FreeRTOS的源码,发现pxDelayedTaskList只有在启动任务调度器,进入到prvInitialiseTaskLists()->pxDelayedTaskList = &xDelayedTaskList1;后,指向的地址才有合法性 - 所以,怀疑是运行
App之前,SysTick的中断是打开的,所以才会进入中断服务函数,从而导致内存非法访问,导致卡死 - 现在,只需在
Boot层,跳转到App之前,调用__disable_irq(),就可以了:
void App_Jump_To_App(uint32_t app_addr) {
__disable_irq(); //关闭总中断
volatile uint32_t jump_addr = *(volatile uint32_t *) (FLASH_APP_CODE_ADDR + 4);
jumpapp = (pjumfunc) jump_addr; //设置MSP指针
__set_MSP(*(volatile uint32_t *) FLASH_APP_CODE_ADDR); //设置栈指针
jumpapp(); //跳转
}
- 为什么不上
RTOS,并且在Boot层不关闭中断,App也能正常运行???因为在App中,使用的时基是Timebase是TIM1,所以SysTick的中断服务函数里什么也没做,所以即使在跳转到App之前,SysTick的中断是开启的,也不影响App - 在
Boot层完成修改之后,调用HAL_Delay又出现了卡死现象 - 这次经调试,发现
TIM1时钟没法进入中断,(HAL_Delay的时基是TIM1),应该是TIM1的中断没生效,联系到之前对Boot层的改动,猜测在Boot层调用了__disable_irq(),对App中的TIM1有影响 - 经上网搜索
__disable_irq()都干了什么事,得知实际上是将primask置1,由此推断,在Boot层执行关闭中断操作后,导致App的TIM1中断不能响应:

- 但是为什么去掉
HAL_Delay,App也能运行,是因为FreeRTOS中,在开启任务调度器的时候,会执行prvStartFirstTask,在这个函数中会执行一个开中断,将primask置0:

结论
-
所以,综合上述问题,最终有两种方法比较好,我选择的方法二
-
方法一:在
Boot层跳转App之前,调用__disable_irq()关闭总中断,然后在App层系统初始化完成后,立即调用__enable_irq()打开总中断//Boot层 void App_Jump_To_App(uint32_t app_addr) { __disable_irq(); //关闭总中断 volatile uint32_t jump_addr = *(volatile uint32_t *) (FLASH_APP_CODE_ADDR + 4); jumpapp = (pjumfunc) jump_addr; __set_MSP(*(volatile uint32_t *) FLASH_APP_CODE_ADDR); jumpapp(); } //App层 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); __enable_irq(); //开启总中断 while (1) { printf("hello world\n"); HAL_Delay(1000); } } -
方法二:在
Boot层跳转App前,只关闭Boot层使用到的中断,不去操作其他中断//Boot层 void App_Jump_To_App(uint32_t app_addr) { //这里只需关闭SysTick中断,并将其寄存器置清零 SysTick->CTRL = 0; SysTick->VAL = 0; SysTick->LOAD = 0; volatile uint32_t jump_addr = *(volatile uint32_t *) (FLASH_APP_CODE_ADDR + 4); jumpapp = (pjumfunc) jump_addr; __set_MSP(*(volatile uint32_t *) FLASH_APP_CODE_ADDR); jumpapp(); } //App层 //不用管
-
评论区