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
权威指南