對於很多人來說,BootLoader並不是一個陌生的詞,甚至會經常用到它。因為在很多情況下我們都需要BootLoader程序,比如我們需要對系統在線升級時就需要它,還有當我們需要在外部存儲器中運行程序時也需要用到它。在這里我們就來設計一個應用於STM32系列MCU的BootLOader程序。
1、BootLoader的基本原理
既然我們想要實現一個面向STM32的BootLOader程序,那么首先我們必須來了解一下BootLOader程序的基本原理。
顧名思義,BootLOader程序肯定是要實現系統的引導,這是BootLOader程序的基本功能。對於STM32系列MCU來說,系統啟動后都會從內部Flash存儲器的起始地址開始執行程序。然后進入應用程序並按既定的順序執行下去,這時BootLoader與應用程序是一體的,具體如圖所示:
但有些時候,我們希望應用程序並不是直接運行,如我們希望對系統實現IAP的時候;或者我們希望應用程序並不在我們的內部Flash中運行;又或者應用程序雖然在內部Flash運行,但我們希望應用程序從我們指定的內部Flash地址運行等等。在這些時候我們就需要一個單獨的BootLoader程序。系統首先啟動BootLoader程序,系統准備就緒后進入到應用程序執行,具體如圖所示:
在上圖中,我們實際上將應用程序存儲在內部Flash的指定位置,這樣做當然是為了實現我們某種需求,如系統IAP。當然我們也可以讓應用程序存儲於外部Flash中,然后BootLoader程序跳轉到外部Flash去執行應用程序,不過前提是外部Flash內購執行程序。具體過程如下:
當然,這只是一種示例,對於不同的存儲器我們只需要修改地址就可以了。至於在BootLoader程序中要實現的功能就看使用情況了。原則上我們可以添加我們任意想要的功能,如硬件檢測、系統升級等等。
2、目標BootLoader設計
我們的目標是實現一個面向STM32的BootLoader程序。那么接下來我們就設計如何實現一個面向STM32的BootLoader程序。
2.1、Flash規划
我們以STM32F407IGT6為目標MCU,這款MCU具有1M的Flash和192K的SRAM。我們將Flash划分為2個部分,一個是啟動程序區(0x0800 0000 - 0x0800 3FFF )大小為16K Bytes,剩下的為應用程序區(0x0800 4000 - 0x080F FFFF)。具體分配如下圖:
我們讓BootLoader程序占用16K的存儲空間。但它是可以操作整個Flash存儲空間的。
2.2、BootLoader程序結構
我們來考慮一下BootLoader程序的結構問題。我們設計BootLoader程序的主要目的就是為了對應用程序進行升級。那么在BootLoader程序中主要需要實現哪些功能呢?有幾個方面是必須要包括的,一是基本的配置,如時鍾等,我們在主程序中實現;二是對Flash的操作,我們升級應用程序肯定會對Flash進行查處和寫入操作;三是跳轉控制程序,我們最終是需要去執行應用程序的,跳轉功能必不可少。當然根據不同的需求可能會有其它的需要。具體如下圖所示:
上圖中,我們除了簽署的三項基本實現外,還添加了IAP文件的獲取功能。這部分功能也是需要的,但在不同的模式下可能會有較大區別。因為獲取文件的方式可以是各類通訊如以太網口、串口等。也可以是各類存儲器,如SD卡、U盤等。所以這一功能雖然必不可少但實現方式則非常靈活,在后續實現中,我們具體問題具體分析。
3、目標BootLoader實現
我們已經確定了Flash的划分,也基本明白了BootLoader的基本工作流程。在接下來我就來討論一下究竟怎么實現一個BootLoader程序。
3.1、BootLoader編碼
我們知道芯片上電時先運行BootLoader程序,然后跳轉到應用程序區執行應用程序。所以我們在編寫BootLoader程序時我們首先判斷系統是否有IAP的需求,如果有IAP請求則進入IAP模式,完成后再跳轉到應用程序執行,如果沒有IAP請求則直接跳轉到應用程序執行。具體流程如下:
關於IAP的處理在不同的情況下會有不同的處理方式,在這里我們主要看一看跳轉控制程序。首先定義應用程序的首地址並聲明一個函數指針類型。具體如下:
#define ApplicationAddress 0x08004000 //應用程序首地址定義
typedef void (*pFunction)(void); //定義跳轉函數指針類型
可能有人要問問什么定義這樣一個函數指針類型,因為我們最終是跳轉到Reset_Handler函數,所以必須要一個可以指向這個函數的函數指針。接下來我們就可以實現跳轉程序了。
1 /*跳轉到應用程序處理函數*/ 2 static void JumpToApplication(void) 3 { 4 uint32_t StackAddr; //應用程序棧地址 5 uint32_t ResetVector; //應用程序中斷向量表的地址 6 7 pFunction JumpToApp; //定義跳轉函數指針 8 9 __set_PRIMASK(1); //關閉全局中斷 10 11 StackAddr = *(__IO uint32_t*)ApplicationAddress; //0x08004000; 12 ResetVector = *(__IO uint32_t*)(ApplicationAddress + 4); //0x08004004; 13 14 if((StackAddr&0x2FFC0000)==0x20000000) //檢查棧頂地址是否合法. 15 { 16 __set_MSP(StackAddr); //初始化應用程序棧指針 17 18 JumpToApp = (pFunction)ResetVector; 19 JumpToApp(); 20 } 21 }
3.2、應用程序處理
實現了BootLoader的編碼后,要想正確的跳轉到App運行,我們還需要對App作相應的修改。重要的修改有2處。如果應用程序是裸機程序則在配置時鍾前我們需要打開全局中端。
/*開啟全局中斷,在BootLoader中關閉的*/
__set_PRIMASK(0);
同時,還需要修改中端向量表的偏移量地址。對於我們所使用的STM32F07可直接在system_stm32f4xx.c文件中修改就可以了。
#define VECT_TAB_OFFSET 0x4000 /*!< Vector Table base offset field.
實現了上述修改並不能達到我們想要的目的,我們還需要在開發環境中做必要的修改。以我們使用的IAR EWARM V8.4為例。修改icf文件中對中斷向量表和Flash存儲區域的設定。具體如下圖:
完成上述配置后我們下載應用程序和BootLoader程序就可以實現正確的跳轉了。
4、小結
本篇中,我們只是實現了一個簡單的BootLoader程序。下載到目標MCU后實現了跳轉,應用程序也正常運行,說明我們的設計是正確的,后續可在次基礎上添加各種功能實現相應的IAP應用。
需要注意的是在BootLoader程序中我們關閉了全局中斷,在應用程序初始化系統時鍾之前一定要記得打開全局中斷,否則SystemTick不能工作會產生硬件故障(hardfault)。不過如果App是運行在RTOS上,則打開中斷可能會出錯,這一點需要注意。