|
之前一篇的《STM32單片機(jī)的Bootloader設(shè)計(jì)(上)》文章中,主要介紹了STM32的啟動流程和內(nèi)存主要空間的分配,這篇文章將在上一篇文章的基礎(chǔ)上,來闡述一下STM32 Bootloader的實(shí)現(xiàn)。 STM32的內(nèi)存劃分 前面文章我們說了,STM32上電后會從0x08000000地址處開始運(yùn)行,因此,如果我們想要使得STM32在上電之處直接進(jìn)入進(jìn)Bootloader,那么其內(nèi)存的起始地址必須要從0x08000000處開始。這一步是由單片機(jī)的硬件所決定的,無法通過軟件干預(yù)。 因此,在我們使用Keil軟件設(shè)計(jì)STM32 Bootloader的時(shí)候,一定要在Keil的工程中設(shè)置這個(gè)地址,當(dāng)然,如果你不設(shè)置也沒關(guān)系,因?yàn)镵eil默認(rèn)就是將單片機(jī)的軟件編譯到此地址的。如圖1所示。 圖1 Keil上電起始地址的設(shè)置 另外在說一點(diǎn),起始地址右邊的Size我們也需要關(guān)注下,因?yàn)檫@個(gè)參數(shù)會影響到后續(xù)的內(nèi)存空間分配。 好了,接下來假設(shè)我們有一塊Flash容量為64KB的芯片,我們來為其劃分一下內(nèi)存空間。我們來計(jì)算下它的地址范圍為多少。 起始地址不需要多說,就是0x08000000。 64K的地址空間該怎么算呢?這個(gè)其實(shí)是一個(gè)進(jìn)制轉(zhuǎn)換的問題,我們知道,在十進(jìn)制中,1KB=1024個(gè)字節(jié),而我們計(jì)算機(jī)中的一個(gè)字節(jié)就是一個(gè)地址單元,因此只要要使用64KB*1024就可以得出有多少個(gè)(十進(jìn)制)地址單元了。又由于計(jì)算機(jī)中的地址都是按照十六進(jìn)制編碼來排列的,所以還需要將這個(gè)十位數(shù)的地址空間轉(zhuǎn)換成十六進(jìn)制,這個(gè)地址空間就是0x10000。注意,以上這個(gè)0x10000指的是地址空間,指明64K Flash中有0x10000個(gè)字節(jié)的存儲單元,而我們的內(nèi)存地址又是從0開始計(jì)算的,因此最終的地址范圍就是0x08000000~0x08010000。 計(jì)算出以上的地址范圍之后,我們就需要對其來劃分功能區(qū)了,首先假設(shè)我們的Bootloader不帶OTA(On The Air)升級功能,因此整個(gè)內(nèi)存空間至少要劃分成兩個(gè)部分,第一個(gè)部分是從0x08000000起始的Bootloader區(qū)域,假設(shè)長度為X,第二個(gè)部分是緊接著(當(dāng)然也可以不僅接著)Bootloader區(qū)域的應(yīng)用區(qū)域,其地址范圍為0x08000000 + X,其長度為Y。 這個(gè)地址的劃分可以現(xiàn)根據(jù)Bootloader最終編譯的大小進(jìn)行動態(tài)改變,不過需要注意的是,STM32內(nèi)存劃分要以半頁為最小單位,因?yàn)樵趯lash編程時(shí),都是按照半頁來擦除的,所以如果你的程序不按照半頁來對齊,那么擦出的時(shí)候就會很尷尬,半頁=32個(gè)字=128個(gè)字節(jié)。 基于上述原因,我們暫定STM32中,前10K地址存放Bootloader程序,后面的地址,存放應(yīng)用,如圖2所示。 圖2 STM32內(nèi)存劃分 Bootloader代碼設(shè)計(jì) Bootloader這個(gè)東西,對于我們來說是一個(gè)特殊的程序,但是對于計(jì)算機(jī)來說,它和千千萬萬的普通程序沒有任何差別,因此我們也需要對這個(gè)程序進(jìn)行初始化,你可以設(shè)置時(shí)鐘,設(shè)置串口或者CAN總線資源等。這些都是必須要進(jìn)行的,我們文中就不討論。 比較重要的需要討論的一點(diǎn)是跳轉(zhuǎn)程序,因?yàn)锽ootloader除了完成一些額外的不適宜在應(yīng)用中完成的工作之外,最重要的一點(diǎn)就是程序跳轉(zhuǎn),即“JampToApp()”。所謂的程序跳轉(zhuǎn),那么程序跳轉(zhuǎn)該怎么實(shí)現(xiàn)呢? 思考下,跳轉(zhuǎn)最終就是通過Bootloader跳轉(zhuǎn)到APP的地址處,在C語言中和跳轉(zhuǎn)直接相關(guān)的便是指針了,因此既能保證跳轉(zhuǎn)到某個(gè)地址,又能保證是類似于函數(shù)能被運(yùn)行的,就只有“指向函數(shù)的指針“這一項(xiàng)了。沒錯(cuò),Bootloader的“JampToApp()”操作就是一個(gè)函數(shù)指針,只需要獲知APP區(qū)的地址,就可以進(jìn)行跳轉(zhuǎn)了。 我們在上一節(jié)中以及指定了APP的地址就是0x08002800,那么是否只要跳轉(zhuǎn)到這個(gè)地址就可以了呢?答案當(dāng)然是否定的,因?yàn)槲覀冎罢f過,STM32內(nèi)存空間最起始的4個(gè)字節(jié)地址,存放的是棧頂指針,因此我們需要跳轉(zhuǎn)到0x08002800+4的地址處。 在跳轉(zhuǎn)的過程中,關(guān)閉中斷等工作也是必須的,具體代碼如圖3所示。 圖3 Bootloader中的跳轉(zhuǎn)程序 上面代碼中,有一個(gè)自定義的“TYP_drcPtr”類型,它起始只是一個(gè)void類型。 typedef void (*TYP_drcPtr)(void); //Define the jump pointer 跳轉(zhuǎn)問題解決了,接下來的問題就是何時(shí)跳轉(zhuǎn)? 這個(gè)何時(shí)跳轉(zhuǎn)很重要,我建議在程序的一開始,初始化Systemclock之前就去判斷和跳轉(zhuǎn),因?yàn)镾TM32如果你在Bootloader中初始化了Systemclock之后,再去APP區(qū)初始化,會造成硬件錯(cuò)誤,因此我建議大家在設(shè)計(jì)Bootloader的時(shí)候,程序一開始就是去檢測能否滿足跳轉(zhuǎn)條件,滿足了立馬跳,不滿足再去執(zhí)行硬件的初始化程序。如圖4所示。 圖4 STM32跳轉(zhuǎn)時(shí)機(jī) 好了,最后說一句,“Talk is easy, show me your code”,我已經(jīng)調(diào)試成功的bootloader請自行g(shù)it。 https:///huangqilong119/STM32_bootloader.git 關(guān)于STM32的Flash編程又是另外一個(gè)故事了,我們后續(xù)再說。 |
|
|