STM32执行IAP升级后无法进入中断程序

文章目录

  • 场景
  • 问题现象
  • 调试过程
  • 结论
  • 附录
  • 场景

    基于STM32IAP升级测试

    问题现象

    Boot层跳转后,APP出现卡死,并进入到HardFault

    调试过程

    1. 调用了HAL_Delay就会出现上述现象,不调用则不会卡死,怀疑是中断没打开,在main前面添加了__enable_irq(),现象一样(解释:其实不调用也会卡死,只是测试过程中只留意了while(1)前的部分代码,并且这段代码很短,执行结束SysTick都没有触发中断,在while(1)前调用HAL_Delay其实是加长了代码执行时间,从而导致卡死现象提前发生)

    2. 由于目前的boot层使用的时基是sysTick,而App层使用的时基是TIM1,理论上应该是没有关系的,但是还是把时基换成一致试试

    3. 先将boot层和App层的时基都设置为TIM1,发现App正常(解释:boot层未使用SysTick,并未开启SysTick中断,在APP层,有无中断服务函数都不受影响)

    4. 先将boot层和App层的时基都设置为SysTick,发现App又出现卡死现象(解释:由于boot层打开了SysTick中断,并未关闭,导致在跳转到APP层后,一旦出发SysTick中断就会卡死)

    5. 经调试,猜测是SysTick配置有问题

    6. 经检查,发现App中竟然***没有SysTick的中断服务函数***,回过头去找CubeMX,果然没有勾选Generate IRQ Handler

    1. 勾选后,App正常(这里没有勾选的原因是,在做IAP之前,系统是上了FreeRTOS的,但是再调试过程中发现IAP功能没实现,于是就将FreeRTOS去掉,只跑裸机,所以这里就有了一个隐患)

    2. 此前,都是基于裸机的调试

    3. 现在在App中加入了FreeRTOS,并使用FreeRTOS内部实现的SysTick中断服务函数后,又出现上述现象(解释:在启用调度器之前,boot层开启了SysTick中断,并没有关闭)

    4. 更改App中断向量表,单独烧录到MCU中,App能正常运行

    5. 然后恢复到第9步的状态,再次调试发现,AppFreeRTOSxPortSysTickHandler()->xTaskIncrementTick->xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );卡死,并且查看内存发现这里的变量的值有些奇怪,并且指针不在RAM访问范围0x20000000-0x20010000内:


    1. 结合FreeRTOS的源码,发现pxDelayedTaskList只有在启动任务调度器,进入到prvInitialiseTaskLists()->pxDelayedTaskList = &xDelayedTaskList1;后,指向的地址才有合法性

    2. 所以,怀疑是运行App之前,SysTick的中断是打开的,所以才会进入中断服务函数,从而导致内存非法访问,导致卡死

    3. 现在,只需在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();	//跳转
      
      }
      
    4. **为什么不上RTOS,并且在Boot层不关闭中断,App也能正常运行???**因为在App中,使用的时基是TimebaseTIM1,所以SysTick的中断服务函数里什么也没做,所以即使在跳转到App之前,SysTick的中断是开启的,也不影响App

    5. Boot层完成修改之后,调用HAL_Delay又出现了卡死现象

    6. 这次经调试,发现TIM1时钟没法进入中断,(HAL_Delay的时基是TIM1),应该是TIM1的中断没生效,联系到之前对Boot层的改动,猜测在Boot层调用了__disable_irq(),对App中的TIM1有影响

    7. 经上网搜索__disable_irq()都干了什么事,得知实际上是将primask1,由此推断,在Boot层执行关闭中断操作后,导致AppTIM1中断不能响应:

    8. 但是为什么去掉HAL_DelayApp也能运行,是因为FreeRTOS中,在开启任务调度器的时候,会执行prvStartFirstTask,在这个函数中会执行一个开中断,将primask0

    结论

  • 所以,综合上述问题,最终有两种方法比较好,我选择的方法二

    1. 方法一:在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);
        	}
      }
      
    2. 方法二:在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权威指南
    项目地址

    作者:六月雨qin

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32执行IAP升级后无法进入中断程序

    发表评论