bootloader下载APP后不能正常跳转

发布于 2020-10-13 14:01:58

image.png
三个没有什么用处的打印函数,任意屏蔽一个就会导致不能正常跳转,这是为啥啊

查看更多

关注者
1
被浏览
700
2 个回答
hichard
hichard 认证专家 2020-10-13

下面的流程基本适用于所有的处理器,具体代码实现以Cortex-M为例。
在Cortex-M处理器上,Bootloader程序可以使用RTOS,也可以裸机实现,但是都要遵循以下流程。现在总结如下:
1.内核级,实现堆栈切换,跳转指令
2.NVIC级别,关闭所有的NVIC中断,清空所有pend的中断
3.具体芯片级,复位必要的外设,尤其是EXTI这类。SDRAM QSPI FLASH这类不需要复位,也不能复位。
下面着重讲解上面三级代码的具体实现,这3步是相互嵌套是关系。最终用户调用的都具体芯片级别的跳转函数。
1.内核级
实现函数__hal_app_jump,该函数的C语言声明为:extern void __hal_app_jump(rt_uint32_t u32AppAddr);
具体源码是汇编语言实现的,如下:

__hal_app_jump
    CPSID   I                       ;  关闭中断

    LDR     R1, =0xE000ED08         ;  切换向量表到应用程序的起始地址
    STR     R0, [R1]

    MOV     R2, #0x00               ;  切换工作模式,工作到特权级的线程模式  使用MSP
    MSR     CONTROL, R2

    LDR     R1, [R0]                ;  设置堆栈指针
    MOV     SP, R1

    CPSIE   I                       ;  使能中断
    LDR        R0, [R0, #4]        ;  设置当前PC的值,开始运行新的应用程序
    BX      R0

通过注释,可以很清晰的明白本函数的功能,该函数完整实现了bootloader的跳转流程,一些裸机场合,有些可能就不需要实现。
2.NVIC级别
先上代码,在根据代码来讲解。

void hal_app_jump(rt_uint32_t u32AppAddr)
{
          /**
          * Step1, 关闭中断
          */
          __asm("  CPSID   I\n");
          
          /**
          * Step2, 关闭NVIC中的所有中断
          */
          memset((void *)HAL_NVIC_DIS_BASE, 0, sizeof(rt_uint32_t) * 8);
          
            /**
          * Step3, 清空NVIC中已经Pending中的所有中断
          */
          memset((void *)HAL_NVIC_PEND_CLEAR_BASE, 0, sizeof(rt_uint32_t) * 8);
          
          /**
          * Step4, 清空Systick中断及已经挂起的中断
          */
          HWREG32(HAL_NVIC_ST_CTRL) = 0;
          HWREG32(HAL_NVIC_INT_CTRL) |= ((1UL << 25) | (1UL << 27));
          
          /**
          * Step5, 执行跳转流程
          */
          __hal_app_jump(u32AppAddr);
}

可以看到NVIC级别,分5步,如果是非Cortex-M处理器,也是这5步,主要是对中断控制器清零,复位操作,避免在跳转的瞬间正好遇上了中断,造成乱跳转,跑飞。第五步
调用了步骤1的函数,执行真正的跳转。

3.具体芯片级别,
先上代码,再来谈具体实现。

void rt_hw_app_jump(rt_uint32_t u32AppAddr)
{
          /**
          * Step1, 关闭中断
          */
          rt_hw_interrupt_disable();
          
          /**
          * Step2, 如果需要的话,复位除GPIO、SYSCFG、PWR和QSPI外的所有外设
          */ 
          //SystemPeripheralAllReset();
          HWREG32(RCC_BASE + RCC_AHB3RSTR) = 0x7FFFBFFF;
          HWREG32(RCC_BASE + RCC_AHB1RSTR) = 0xFFFFFFFF;
          HWREG32(RCC_BASE + RCC_AHB2RSTR) = 0xFFFFFFFF;
          HWREG32(RCC_BASE + RCC_AHB4RSTR) = 0xFFFF0000;
          HWREG32(RCC_BASE + RCC_APB3RSTR) = 0xEFFFFFFF;
          HWREG32(RCC_BASE + RCC_APB1LRSTR) = 0xFFFFFFFF;
          HWREG32(RCC_BASE + RCC_APB1HRSTR) = 0xEFFFFFFF;
          HWREG32(RCC_BASE + RCC_APB2RSTR) = 0xFFFFFFFF;
          HWREG32(RCC_BASE + RCC_APB4RSTR) = 0xEFFFFFFD;
          
          HWREG32(RCC_BASE + RCC_AHB3RSTR) = 0;
          HWREG32(RCC_BASE + RCC_AHB1RSTR) = 0;
          HWREG32(RCC_BASE + RCC_AHB2RSTR) = 0;
          HWREG32(RCC_BASE + RCC_AHB4RSTR) = 0;
          HWREG32(RCC_BASE + RCC_APB3RSTR) = 0;
          HWREG32(RCC_BASE + RCC_APB1LRSTR) = 0;
          HWREG32(RCC_BASE + RCC_APB1HRSTR) = 0;
          HWREG32(RCC_BASE + RCC_APB2RSTR) = 0;
          HWREG32(RCC_BASE + RCC_APB4RSTR) = 0;
          
          /**
          * Step3, 执行跳转
          */
          rt_hw_cpu_icache_disable();
          rt_hw_cpu_dcache_disable();
          HWREG32(HAL_ACCESS_CACR) &= ~(((rt_uint32_t)1) << 2); //  禁止D-CACHE透写
          hal_app_jump(u32AppAddr);
}

具体芯片级别分了3步,该函数是H7下的例子。该步骤主要就是清除具体是外设,让CPU恢复到最初上电时的状态。第三步执行了NVIC级别的跳转函数。应用层只要调用该函数即可实现跳转。
如,如果要跳转到0x08020000处执行代码,则可以运行代码:rt_hw_app_jump(0x08020000),,严谨一点,跳转代码写如下:

 if((HWREG32(0x08020000) != 0xFFFFFFFF) && 
     (HWREG32(0x08020000) >= 0x20000000) && 
       (HWREG32(0x08020000) < STM32_HEAP_END) && 
         (HWREG32(0x08020000 + 4) != 0xFFFFFFFF) && 
           (HWREG32(0x08020000 + 4) >= 0x08020000))
  {
    rt_hw_app_jump(HN_APP_ADDR);
  }

4.其他问题
上面描述基本已经可以实现一个完整的BootLoader跳转了。细心的读者可能发现,复位外设,缺没有复位GPIO等外设,这里其实可能一些潜在隐患。如果BootLoader里面使用了GPIO中断,
在跳转的瞬间,由于静电干扰造成触发了中断信号,也可能造成跳转失败。这里比较妥当的做法当然的将EXIT的GPIO中断关闭,这通常是在按键驱动实现的。建议的是,如果在BootLoader中open了某个
外设,那么跳转前,请先close掉,在跳转。其它特殊的硬件,如有存在外部看门狗,不能关闭,在跳转前最好先喂狗一次,避免跳转的时候发生看门狗溢出,造成跳转不成功。

读完本文,读者是不是发现跳转没有那么简单。跳转异常基本都是跳转瞬间发生中断引起,只要控制好中断,就能控制好跳转流程。

hichard

xiaoyuan_svip
xiaoyuan_svip 2020-10-21

知道跳转的逻辑,但是就是跳转不成功,哈哈哈

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览