STM32 之 線上升級(IAP)超詳細圖解 及 常見問題解決
IAP 是啥
IAP( In Application Programming)即在應用程式設計,也就是使用者可以使用自己的程式對MCU的中的執行程式進行更新,而無需藉助於外部燒寫器。其實ST官網也給出了IAP的示例程式,感興趣的可以直接去官網搜尋。
這裡有一點需要特殊注意,就是在MCU中,有一個特殊區域被稱為 System memory。在這塊區域中存放了ST公司自己的 bootloader 程式,它是在MCU出廠時,有ST固化到晶片中的,後續不能再更改。其中的 bootloader 程式也可以對MCU進行升級(DFU對晶片的程式設計應該就是用的這個Bootloader)。而且,晶片不同,BootLoader的功能也是有區別的。ST官網對於這些也是有詳細文件的,後續再寫篇文章介紹這一塊。下圖為部分晶片BootLoader版本及功能
STM32 MCU啟動配置
要實現IAP,首先要了解一下MCU是如何啟動的。這一點在晶片的參考手冊中都有詳細的說明,不同的晶片手冊所在位置可能不同,但是一般在第二章會有單獨一節叫Boot configuration。如下圖:
主要就是說,啟動是通過管腳BOOT0和BOOT1的連線方式來控制的。這個是在硬體設計階段設計好的。不同的配置決定了,MCU將何處對映到0x00000000。從這裡又可以看到一點,MCU眼裡只有0x00000000。至於為啥可以從Flash(0x08000000)啟動,就是因為MCU內部做了對映。從其他位置啟動時同理。
IAP 實現
要實現IAP,則整個程式實現分為大程式(APP)和小程式(IAP)兩部分。其中,APP主要接收升級資料並存儲,IAP處理擦除APP,並重新寫入升級資料。此外,IAP還應該可以獨立接收升級資料的情況。但是,由於Cortex-M0核是沒有中斷向量表偏移暫存器的,這就導致了在Cortex-M0核的MCU上實現線上升級比較麻煩
實際的IAP流程如下:
就是這麼簡單!
注意:
(1)與 Cortex-M3 和 Cortex-M4 不同,Cortex-M0 沒有中斷向量表偏移暫存器(VTOR暫存器)
(2)Cortex-M3 r2p0 及其之前版本,中斷向量表只能位於SRAM或者CODE區域,但是Cortex-M3 r2p1及之後,Cortex-M4 沒有該限制!
(3)MCU根據Boot引腳配置將指定地址對映為0x地址!
IAP 啟動
啟動網上有很多文章介紹,但是或多或少不是很完善,我只做了一張相對來說比較詳細的圖,如下:
Cortex-M核心規定,中斷向量表開始的4個位元組存放的是堆疊棧頂的地址,其後是中斷向量表各中斷服務程式的地址。當發生中斷後程式通過查詢該表得到相應的中斷服務程式入口地址,然後再跳到相應的中斷服務程式中執行,中斷服務程式中最終呼叫使用者實現的各函式。例如:main函式就是復位中斷服務函式中呼叫的!
在沒有IAP時,上電後從0x08000004處取出復位中斷向量的地址,然後跳轉到復位中斷程式的入口(標號①所示),執行結束後跳轉到main函式中(標號②所示)。通常main函式是個死迴圈,不會退出。在執行main函式的過程中發生中斷,則STM32強制將PC指標指回中斷向量表處(標號④所示),從中斷向量表中找到相應的中斷函式入口地址,跳轉到相應的中斷服務函式(標號⑤所示),執行完中斷函式後再返回到main函式中來(標號⑥所示)。
在新增IAP後,上電後仍然從0x08000004處取出復位中斷向量的地址,然後跳轉到復位中斷程式的入口(標號①所示),執行結束後跳轉到小程式的main函式中(標號②所示)。在執行小程式main函式的過程中發生中斷,則STM32強制將PC指標指回中斷向量表處(標號④所示),從中斷向量表中找到相應的中斷函式入口地址,跳轉到相應的中斷服務函式(標號⑤所示),執行完中斷函式後再返回到main函式中來(標號⑥所示)。而想要大程式執行,則必須在小程式中顯示強制跳轉(標號⑦)。
在大程式的main函式的執行過程中,如果CPU得到一箇中斷請求,由於我們設定了中斷向量表偏移量為N+M,因此PC指標被強制跳轉到0x08000004+N+M處的中斷向量表中得到相應的中斷函式地址,再跳轉到相應新的中斷服務函式,執行結束後返回到main函式中來。
需要注意的是,復位中斷比較特殊。產生復位後,PC的值會被硬體強制置為0x08000004。因為,在發生復位後,負責中斷向量偏移的暫存器VTOR變為了0,因此,復位後的中斷就變為了0x08000004。而其他中斷髮生時,VTOR為已經設定好的終端向量表偏移。
程式實現
有了上面的介紹,實現就比較簡單了!其實我有設計了一套適用於全部STM32晶片的IAP模板,但是屬於公司產品,不方便對外公佈!簡單說幾個重點:
- 使用分散載入檔案實現起來會比較方便
- 對於沒有中斷向量表偏移暫存器的MCU(主要是Cortex-M0核),一般採用將中斷向量表複製到指定位置的記憶體中的方式實現:
- 使用分散載入檔案在記憶體中指定一塊區域:
#if (defined ( __CC_ARM )) __IO uint32_t VectorTable[48] __attribute__((section("SECTION_APP_VECTOR"))); #elif (defined (__ICCARM__)) #pragma location = 0x20000000 __no_init __IO uint32_t VectorTable[48]; #elif defined ( __GNUC__ ) __IO uint32_t VectorTable[48] __attribute__((section(".RAMVectorTable"))); #elif defined ( __TASKING__ ) __IO uint32_t VectorTable[48] __at(0x20000000); #endif
- 將APP的終端向量表複製到以上位置,設定中斷向量表重對映
static void SetVectorTable(void) { int i; /*!< At this stage the microcontroller clock setting is already configured, this is done through SystemInit() function which is called from startup file (startup_stm32f0xx.s) before to branch to application main. To reconfigure the default setting of SystemInit() function, refer to system_stm32f0xx.c file */ /* Relocate by software the vector table to the internal SRAM at 0x20000000 ***/ /* Copy the vector table from the Flash (mapped at the base of the application load address 0x08003000) to the base address of the SRAM at 0x20000000. */ for(i = 0; i < 48; i++) { VectorTable[i] = *(__IO uint32_t*)(APP_SPACE_ADDR + (i<<2)); } /* Enable the SYSCFG peripheral clock */ RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* 注意:ST官方例程使用 RCC_APB2PeriphResetCmd是不對的 */ /* Remap SRAM at 0x00000000 */ SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); }
- 在由 IAP 跳轉到 APP 時,最好把 IAP中開啟的外設全部關閉,否則在剛進入 APP中時,如果產生中斷將導致宕機
- STM32 的 back SRAM 在 IAP中和APP中都初始化時,將導致 APP中的初始化不起作用。如果 IAP 中有使用,則在跳轉 APP前必須反初始化。