【问题标题】:In-Application Program jump from user bootloader to user application and viceversa on STM32F446RE board在 STM32F446RE 板上,应用内程序从用户引导加载程序跳转到用户应用程序,反之亦然
【发布时间】:2021-01-16 14:29:41
【问题描述】:

我有一个问题。 我正在为我的 STM32F446RE 板开发 IAP(应用内编程)工具,但我被卡住了。 我已经开发了所有必要的实用程序,以便让微控制器从 GUI 接收二进制 (.bin) 编译文件,将其写入特定的闪存扇区并执行它。 我的问题是,从上传的代码中,我想再次跳转到存储在闪存扇区 0 上的引导加载程序,我看到代码没有跳转到引导加载程序,而是继续执行用户应用程序代码。我调试了代码,发现引导加载程序代码的所有地址(msp 和重置处理程序)都已正确设置,并且与上传的代码相比,它们有所不同。

我想要实现的流程如下:

1 --> 执行存储在扇区 0 上的引导加载程序代码(从地址 0x0800 0000 开始,当接收到来自用户按钮的中断时)并将新接收到的代码写入扇区 2(从地址 0x0800 8000 开始)

2 --> 设置msp地址(@0x0800 8000)和reset handler地址(0x0800 8004)

3 --> 跳转到新代码的reset handler地址(@0x0800 8004)

4 --> 执行新上传的代码。

5 --> 在用户代码执行期间,如果接收到中断(来自用户按钮),则设置引导加载程序 msp 地址、复位处理程序并跳转到引导加载程序

6 --> 从第一步开始重复。

这是用于从引导加载程序跳转到用户应用程序的代码:

    IAP_loadProgram(&data);

//pointer to the user application reset handler address
void (*user_resetHandler)(void);


//set the user application MSP address (user application starts on the flash SECTOR2
uint32_t msp_addr = *(volatile uint32_t *)APPLICATION_ADDRESS;

__set_MSP(msp_addr);

//Set now the addres of the reset handler
uint32_t resetAddr = *(volatile uint32_t *)(APPLICATION_ADDRESS + 4);

user_resetHandler = (void *)resetAddr;

//When there, the bootloader sector will be leaved and the user code execution starts
user_resetHandler();

最后,用于从用户应用程序代码跳转到引导加载程序的代码是:

  if(toBootloader){
      toBootloader = 0;

      //pointer to the user application reset handler address
      void (*bootLoader_resetHandler)(void);

      //set the user application MSP address (user application starts on the flash SECTOR2
      uint32_t msp_addr = *(volatile uint32_t *)BOOTLOADER_ADDRESS;

     __set_MSP(msp_addr);

      //Set now the address of the reset handler
      uint32_t bootLoaderResetAddr = *(volatile uint32_t *)(BOOTLOADER_ADDRESS + 4);

      bootLoader_resetHandler = (void *)bootLoaderResetAddr;

      //When there, the user code sector will be leaved and the bootloader code execution starts
      bootLoader_resetHandler();
  }

其中 APPLICATION_ADDRESS 为 0x0800 8000,BOOTLOADER_ADDRESS 为 0x0800 0000。 bootloader代码前两个地址的内容为: 0x08000000: 20020000
0x08000004: 080044DD

同时应用代码前两个地址的内容为: 0x08008000: 20020000
0x08008004: 0800A1F1

我所做的最后修改是在用户应用程序链接器 (.ld) 文件上,我将闪存开始设置为地址 0x0800 8000(而不是地址 0x0800 0000)。

所有中断都正常工作,在上传代码之后,如果我进行硬件复位,结果是一样的,代码执行从用户应用程序代码开始,而不是从引导加载程序开始。 有什么建议吗?

【问题讨论】:

  • 您没有更改 VTOR - 这意味着您的向量表没有被更改。应用程序不太可能具有与引导加载程序完全相同的中断处理程序。您的逻辑很奇怪,对问题的描述不清楚。
  • 要进行跳转(无论哪种方式),您都应该从向量表中加载 SP 和 PC,而不是使用将返回地址压入堆栈的函数调用。你的方法还有很多问题。

标签: c embedded bootloader flash-memory


【解决方案1】:

从应用程序运行引导加载程序最简单、最安全的方法是使用 CMSIS NVIC_SystemReset() 函数发出软复位。

if( toBootloader )
{
    NVIC_SystemReset() ;
}

通过直接调用跳转到引导加载程序是不必要且不明智的。虽然可以做到,就像您可以从引导加载程序跳转到应用程序一样,您至少需要禁用中断/异常并将向量表从应用程序的向量表切换到引导加载程序的向量表。您的应用程序代码和引导加载程序代码似乎都没有这样做。例如,请参阅ARM: How to Write a Bootloader

发出复位的好处是将处理器和所有片上外设和 I/O 设置为已知的复位状态,因此您无需担心取消初始化 NVIC 或任何可能产生中断的外设在切换向量表时。

如果您需要从应用程序向引导加载程序传递信息,片上 SRAM 的状态将在复位过程中继续存在,因此您可以保留运行时启动不会初始化的空间以将参数传递给如果需要,引导加载程序。

【讨论】:

    【解决方案2】:

    您的问题描述不清楚,但调用应用程序的过程还远远不够。您需要确保应用程序的环境与 uC 重置后的环境相同。您还需要更改向量表地址。

    我写了几十个引导加载程序,但我不明白你的问题

    这里有一个应该如何完成的示例(来自引导加载程序的应用调用)

    void startAPP(void)
    {
        static uint32_t *pAppPosition;
        static voidFunc *appResetHandler;
        static uint32_t newSP;
    
        pAppPosition = (uint32_t *)(bankStartAddress[0] + (uint32_t)&_BOOTFlashSize);
        appResetHandler = (voidFunc *)pAppPosition[1];
        newSP = pAppPosition[0];
    
        SPI_DeInit();
        FLASH_DeInit();
        I2C_DeInit();
        IRQ_DeInit();
        GPIO_DeInit();
    
        __disable_irq();
        __set_MSP(newSP);
        __enable_irq();
        SCB -> ICSR = 0x00000000;   // reset value;
        SCB -> SCR = 0;
        SCB -> CCR = 0x00000200;    // reset value
        SCB -> SHP[0] = 0;
        SCB -> SHCSR = 0;
        SCB -> CFSR = (SCB_CFSR_DIVBYZERO_Msk | SCB_CFSR_UNALIGNED_Msk | SCB_CFSR_UNDEFINSTR_Msk | SCB_CFSR_NOCP_Msk | SCB_CFSR_INVPC_Msk | SCB_CFSR_INVSTATE_Msk);
        SCB -> HFSR = (SCB_HFSR_DEBUGEVT_Msk | SCB_HFSR_FORCED_Msk | SCB_HFSR_VECTTBL_Msk);
        SCB -> VTOR = bankStartAddress[0] + (uint32_t)&_BOOTFlashSize;  // new vector table pos. I od not clear 8 LSB because APP start position is aligned to FLASH Sectors which are at least 2k aligned
    
        // SysTick
        SysTick -> CTRL = 0;
        SysTick -> LOAD = 0;
        SysTick -> VAL  = 0;
    
        appResetHandler();
    
        __builtin_unreachable();
    }
    

    【讨论】:

    • Vector 表地址更改为声明它的文件:在 STM 实现中,地址计算为: CODE_STARTING_ADDRESS | OFFSET 和我将偏移值更改为 0x8000(它必须是 0x200 的乘积)(仅在用户应用程序文件中,因为代码存储在第二个扇区中,而引导加载程序存储在扇区 0 中,因此不会更改)。唯一缺少的是上传SCB的价值。谢谢!我会以更清晰的方式再试一次
    • 宁可CODE_STARTING_ADDRESS & OFFSET
    猜你喜欢
    • 2020-07-10
    • 2019-12-26
    • 2017-11-03
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    相关资源
    最近更新 更多