天天看點

關于STM32的IAP程式設計的一些記錄--BootLoader與APP互相跳轉

背景

在給STM32單片機做IAP(Flash分為兩部分:BootLoader+APP)更新功能時,有如下需求- -

  1. 系統上電預設先進入BootLoader中,BootLoader會停留幾百毫秒;
  2. 停留期間,系統會判斷是否具有IAP請求,如果有則進行IAP更新,否則逾時後跳轉到APP中即可;

這樣得邏輯很簡單,但是有一個缺陷,就是不管上電還是複位後始終必須停留幾百毫秒後才能跳轉至APP,對于是實作要求高的、跑飛後需要立刻進行複位重新開機的系統,是不合理的。是以可以進行一個優化:

  • 在需求2的基礎上,先判斷複位類型是上電店複位還是軟體複位,如果是軟體複位,直接跳轉至APP,不停留。

關鍵代碼實作

以stm32f4為例,修改system_stm32f4xx.c中的SystemInit函數

void SystemInit(void)
{
    /* USER CODE BEGIN 1 */
    // 判斷 app是否存在标志,app存在時跳轉的前提
    uint8_t isAppExistFalg = *(__IO uint8_t*)(标記app是否存在的位址));
    // 判斷是否為軟體複位,軟體複位就直接跳轉到app中
    if( (1 == isAppExistFalg) && ((RCC->CSR) & 0x10000000) )
    {
       __HAL_RCC_CLEAR_RESET_FLAGS();

       extern void jump2aplication(void);
       jump2aplication();
    }
    /* USER CODE END 1 */
    ...
    省略
    ...
  // 注意:編譯APP時,宏VECT_TAB_OFFSET要設定正确的偏移量,設定方法見文末
  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
           

其中 函數jump2aplication()實作為:

typedef void (* pFunc)(void);

void  jump2aplication(void)
{
    // 确定應用程式入口位址
    pFunc jump_to_application = (pFunc)(*(__IO uint32_t*)(APP的起始位址值 + 4));

    // 檢查應用程式堆棧位址是否合法
    if(((*(__IO uint32_t *)APP的起始位址值) & 0x2FF80000) == 0x20000000)
    {
        // 初始化MSP
        __set_MSP(*(__IO UINT32 *)APP的起始位址值);

        // 跳轉到應用程式
        jump_to_application(); 
    }

}
           

通過以上的修改,就可以實作BootLoader到APP的跳轉。

更進階功能

現在,仔細思考後發現:如果想更新,就隻能将裝置斷電,然後在上電的幾百毫秒内握手更新(不然逾時跳轉到APP中)。能不能做到讓APP主動跳轉到BootLoader裡面,這樣不就可以随時進行更新?當然可以。

直接上代碼:

typedef void (* pFunc)(void);

void  jump2bootloader(void)
{
    // 确定應用程式入口位址, FLASH_BASE=0x08000000,即stm32代碼預設起始位址
    pFunc jump_to_bootloader = (pFunc)(*(__IO uint32_t*)(FLASH_BASE + 4));

    // 檢查應用程式堆棧位址是否合法
    if(((*(__IO uint32_t *)FLASH_BASE ) & 0x2FF80000) == 0x20000000)
    {
        // 是所有裝置的設定恢複到初始狀态,否則跳轉後可能産生硬錯誤
        __disable_irq();
        HAL_RCC_DeInit();
        
        // 初始化MSP
        __set_MSP(*(__IO UINT32 *)FLASH_BASE );

        // 跳轉到應用程式
        jump_to_bootloader(); 
    }

}
           

注意:APP中一般會對外設、始終及中斷做一系列配置,跳轉至BootLoader後,并未經過複位操作,是以必須要調用HAL_RCC_DeInit()将所有外設和始終配置恢複初始化狀态。我初次應用時就沒有調用HAL_RCC_DeInit(),導緻在main函數的 void SystemClock_Config(void)函數中進入錯誤死循環。至于APP,跳轉至APP一般在複位或上電時刻,所有不執行HAL_RCC_DeInit()也無所謂。

參考

  • 做APP跳轉到BootLoader功能時,碰到上面提到的死循環問題時,多虧這篇部落客的文章:關于STM32的IAP與APP互相跳轉
  • 如果發現跳轉至APP後始終跑飛的,可以思考一下app的相關偏移是否修改?修改方法可以參考我的另一篇部落格STM32 CubeIDE中修改APP起始位址的方法

繼續閱讀