STM32执行IAP升级后无法进入中断程序
文章目录
场景
基于
STM32做IAP升级测试
问题现象
从
Boot层跳转后,APP出现卡死,并进入到HardFault
调试过程
-
调用了
HAL_Delay就会出现上述现象,不调用则不会卡死,怀疑是中断没打开,在main前面添加了__enable_irq(),现象一样 -
由于目前的
boot层使用的时基是sysTick,而App层使用的时基是TIM1,理论上应该是没有关系的,但是还是把时基换成一致试试 -
先将
boot层和App层的时基都设置为TIM1,发现App正常 -
先将
boot层和App层的时基都设置为SysTick,发现App又出现卡死现象 -
经调试,猜测是
SysTick配置有问题 -
经检查,发现
App中竟然***没有SysTick的中断服务函数***,回过头去找CubeMX,果然没有勾选Generate IRQ Handler -
勾选后,
App正常(这里没有勾选的原因是,在做IAP之前,系统是上了FreeRTOS的,但是再调试过程中发现IAP功能没实现,于是就将FreeRTOS去掉,只跑裸机,所以这里就有了一个隐患) -
此前,都是基于裸机的调试
-
现在在
App中加入了FreeRTOS后,又出现上述现象 -
更改
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层 //不用管
附录
Cortex-M3权威指南