標題: STM32的BootLoader 從SD卡更新固件 [打印本頁]
作者: niuniu 時間: 2015-6-9 03:29
標題: STM32的BootLoader 從SD卡更新固件目 錄
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.2 BootLoader的簡單理解 BootLoader就是單片機啟動時候運行的一段小程序,這段程序負責單片機固件更新,也就是單片機選擇性的自己給自己下程序。可以更新也可以不更新,更新的話, BootLoader更新完程序后,跳轉到新程序運行;不更新的話, BootLoader直接跳轉到原來的程序去運行。
需要注意的是:BootLoader下載新程序后并不擦除自己(BootLoader程序還在),下次啟動依然先運行BootLoader程序,又可以選擇性的更新或者不更新程序,所以BootLoader就是用來管理單片機程序的更新。
這是本人的大概理解,大家有不明白請網上搜索一下更詳細介紹吧。
2.3 BootLoader的作用 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.1 STM32的中斷向量表 我們只看前 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
__initial_spTop就是棧指針,Reset_Handler是復位向量。這里只顯示了16個向量,CortexM3單片機的話總共有256個向量,也就是從棧指針的地址開始有1KB的區域屬于中斷向量表。
單片機啟動默認先運行 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.1 C語言的函數地址 我們知道 C語言的函數名就是函數的地址,并且 STM32單片機 ROM的起始地址是在 0x08000000,那么使用編譯器編譯程序的話(這里使用的是 RVMDK),函數的地址默認都在以 0x08000000為首的一段 ROM里面了。比如我們一個函數 Delay(),它的地址可以是 0x08000167( CortexM3中函數的地址 0bit位一般是 1),也就是 Delay函數的代碼在 0x08000167, C語言函數調用 Delay時,就是執行 0x08000167的代碼。
3.3.2 BootLoader占用的 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.4 hex文件和 bin文件 3.4.1 hex文件 平時我們用 j-Link或者串口下載程序的話,都是打開 hex文件下載的,因為 hex文件包含地址信息,下載程序的時候知道程序下載到 ROM的哪個區域。從另一個角度上說,也就是 hex文件是不能直接寫進 ROM的,一邊寫需要一邊轉換(解碼出地址信息,將對應內容寫入 ROM)。
3.4.2 bin文件 bin文件的話,很好理解,是直接的可執行代碼。也就是 bin文件的內容跟下載 ROM里面的內容是一樣的。 bin文件是沒有包含地址信息的,所以在下載之前要知道 bin文件是要下載到 ROM的那個區域。
我們的 BootLoader下載的是 bin文件,直接寫進 STM32的 Flash里面,地址信息的話就是上一節的 IROM, 0x08070000,從 0x08070000開始連續寫入,中間不間斷。
3.5 Bin文件生成 默認情況下編譯后生成的是 hex文件,不過很輕松可以生成 bin文件。介紹具體怎么生成 bin文件,工具的話是使用 fromelf.exe(目錄一般是在 Keil安裝目錄里面,本人的 fromelf.exe目錄是在 C:\Keil\ARM\ARMCC\bin),我們是使用 fromelf工具將 axf文件轉換為 bin文件。
熟悉命令行的同學可能會選擇直接敲命令,不過這里介紹使用 RVMDK提供的用戶命令(編譯時可以自動生成 bin,省去每次生成 bin文件都要敲命令的過程)。
打開 TargetOption...,切換到 User選項卡,如下
主要是在運行用戶命令, Run#1
具體命令是(記得在 Run#1前打勾,才會在編譯后執行用戶命令生成 bin文件):
C:\Keil\ARM\ARMCC\bin\fromelf.exe
--bin
-o
.\Output\MY_STM32.bin
.\Output\MY_STM32.axf
命令可以分為五部分,簡化后是 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.1 Flash寫入步驟 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 跳轉到新程序運行 這一節要結合上面提到過的,Cortex-M3啟動做了什么事情,然后我們的BootLoader下載App程序后,App程序就需要做同樣的事情。主要有三個步驟,其中BootLoader程序需要做的是:
l 跳轉到復位向量
App需要做的是:
l 重定位中斷向量表
l 設置棧指針
4.3.1 跳轉到復位向量 BootLoader程序需要做的是跳轉到復位向量,具體實現可以參考以下代碼。
( (void (*)()) (Reset))(); //跳轉到復位向量
注意 ( (void (*)()) (Reset) )();是一去就不返回的,執行完這條語句,單片機就直接跳轉到 App程序運行的,所以 BootLoader程序下載完 App后,做一些簡單的處理(根據自己的應用,也可以不做任何處理),就用這條語句跳轉到 App執行。
4.3.2 App開始運行 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.2 BootLoader流程 老樣子,先上截圖:
具體流程如下所示:
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完全結束,感謝大家都支持啦 ~
附錄A 主函數
#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狀態
}
}
附錄B 更新說明
l 版本: V1.01
l 時間: 2014-4-8
l 作者: coolweedman
l 更新:說明 Bootloader程序和 App程序需要配合才能實現 BootLoader, App程序需要重定位中斷向量表
l 特別感謝:網友 cary_yingj的研究和分享
參考文獻
[1] 《 CM3權威指南 CnR2》 Joseph Yiu 著 宋巖 譯
[2] ST官方庫 3.0.0
[3] 《 C和指針》 KennethA. Reek著 徐波 譯
[4] FATFS文件系統
[5] 其他互聯網資料
作者: srz1play 時間: 2016-7-28 16:39
不錯的
作者: messicss 時間: 2016-8-25 21:57
贊一個!
作者: yunqingabc 時間: 2016-10-17 22:06
感謝樓主分享的文章,專門注冊個號,頂貼!
作者: yq97723997 時間: 2016-10-17 22:50
謝謝分享了
作者: zhslcd 時間: 2016-10-20 09:52
21icbbs 上有這些資料
作者: zuiyunge 時間: 2016-12-12 08:56
不錯,紫薯,補丁
作者: XIAOXIAOYU 時間: 2016-12-20 18:13
寫的不錯!多謝樓主!
作者: 白努力 時間: 2016-12-22 17:26
很好學習了
作者: dzxqyt 時間: 2017-1-7 15:06
感謝樓主分享的文章,專門注冊個號,頂貼!
作者: 35mickey 時間: 2017-10-10 19:29
看完樓主的帖子,思路非常清晰,受益匪淺
作者: a370306531 時間: 2018-3-2 21:06
感謝分享,,,,
作者: zhangchangsheng 時間: 2018-3-28 13:56
贊一個!
作者: 神出沒 時間: 2020-7-4 09:50
寫的不錯,不過想請教個問題。有些程序是在APP中有一個系統升級的選項,點進去,找到SD卡的固件進行升級。這樣是不是bootloader和APP是二合一的,flash空間夠用嗎,這種是怎么實現的?有人這樣做嗎,求教
作者: MikeCai 時間: 2020-7-6 15:01
感恩樓主分享,學習收藏
歡迎光臨 (http://www.denmoz.com/bbs/)
Powered by Discuz! X3.1