目 錄
1. 前言
2. 初識BootLoader
2.1 百度百科的BootLoader
2.2 BootLoader的簡單理解
2.3 BootLoader的做用
3. BootLoader預備知識
3.1 復位序列
3.1.1 棧指針
3.1.2 復位向量
3.2 重定位中斷向量表
3.2.1 STM32的中斷向量表
3.2.2 設置中斷向量表偏移
3.3 分散加載文件相關
3.3.1 C語言的函數地址
3.3.2 BootLoader佔用的ROM
3.3.3 修改ROM起始地址
3.4 hex文件和bin文件
3.4.1 hex文件
3.4.2 bin文件
3.5 Bin文件生成
4. 分幾步實現BootLoader
4.1 跑FAT文件系統
4.2 讀寫Flash程序
4.2.1 Flash寫入步驟
4.2.2 讀寫Flash調用的庫函數
4.2.3 實現Flash讀寫
4.3 跳轉到新程序運行
4.3.1 跳轉到復位向量
4.3.2 App開始運行
5. Bootloader具體流程
5.1 主函數流程
5.2 BootLoader流程
5.3 跳轉到新程序流程
附錄A主函數
附錄B更新說明
參考文獻小程序
1. 前言自從幾個月前接觸到有Bootloader這回事,就有一種強烈的衝動,想寫一個BootLoader出來。很快在飛思卡爾的Cortex-M4單片機上實現,已是好幾個月前的事情了。而後關於BootLoader的事擱在一邊很久了,此次弄個STM32的BootLoader出來,Cortex-M3的,順便發表下博客,跟你們分享一下。
。。。
又過了大半年了吧,慢慢對BootLoader的認識也有點長進啦。特別是跟網友討論後發現BootLoader的實現仍是須要靠BootLoader程序和App程序的配合才能正常使用。在這裏特別感謝網友cary_yingj ,對本BootLoader的研究後發現App程序須要重定位中斷向量表,才能正常工做。
在其餘網友的反饋下,本人準備再將次文檔完善,把不夠詳細的地方寫得再詳細,而且力求通俗易懂一點。但願對學習BootLoader的同窗們有所幫助。
2. 初識BootLoader可能有的同窗據說過BootLoader,有的同窗沒有據說過,這個都很正常。關於BootLoader的概念你們能夠上網查一下,有比較詳細的說明,我在這裏說說我本身比較片面的理解,而且是針對CortexM3說明的,實現平臺爲STM32F103VET6。
2.1百度百科的BootLoader這裏借用一下百度百科對BootLoader的解釋。在嵌入式操做系統中,BootLoader是在操做系統內核運行以前運行。能夠初始化硬件設備、創建內存空間映射圖,從而將系統的軟硬件環境帶到一個合適狀態,以便爲最終調用操做系統內核準備好正確的環境。在嵌入式系統中,一般並無像BIOS那樣的固件程序(注,有的嵌入式CPU也會內嵌一段短小的啓動程序),所以整個系統的加載啓動任務就徹底由BootLoader來完成。在一個基於ARM7TDMIcore的嵌入式系統中,系統在上電或復位時一般都從地址0x00000000處開始執行,而在這個地址處安排的一般就是系統的BootLoader程序。
2.2BootLoader的簡單理解BootLoader就是單片機啓動時候運行的一段小程序,這段程序負責單片機固件更新,也就是單片機選擇性的本身給本身下程序。能夠更新也能夠不更新,更新的話,BootLoader更新完程序後,跳轉到新程序運行;不更新的話,BootLoader直接跳轉到原來的程序去運行。緩存
2.3BootLoader的做用BootLoader使單片機能本身給本身下載程序,因此在程序升級方面很是有做用。好比咱們的BootLoader是經過GSM更新程序的,咱們在升級單片機程序的時候,只要把新程序經過GSM發送給單片機,單片機本身實現程序更新,而後跳轉到新程序執行,這樣就省去咱們不少升級的功夫啦。
能夠想象一下若是把單片機安裝在很是高的地方,或者危險的工業現場,或者封裝得很難拆下來,咱們很難直接給單片機下載程序,那麼BootLoader的做用就體現出來了。簡單的說,有了BootLoader,咱們更新程序的話是省心又省力。
想一想是否是很高級,還帶點小興奮哈哈。不用急,下面咱們會繼續介紹,讓你們都能本身實現BootLoader。至因而經過什麼方式升級,這個你們自由發揮,相信會設計出豐富多彩的BootLoader升級方式呢。
3.BootLoader預備知識咱們這裏是爲ARM的Cortex-M3單片機寫的BootLoader,須要瞭解一下M3內核的架構,而且要了解M3單片機是怎麼啓動的等等。這個方面的知識,能夠參考《Cortex-M3權威指南》,這裏的話我只是爲了實現BootLoader簡單介紹一下,你們有什麼不清楚的請參考權威指南。而且這裏是以STM32爲例說明問題的,使用的開發環境是RVMDK(Keil)。
3.1復位序列這裏參考的是《Cortex-M3權威指南》的3.8節,復位序列。
M3單片機復位後,從0x00000000取棧指針(SP),從0x00000004取復位向量(PC),有了棧指針和復位向量後,單片機就按照正常流程運行了,在BootLoader裏面,咱們更新完程序後須要作的步驟之一就是設置棧指針,跳轉到復位向量。
3.1.1棧指針棧是一種數據結構,後進先出LIFO。借用百度百科的解釋:棧由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操做方式相似於數據結構中的棧。它使用的是一級緩存,他們一般都是被調用時處於存儲空間中,調用完畢當即釋放。
3.1.2復位向量復位向量是一個函數地址,在CortexM3單片機裏是復位函數的地址。也就是單片機啓動後第一個執行的函數。
3.2重定位中斷向量表這裏參考《Cortex-M3權威指南》的7.3節,向量表。
BootLoader是一個完整的程序,下載的新程序(如下稱爲App)也是一個完整的程序。都包含中斷向量表,因此的話,咱們是有兩個中斷向量表,相信由於有兩個向量表,你們都知道咱們應該須要對這兩個向量表作點什麼吧。
3.2.1STM32的中斷向量表咱們只看前16個向量,由於其他的向量屬於外設使用,與CortexM3內核無關。
__Vectors DCD __initial_spTop ;Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler數據結構
單片機啓動默認先運行BootLoader,因此默認的中斷向量表位置是BootLoader的中斷向量表。爲了App能夠正常運行,下載完App後,咱們還須要把中斷向量表從新定位到App程序那裏。根據《CortexM3權威指南》,介紹一下怎樣重定位中斷向量表。
3.2.2設置中斷向量表偏移Cortex-M3單片機有一個管理中斷向量表的寄存器,叫作向量表偏移量寄存器(VTOR)(地址:0xE000_ED08)。具體能夠看看截圖:架構
STM332程序的起始地址通常在0x08000000。因此BootLoader程序是在0x08000000,不是在0x00000000是由於STM32的重映射技術(不符合Cortex-M3的設計,有點搞另類的感受)。因此BootLoader的中斷向量表在0x08000000那裏。若是咱們的App程序起始地址在0x08070000,而且App的中斷向量表在起始地址,那麼BootLoader程序下載App後,爲了App程序能正確運行,開始App程序的運行後第一步,就要把中斷向量表重定位到0x08070000那裏。
具體實現下面會再介紹,接下來介紹分散加載文件相關內容。
3.3分散加載文件相關這一節涉及的內容主要屬於分散加載文件,你們具體上網瞭解,這裏只是介紹了可以實現BootLoader的一小部分。
3.3.1C語言的函數地址咱們知道C語言的函數名就是函數的地址,而且STM32單片機ROM的起始地址是在0x08000000,那麼使用編譯器編譯程序的話(這裏使用的是RVMDK),函數的地址默認都在以0x08000000爲首的一段ROM裏面了。好比咱們一個函數Delay(),它的地址能夠是0x08000167(CortexM3中函數的地址0bit位通常是1),也就是Delay函數的代碼在0x08000167,C語言函數調用Delay時,就是執行0x08000167的代碼。
3.3.2BootLoader佔用的ROM咱們須要注意的問題是,若是不修改程序默認的起始地址的話,那麼BootLoader和新App程序的起始地址都是0x08000000,也就是他們重疊了(代碼重疊),這樣的話確定相互之間有影響,程序是不能正常工做的。
這裏的解決方法是,BootLoader程序依然佔用0x08000000爲首的那段ROM,由於STM32的默認就是從0x08000000運行程序的。保持BootLoader程序先能正確運行。而後App使用除BootLoader佔用ROM之外的空間。這裏須要知道BootLoader到底佔用了多少ROM,很簡單,查看MAP文件就好了。這裏以個人BootLoader的MAP文件爲例說明一下,看截圖:
Memory Map of the image
Image Entry point :0x08000131
Load Region LR_IROM1 (Base: 0x08000000,Size: 0x00006da4, Max: 0x00080000, ABSOLUTE)
Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00006d54, Max:0x00080000, ABSOLUTE)
主要是這句話「Base:0x08000000, Size: 0x00006da4, Max: 0x00080000」,這句話說明了個人BootLoader程序是從0x08000000開始,佔用了0x00006DA4大小。只要咱們的App不要和BootLoader程序佔用的空間衝突就能夠了。個人App程序的起始地址選擇爲0x08070000,不與BootLoader程序衝突。具體怎麼修改ROM起始地址,下面介紹。
3.3.3修改ROM起始地址編譯新程序的時候,咱們要修改程序的起始地址,個人修改方法以下(開發環境是RVMDK):打開TargetOption...,切換到Target選項卡,以下函數
修改IROM1的起始地址和長度:
好比,爲了避免產生地址衝突,我將起始地址0x08000000修改爲0x0807000,將ROM長度0x80000修改爲0x10000。以下圖所示(左圖爲修改前、右圖爲修改後):工具
注意:BootLoader程序是不須要修改的,只是App須要修改(App就是使用BootLoader下載的程序)。
3.4hex文件和bin文件3.4.1hex文件平時咱們用j-Link或者串口下載程序的話,都是打開hex文件下載的,由於hex文件包含地址信息,下載程序的時候知道程序下載到ROM的哪一個區域。從另外一個角度上說,也就是hex文件是不能直接寫進ROM的,一邊寫須要一邊轉換(解碼出地址信息,將對應內容寫入ROM)。
3.4.2bin文件bin文件的話,很好理解,是直接的可執行代碼。也就是bin文件的內容跟下載ROM裏面的內容是同樣的。bin文件是沒有包含地址信息的,因此在下載以前要知道bin文件是要下載到ROM的那個區域。
咱們的BootLoader下載的是bin文件,直接寫進STM32的Flash裏面,地址信息的話就是上一節的IROM,0x08070000,從0x08070000開始連續寫入,中間不間斷。
3.5Bin文件生成默認狀況下編譯後生成的是hex文件,不過很輕鬆能夠生成bin文件。介紹具體怎麼生成bin文件,工具的話是使用fromelf.exe(目錄通常是在Keil安裝目錄裏面,本人的fromelf.exe目錄是在C:\Keil\ARM\ARMCC\bin),咱們是使用fromelf工具將axf文件轉換爲bin文件。
熟悉命令行的同窗可能會選擇直接敲命令,不過這裏介紹使用RVMDK提供的用戶命令(編譯時能夠自動生成bin,省去每次生成bin文件都要敲命令的過程)。
打開TargetOption...,切換到User選項卡,以下學習
主要是在運行用戶命令,Run#1ui
具體命令是(記得在Run#1前打勾,纔會在編譯後執行用戶命令生成bin文件):spa
命令能夠分爲五部分,簡化後是fromelf --bin -o xxx.bin xxx.axf,須要注意的是命令的五個部分之間要有空格。還須要說明的是路徑問題,這裏的路徑都是相對.uvproj文件的,下面是個人目錄(注意MY_STM32.uvproj文件和Output文件夾)。
個人bin文件和axf文件都在Output文件夾裏面,而且路徑是相對MY_STM32.uvproj的,Output文件夾裏的bin文件(MY_STM32.bin)相對於MY_STM32.uvproj應該寫成「.\Output\MY_STM32.bin」。
l 第一部分
這部分是fromelf.exe文件的路徑,根據本身的安裝目錄而變。我這裏由於Keil是安裝在C盤的,因此個人路徑以下所示。
參考命令:C:\Keil\ARM\ARMCC\bin\fromelf.exe
l 第二部分
這部分是固定的,--bin表示生成bin文件。
參考命令:--bin
l 第三部分
這部分也是固定的,-o表示輸出。
參考命令:-o
l 第四部分
這部分是生成文件的目錄和文件名,我是輸出在Output文件夾的,也就是bin文件在Output文件夾裏面。
參考命令:.\Output\MY_STM32.bin
l 第五部分
這部分是axf文件的目錄和文件名,咱們的bin文件是根據axf文件生成的,也就是說axf文件至關於輸入,bin文件至關於輸出。個人axf文件也在Output文件夾的。
參考命令:.\Output\MY_STM32.axf
介紹了這些基本知識後,咱們能夠來實現BootLoader了。
4. 分幾步實現BootLoader有了前面的基礎知識後,應該是比較容易理解BootLoader須要怎麼實現了。這一章,咱們分幾個步驟,一步一步實現BootLoader。
4.1跑FAT文件系統咱們的BootLoader是從SD卡更新程序的,把在電腦上編譯後的App程序,也就是bin文件,複製到SD卡中,而後讓單片機讀取相應的bin文件,就能夠實現程序的更新。須要注意的是,App程序須要修改ROM的起始地址,再編譯,而且要生成bin文件才支持正常下載。
我跑的文件系統是FATFS_R0.07c,很經典的一個版本。若是你們對文件系統方面不瞭解的話,請本身網上查找教程,或者說不少同窗對這一步應該已經很熟悉啦。
只要單片機上實現讀取bin文件,結合Flash寫入程序,就能夠實現程序更新。下面介紹讀寫Flash。
4.2讀寫Flash程序要實現BootLoader,還有一個前提是能夠寫入Flash了。若是是STM32單片機的話是很容易實現的,由於咱們有官方庫。本人使用的是3.0.0版本,參考官方例程,很容易實現Flash的讀寫,這裏一樣是爲了實現BootLoader簡單介紹一下。
4.2.1Flash寫入步驟l 解鎖Flash
l 擦除Flash
l 寫入Flash
l 驗證讀寫是否正確
4.2.2讀寫Flash調用的庫函數l voidFLASH_Unlock(void) Flash解鎖
l FLASH_Status FLASH_ErasePage(uint32_tPage_Address) Flash擦除
l FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_tData) Flash寫入
4.2.3實現Flash讀寫稍微封裝一下STM32的官方庫函數,就能實現Flash的讀寫,並驗證讀寫是否正確,具體我實現的接口函數爲如下截圖,你們能夠參考一下:操作系統
來到這裏,咱們能夠實如今bin文件寫入Flash了,寫入完後,就要跳轉到App程序執行了,接下來繼續介紹。
4.3跳轉到新程序運行
l 跳轉到復位向量
l 重定位中斷向量表
l 設置棧指針
4.3.1跳轉到復位向量BootLoader程序須要作的是跳轉到復位向量,具體實現能夠參考如下代碼。
( (void (*)()) (Reset))(); //跳轉到復位向量
注意( (void (*)()) (Reset) )();是一去就不返回的,執行完這條語句,單片機就直接跳轉到App程序運行的,因此BootLoader程序下載完App後,作一些簡單的處理(根據本身的應用,也能夠不作任何處理),就用這條語句跳轉到App執行。
4.3.2App開始運行BootLoader跳轉到App後,App須要作的是先設置棧指針,而後重定位中斷向量表地址,具體能夠參考如下代碼。
__set_MSP( Msp); //設置棧指針
NVIC_SetVectorTable( base, offset); //重定位中斷向量表
其中Msp是棧指針,也就是中斷向量表第一個字的內容,咱們這裏的內容是*((uint32_t)(0x08070000) )。
base是中斷向量表的基地址,通常狀況下就是ROM的起始地址,這裏是0x08070000。
至此,BootLoader實現步驟完了,相信熟悉了這幾個步驟後,你們能夠本身給本身的單片機寫個BootLoader。順便說一下,Cortex-M4的BootLoader跟Cortex-M3幾乎是同樣的。我在STM32上的實現徹底是參考本身上次在飛思卡爾Cortex-M4上的實現。下面說一下個人主函數吧,咱們再看看具體的BootLoader流程,再熟悉一下BootLoader。
5.Bootloader具體流程
5.1主函數流程先看截圖。
主函數的流程以下所示:
l 時鐘初始化
l LED初始化(可有可無)
l 調試接口初始化(可有可無)
l Flash初始化(解鎖Flash)
l FAT初始化(掛載文件系統)
l 咱們的BootLoader(重點,下面展開繼續介紹)
l 主循環(實際不會運行到這裏)
而後在具體講解BootLoader_FromSDCard函數,這就是咱們的重點,傳說中STM32的BootLoader從SD卡更新固件。
5.2BootLoader流程
具體流程以下所示:
l 打開bin文件,檢查文件打開是否正確
l 設置Flash下載起始地址(App程序起始地址)
l 讀取bin文件,檢查讀取是否正確
l 獲取棧指針SP和復位向量PC
l 進入循環(這裏是第5步),條件爲若是讀取bin文件字節數不爲零
l 將讀取到的bin寫入Flash,並判寫入狀態
l 調整Flash地址,根據寫入字節調整
l 繼續讀取bin文件,檢查讀取是否正確,回到5繼續循環
來到這裏已是退出循環了,也就是說咱們已經將bin寫入Flash完成了,準備跳轉到新程序運行
5.3跳轉到新程序流程其實上面已經講過了,這裏繼續囉嗦,截圖:
l 重定位中斷向量
l 設置棧指針
l 跳轉到復位向量(開始運行App程序)
說明一下,在這裏重定位中斷向量實際上是多餘的,App程序執行初始化後,又回到STM32初始狀態,因此在App程序中須要執行重定位中斷向量表操做,具體同以上操做相同。
囉嗦了又一遍,BootLoader徹底結束,感謝你們都支持啦~
#include "main.h"
int main(void)
{
SystemInit(); //配置系統時鐘爲72M
LED_GPIO_Config(); //初始化LED端口
Debug_TraceIOEnable(); //使能調試printf的IO口
Flash_Init(); //初始化Flash
FAT_Init(); //初始化文件系統
BootLoader_FromSDCard(); //Bootloader從SD卡更新固件
while(1)
{
LED_StatShow( FuncErr); //LED顯示Bootloader狀態 }