STM32F40x的啟動代碼匯編分析

啟動代碼界面

博客主頁开瞭,歡迎訪問:blog.spursgo.com

之前接觸較多的是stm32F103單片機捞稿,在開始學(xué)習(xí)的時候,為了深入地學(xué)習(xí)荞胡,在這款單片機的啟動部分也花了不少時間妈踊。但是,當(dāng)時作為初學(xué)者泪漂,還沒有養(yǎng)成學(xué)習(xí)之后做筆記的習(xí)慣廊营。以至于,過了一段時間后萝勤,對這部分又有所忘卻赘风,剛好這次電子設(shè)計競賽,又要使用stm32的另外一種型號的單片機stm32F407纵刘,借此機會邀窃,對之前的知識做一個簡單的復(fù)習(xí)與總結(jié)。并且作為筆記記錄下來假哎,當(dāng)再次來看這些知識的時候瞬捕,會輕松很多,也希望能夠給正在學(xué)習(xí)stm32的朋友帶來一點幫助舵抹。

對于stm32系列的單片機肪虎,就啟動代碼來說,基本上沒有什么區(qū)別惧蛹。在這里扇救,我就以stm32F407型號的單片機為例,來講解一下啟動問題香嗓。

1.首先迅腔,我們來講解一下stm32的啟動方式。

stm32的啟動方式我一直覺得是一個比較有趣的地方:通過BOOT0和BOOT1兩個管腳的不同電平狀態(tài)來決定單片機從何處啟動運行代碼靠娱,這給我們的代碼調(diào)試以及芯片的升級帶來了很大的便利沧烈。


啟動方式

從這張圖片中我們可以看到,stm32一共有3種不同的啟動方式像云。

在講解具體的啟動方式之前锌雀,可能之前沒有接觸過stm32的朋友會不理解0x00000000和0x00000004地址被映射到不同的地址是什么意思。

下面迅诬,我們通過講解stm32的復(fù)位過程來理解上面所提到的幾個地址腋逆。


復(fù)位

對所有單片機來說 ,當(dāng)它獲取到復(fù)位REESET信號后侈贷,首先它會做兩個事情:取出棧指針SP的初始值和取出程序指針PC的初始值惩歉。對于不同位數(shù)的單片機來說,取出這些值的地址表示是不同的。還是以stm32來說柬泽,作為32位的處理器慎菲,那么以16進制來表示地址的話,就應(yīng)該是8位數(shù)字锨并。所以露该,棧指針SP的初始值就是從地址0x00000000處取出,程序指針PC的初始值從地址0x00000004處取出第煮。而且可以看出解幼,我們?nèi)〕龅倪@些值正好也是32位的,占用4個字節(jié)包警,因為我們存儲的是地址嘛撵摆,32位的,沒毛病害晦。

了解了0x00000000和0x00000004是怎么回事了之后特铝,就來繼續(xù)說啟動方式的問題吧。

既然stm32要想實現(xiàn)多種方式的啟動壹瘟,那么我們的SP和PC就應(yīng)該從不同的位置取呀鲫剿!而前面所提到的將0x00000000地址映射到0x08000000地址做的正是這個是呀,懂了映射是怎么回事了嗎稻轨?

好灵莲!正式講解具體的啟動方式:

(1)內(nèi)部FLASH啟動方式

當(dāng)芯片上電后采樣到BOOT0引腳為低電平時,0x00000000和0x00000004地址被映射到內(nèi)部FLASH的首地址0x08000000和0x08000004殴俱。因此政冻,內(nèi)核離開復(fù)位狀態(tài)后,讀取內(nèi)部FLASH的0x08000000地址空間存儲的內(nèi)容线欲,賦值給棧指針SP明场,作為棧頂?shù)刂罚僮x取內(nèi)部FLASH的0x08000004地址空間存儲的內(nèi)容询筏,賦值給程序指針PC榕堰,作為將要執(zhí)行的第一條指令所在的地址。具備這兩個條件后嫌套,內(nèi)核就可以開始從PC指向的地址中讀取指令執(zhí)行了。

(2)內(nèi)部SRAM啟動方式

類似地圾旨,當(dāng)芯片上電后采樣到BOOT0和BOOT1引腳均為高電平時踱讨,0x00000000和0x00000004地址被映射到內(nèi)部SRAM的首地址0x20000000和0x20000004,內(nèi)核從SRAM空間獲取內(nèi)容進行自舉砍的。其實自舉就是設(shè)置運行環(huán)境并執(zhí)行主體程序痹筛,只是聽起來高大上罷了。

(3)系統(tǒng)存儲器啟動方式

當(dāng)芯片上電后采樣到BOOT0引腳為高電平,BOOT1為低電平時帚稠,內(nèi)核將從系統(tǒng)存儲器的0x1FFF0000及0x1FFF0004獲取MSP及PC值進行自舉谣旁。系統(tǒng)存儲器是一段特殊的空間,用戶不能訪問滋早,ST公司在芯片出廠前就在系統(tǒng)存儲器中固化了一段代碼榄审。因而使用系統(tǒng)存儲器啟動方式時,內(nèi)核會執(zhí)行該代碼杆麸,該代碼運行時搁进,會為ISP提供支持(In System Program),如檢測USART1/3昔头、CAN2及USB通訊接口傳輸過來的信息饼问,并根據(jù)這些信息更新自己內(nèi)部FLASH的內(nèi)容,達到升級產(chǎn)品應(yīng)用程序的目的揭斧,因此這種啟動方式也稱為ISP啟動方式莱革。

很好,方式這個問題我們已經(jīng)講完了讹开,那現(xiàn)在來講講難啃的啟動代碼吧盅视。

我們在這里以內(nèi)部FLASH的啟動過程為例,來講解啟動匯編代碼萧吠。

2.啟動代碼匯編分析



啟動代碼位置

注意到圖片中左邊的紅色框了嗎左冬?startup_stm32f40xx.s文件就是我們的啟動代碼所在的文件,很奇怪吧纸型!怎么是.s后綴呢拇砰?沒啥奇怪的,因為里面是匯編代碼呀狰腌,不是c語言寫的哦除破!啟動代碼可是要求運行速度非常快的呀琼腔,雖然c語言速度也很快了瑰枫,但是和匯編比起來,呵呵丹莲,你懂得光坝。

右邊是官方給出的英文注釋,本人英語渣渣甥材,在這里我也不去獻丑翻譯啦盯另,想了解的朋友可以自行翻譯,但是個人覺得沒有必要去看懂洲赵。

下面不廢話了鸳惯,直接來干貨商蕴,講解匯編代碼。

匯編代碼

下面對匯編代碼進行逐一解釋芝发,涉及到相關(guān)匯編指令和偽指令绪商,想詳細了解的朋友自行百度,這里只是簡單的介紹一下辅鲸,不做深入探討格郁。

1)堆和棧的初始化

Stack_Size? ? ? EQU? ? 0x00000400

這段代碼很簡單,就是給Stack_Size賦一個值而已瓢湃,用來定義棧區(qū)大小理张。

棧區(qū)(stack)— 由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值绵患,局部變量的值等雾叭。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

AREA? ? STACK, ?NOINIT, ?READWRITE, ?ALIGN=3

AREA 偽指令用于定義一個段落蝙,如代碼段织狐、數(shù)據(jù)段或者堆棧段。(注意:段是匯編語言中非常重要的一個概念筏勒,可以去詳細了解一下) 移迫;STACK表示我們定義的是棧,其實這里僅僅是一個便于人理解的一個單詞啦管行;NOINIT指定此數(shù)據(jù)段僅僅保留了內(nèi)存單元厨埋,而沒有將各初始值寫入內(nèi)存單元,或者將各個內(nèi)存單元值初始化為0捐顷;READWRITE屬性荡陷,指定本段為可讀可寫;ALIGN屬性迅涮,用來指定數(shù)據(jù)對齊的方式废赞,為2的ALIGN次方,這是ALIGN=3叮姑,也就是按照字節(jié)對齊唉地。

Stack_Mem? ? SPACE? Stack_Size?

SPACE用來分配一片連續(xù)的存儲區(qū)域并初始化為0。這里也就是分配一篇大小為0x400的連續(xù)存儲區(qū)域传透,并初始化為0耘沼,并且該區(qū)域的起始地址為Stack_Mem。

__initial_sp?

是匯編代碼地址標(biāo)號朱盐,在這里我們用來表示椄剑空間頂?shù)刂贰??

Heap_Size? ? ? EQU? ? 0x00000200?

用來定義堆區(qū)大小。

堆區(qū)(heap) — 一般由程序員分配釋放托享, 若程序員不釋放骚烧,程序結(jié)束時可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事闰围,分配方式倒是類似于鏈表赃绊。

AREA? ? HEAP, ?NOINIT, ?READWRITE, ?ALIGN=3

STACK表示我們定義的是堆,其他見上文

Heap_Mem? ? ? ? SPACE? Heap_Size

這里也就是分配一篇大小為0x200的連續(xù)存儲區(qū)域羡榴,并初始化為0碧查,并且該區(qū)域的起始地址為Stack_Mem。

__heap_limit ?

是匯編代碼地址標(biāo)號校仑,在這里我們用來表示堆空間結(jié)束地址忠售。

2)中斷向量表定義

PRESERVE8

THUMB

PRESERVE8指定當(dāng)前文件要求堆棧8位對齊。

THUMB指定所用的指令集為thumb迄沫。

其實這兩條代碼我也不是很懂稻扬,希望懂得朋友可以指點一下。

這里我們需要注意一下這條注釋:

; Vector Table Mapped to Address 0 at Reset

它指的Address 0并不是真正意義上的0x00000000羊瘩。在我們之前的假設(shè)下(假設(shè)STM32從FLASH啟動)則此中斷向量表起始地址為0x8000000泰佳,實際上是在CODE區(qū)。

AREA? ? RESET, ?DATA, ?READONLY?

AREA定義一塊數(shù)據(jù)段尘吗,只可讀逝她,段名字是RESET;DATA屬性:用于定義數(shù)據(jù)段睬捶,默認(rèn)為READWRITE黔宛。指定本段為可讀可寫。

EXPORT? __Vectors ? ??

EXPORT偽指令用于在程序中聲明一個全局的標(biāo)號擒贸,該標(biāo)號可在其他的文件中引用臀晃。EXPORT可用GLOBAL代替。標(biāo)號在程序中區(qū)分大小寫酗宋。在程序中聲明一個全局的標(biāo)號__Vectors积仗,該標(biāo)號可在其他的文件中引用

EXPORT??__Vectors_End?

在程序中聲明一個全局的標(biāo)號__Vectors_End

EXPORT??__Vectors_Size?

在程序中聲明一個全局的標(biāo)號__Vectors_Size

__Vectors? ? ? DCD? ? __initial_sp ? ? ? ? ? ? ?

DCD ?用于分配一片連續(xù)的字存儲單元并用指定的數(shù)據(jù)初始化。向量表第一個表項是棧頂?shù)刂吠擅ǎ撎幬锢淼刂分导礊?__Vetors?標(biāo)號所表示的值寂曹,該地址中存儲__initial_sp所表示的地址值,大小為一個字(32bit)回右。

DCD? ? Reset_Handler ? ? ? ? ?

向量表第二個表項是復(fù)位中斷服務(wù)函數(shù)Reset Handler入口地址

DCD? ? NMI_Handler

...

...

...

DCD? ? OTG_FS_IRQHandler ? ? ? ?

這些都是在中斷向量表中注冊函數(shù)入口地址隆圆,就不在這里挨個解釋了。

__Vectors_End ?

表示中斷向量表結(jié)束

__Vectors_Size ? EQU ? __Vectors_End ?- ?__Vectors ?

得到向量表的大小

3)地址重映射及中斷向量表的轉(zhuǎn)移

AREA? ? |.text|, ?CODE, ?READONLY ??

定義一個代碼段翔烁,可讀渺氧,段名字是.text? 段名若以數(shù)字或者標(biāo)點開頭,則該段名需用"|"括起來蹬屹,如|1_test|侣背。

Reset_Handler? ? PROC?

標(biāo)記一個函數(shù)的開始白华。利用PROC、ENDP這一對偽指令把程序段分為若干個過程贩耐,使程序的結(jié)構(gòu)加清晰弧腥。

EXPORT? Reset_Handler? ? ? ? ? ? [WEAK] ?

EXPORT偽指令用于在程序中聲明一個全局的標(biāo)號,[WEAK]選項聲明其他的同名標(biāo)號優(yōu)先于該標(biāo)號被引用潮太。在外部沒有定義該符號時導(dǎo)出該符號Reset_Handler管搪。

IMPORT? SystemInit

IMPORT? __main?

?IMPORT偽指令用于通知編譯器要使用的標(biāo)號在其他的源文件中定義

LDR? ? R0, =SystemInit

BLX? ? R0

把SystemInit函數(shù)的地址裝載到R0,BLX為跳轉(zhuǎn)指令铡买,跳轉(zhuǎn)到R0中的地址處更鲁,換句話說,就是執(zhí)行SystemInit()這個函數(shù)啦奇钞。函數(shù)SystemInit()主要作用是設(shè)置系統(tǒng)時鐘頻率和中斷寄存器的初始化澡为。

LDR? ? R0, =__main?

BX? ? ? R0 ?

把main()函數(shù)的地址裝載到R0,BLX為跳轉(zhuǎn)指令蛇券,跳轉(zhuǎn)到R0中的地址處缀壤,換句話說,就是執(zhí)行main()這個函數(shù)啦纠亚,進入C的世界塘慕。

ENDP

函數(shù)結(jié)束

然后由很長的代碼都是用來定義相關(guān)函數(shù)的,這里就不解釋了蒂胞。

B

B本來也是跳轉(zhuǎn)指令图呢。它的作用相當(dāng)于 MOV PC, 目標(biāo)地址骗随。但是這里B后面沒有接上目標(biāo)地址蛤织,我也不知道為什么要用上這么一句。

ENDP

ALIGN

ENDP不多說鸿染。ALIGN屬性:使用方式為ALIGN 表達式指蚜。在默認(rèn)時,ELF(可執(zhí)行連接文件)的代碼段和數(shù)據(jù)段是按字對齊的涨椒,表達式的取值范圍為0~31摊鸡,相應(yīng)的對齊方式為2表達式次方。

4)堆和棧的初始化

IF? ? ? :DEF:__MICROLIB ?

判斷是否使用DEF:__MICROLIB(micro lib)

EXPORT??__initial_sp ? ??


EXPORT??__heap_base ?;使外部程序可以使用

EXPORT??__heap_limit

將__initial_sp蚕冬、__heap_base免猾、__heap_limit賦予全局屬性、

ELSE ??

IMPORT??__use_two_region_memory ?

定義全局標(biāo)號 __use_two_region_memory

EXPORT??__user_initial_stackheap ??

聲明全局標(biāo)號__user_initial_stackheap囤热,這樣外程序也可調(diào)用此標(biāo)號猎提。允許進行棧和堆的賦值,在__main函數(shù)執(zhí)行過程中調(diào)用旁蔼。

__user_initial_stackheap ??

標(biāo)號__user_initial_stackheap锨苏,表示用戶堆棧初始化程序入口

LDR? ? R0, =? Heap_Mem ? ?

保存堆始地址

LDR?????R1,?=(Stack_Mem?+?Stack_Size)?

保存棧的大小

LDR?????R2,?=?(Heap_Mem?+??Heap_Size)?

保存堆的大小

LDR?????R3,?=?Stack_Mem ?

保存棧頂指針

BX??????LR

相當(dāng)于MOV ?PC, LR

LR就是連接寄存器(Link Register疙教,LR)在ARM體系結(jié)構(gòu)中LR的特殊用途有兩種:一是用來保存子程序返回地址;二是當(dāng)異常發(fā)生時蚓炬,LR中保存的值等于異常發(fā)生時PC的值減4(或者減2)松逊,因此在各種異常模式下可以根據(jù)LR的值返回到異常發(fā)生前的相應(yīng)位置繼續(xù)執(zhí)行。在這里是第一種功能肯夏,那么結(jié)合BX的用法,就是回到之前保存的返回地址處犀暑。

ALIGN

同上一個ALIGN

ENDIF

END

over喏驯击!

最后我們來總結(jié)一下啟動代碼的作用:

(1)堆和棧的初始化;

(2)向量表定義耐亏;

(3)地址重映射及中斷向量表的轉(zhuǎn)移徊都;

(4)設(shè)置系統(tǒng)時鐘頻率;

(5)中斷寄存器的初始化广辰;

(6)進入C應(yīng)用程序暇矫。

我廢話了這么多,還是希望各位要好好消化一下哦择吊!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末李根,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子几睛,更是在濱河造成了極大的恐慌房轿,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,332評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件所森,死亡現(xiàn)場離奇詭異囱持,居然都是意外死亡,警方通過查閱死者的電腦和手機焕济,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,508評論 3 385
  • 文/潘曉璐 我一進店門纷妆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晴弃,你說我怎么就攤上這事掩幢。” “怎么了肝匆?”我有些...
    開封第一講書人閱讀 157,812評論 0 348
  • 文/不壞的土叔 我叫張陵粒蜈,是天一觀的道長。 經(jīng)常有香客問我旗国,道長枯怖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,607評論 1 284
  • 正文 為了忘掉前任能曾,我火速辦了婚禮度硝,結(jié)果婚禮上肿轨,老公的妹妹穿的比我還像新娘。我一直安慰自己蕊程,他們只是感情好椒袍,可當(dāng)我...
    茶點故事閱讀 65,728評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著藻茂,像睡著了一般驹暑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辨赐,一...
    開封第一講書人閱讀 49,919評論 1 290
  • 那天优俘,我揣著相機與錄音,去河邊找鬼掀序。 笑死帆焕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的不恭。 我是一名探鬼主播叶雹,決...
    沈念sama閱讀 39,071評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼换吧!你這毒婦竟也來了折晦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,802評論 0 268
  • 序言:老撾萬榮一對情侶失蹤式散,失蹤者是張志新(化名)和其女友劉穎筋遭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體暴拄,經(jīng)...
    沈念sama閱讀 44,256評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡漓滔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,576評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乖篷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片响驴。...
    茶點故事閱讀 38,712評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖撕蔼,靈堂內(nèi)的尸體忽然破棺而出啸臀,到底是詐尸還是另有隱情远荠,我是刑警寧澤,帶...
    沈念sama閱讀 34,389評論 4 332
  • 正文 年R本政府宣布,位于F島的核電站齿风,受9級特大地震影響绿店,放射性物質(zhì)發(fā)生泄漏轧邪。R本人自食惡果不足惜叼耙,卻給世界環(huán)境...
    茶點故事閱讀 40,032評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炫狱,春花似錦藻懒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酷含,卻和暖如春鄙早,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背第美。 一陣腳步聲響...
    開封第一講書人閱讀 32,026評論 1 266
  • 我被黑心中介騙來泰國打工蝶锋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人什往。 一個月前我還...
    沈念sama閱讀 46,473評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像慌闭,于是被迫代替她去往敵國和親别威。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,606評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 8086匯編 本筆記是筆者觀看小甲魚老師(魚C論壇)《零基礎(chǔ)入門學(xué)習(xí)匯編語言》系列視頻的筆記,在此感謝他和像他一樣...
    Gibbs基閱讀 37,140評論 8 114
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,834評論 25 707
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理描验,因此不免有一些不準(zhǔn)確的地方白嘁,同時不同JDK版本的...
    高廣超閱讀 15,570評論 3 83
  • 若說到兒童文學(xué)界的泰斗,恐怕就是曹文軒了膘流。 他的文章自然而幽默絮缅,精彩處會讓人忍俊不禁。最經(jīng)典的是他的《草房子》了呼股。...
    慢煮生活閱讀 790評論 3 2
  • 每個男人心中都住著一位不可比擬的董小姐耕魄,亦會遇見乘風(fēng)而來的趙小姐,無論心中是否存在草原彭谁,與你牽手的吸奴,一定是能讓你溫...
    慕與閱讀 4,522評論 75 93