1. 程式人生 > >STM32 之 線上升級(IAP)超詳細圖解 及 常見問題解決

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。如下圖:
BootConfig
主要就是說,啟動是通過管腳BOOT0和BOOT1的連線方式來控制的。這個是在硬體設計階段設計好的。不同的配置決定了,MCU將何處對映到0x00000000。從這裡又可以看到一點,MCU眼裡只有0x00000000。至於為啥可以從Flash(0x08000000)啟動,就是因為MCU內部做了對映。從其他位置啟動時同理。

IAP 實現

  要實現IAP,則整個程式實現分為大程式(APP)和小程式(IAP)兩部分。其中,APP主要接收升級資料並存儲,IAP處理擦除APP,並重新寫入升級資料。此外,IAP還應該可以獨立接收升級資料的情況。但是,由於Cortex-M0核是沒有中斷向量表偏移暫存器的,這就導致了在Cortex-M0核的MCU上實現線上升級比較麻煩

。在實際產品中,整個程式的基本組成結構:
FlashUsage
實際的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 啟動

  啟動網上有很多文章介紹,但是或多或少不是很完善,我只做了一張相對來說比較詳細的圖,如下:
Startup
  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模板,但是屬於公司產品,不方便對外公佈!簡單說幾個重點:

  1. 使用分散載入檔案實現起來會比較方便
  2. 對於沒有中斷向量表偏移暫存器的MCU(主要是Cortex-M0核),一般採用將中斷向量表複製到指定位置的記憶體中的方式實現:
    1. 使用分散載入檔案在記憶體中指定一塊區域:
    	#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
    
    1. 將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);
    }
    
  3. 在由 IAP 跳轉到 APP 時,最好把 IAP中開啟的外設全部關閉,否則在剛進入 APP中時,如果產生中斷將導致宕機
  4. STM32 的 back SRAM 在 IAP中和APP中都初始化時,將導致 APP中的初始化不起作用。如果 IAP 中有使用,則在跳轉 APP前必須反初始化。