生活随笔
收集整理的這篇文章主要介紹了
STM32启动过程分析
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
硬件: STM32F1系列
軟件環(huán)境:Keil 4.54
注:本文中提到的RTOS以RT-Thread為例,不涵蓋所有RTOS的情況
?
在Keil MDK中新建工程時會根據(jù)所選的device自動生成啟動代碼文件startup.s,該文件的作用可根據(jù)其頭部的注釋看出
This module performs:
;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Configure the clock system
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM3 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.
?在startup.s中,完成了堆棧大小和中斷向量表的設(shè)置。默認(rèn)的棧大小為400字節(jié),堆大小為200字節(jié),可自行更改。這個棧在bare-metal系統(tǒng)中為全局所使用,在帶RTOS的系統(tǒng)中被操作系統(tǒng)內(nèi)核和中斷所使用,如果無多層函數(shù)嵌套調(diào)用,通常是夠用的。堆在使用malloc()的時候會被用到。堆棧的設(shè)置必須用匯編語言完成,因為C語言通常會用到函數(shù),而函數(shù)調(diào)用是依賴于堆棧的。關(guān)于startup.s的詳細(xì)分析請參考004:STM32啟動文件詳解及SystemInit函數(shù)分析一文
?
?系統(tǒng)上電后,默認(rèn)從地址為0的地方開始執(zhí)行。在STM32中,若根據(jù)boot引腳選擇從主閃存存儲器啟動,則主閃存存儲器被映射到啟動空間(0x0000?0000),但仍然能夠在它原有的地址(0x0800?0000)訪問它。0x08000000開始的一段區(qū)域存放的是中斷向量表(即startup.s中__Vectors開始的部分)Assembly代碼??
;?Vector?Table?Mapped?to?Address?0 ?at?Reset???? ????????????????AREA????RESET,?DATA,?READONLY?? ????????????????EXPORT??__Vectors?? ????????????????EXPORT??__Vectors_End?? ????????????????EXPORT??__Vectors_Size?? ?? __Vectors???????DCD?????__initial_sp????????????????????;?Top?of?Stack?? ????????????????DCD?????Reset_Handler???????????????????;?Reset?Handler????? ?先是執(zhí)行__initial_sp設(shè)置主堆棧指針MSP(相關(guān)概念請參考宋巖翻譯的《Cortex-M3權(quán)威指南》),而后執(zhí)行復(fù)位操作Reset_HandlerAssembly代碼??
Reset_Handler????PROC?? ?????????????????EXPORT??Reset_Handler?????????????[WEAK]?? ?????IMPORT??__main?? ?????IMPORT??SystemInit?? ?????????????????LDR?????R0,?=SystemInit?? ?????????????????BLX?????R0?? ?????????????????LDR?????R0,?=__main?? ?????????????????BX??????R0?? ?????????????????ENDP?? ?Reset_Handler中首先執(zhí)行SystemInit()函數(shù)(在system_stm32f10x.c文件中定義),該函數(shù)主要完成了RCC時鐘的設(shè)置。接著執(zhí)行__main()處的代碼。在Keil IDE的工程窗口中,是無法搜索到__main()的定義的,但在匯編代碼和工程map文件中可以找到它的身影Assembly代碼??
__main??????????????????0x08000121 ???Thumb?Code?????0 ??entry.o(.ARM.Collect$$$$00000000 )?? ?推測應(yīng)該是在entry.c文件中,而entry.c文件應(yīng)該是在Keil自帶的library里。?
?參考MDK __main()代碼執(zhí)行過程分析一文,__main()中主要通過
1. __scatterload()把RW/RO輸出段從裝載域地址復(fù)制到運行域地址,并完成ZI運行域的初始化工作。
2. __rt_entry()初始化堆棧,完成庫函數(shù)的初始化,最后自動跳轉(zhuǎn)向main()函數(shù)。其中__user_initial_stackheap()是在startup.s中定義的
Assembly代碼??
EXPORT??__user_initial_stackheap??????????????????? __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?? 根據(jù)AAPCS的規(guī)定,棧任何時候都得4字節(jié)對齊,在調(diào)用入口得8字節(jié)對齊。對于帶RTOS的系統(tǒng),該函數(shù)根據(jù)
Assembly代碼??
AREA?STACK,?NOINIT,?READWRITE,?ALIGN=3 ?? AREA????HEAP,?NOINIT,?READWRITE,?ALIGN=3 ?? PRESERVE8?? ?保證了主堆棧指針MSP是遵守規(guī)定的,而線程堆棧指針PSP全靠自己來保證每次進(jìn)入C世界時是8字節(jié)對齊,通常的做法是在程序中使用__attribute__((aligned(8)))來告知編譯器在分配空間時采用8字節(jié)對齊。在發(fā)生中斷時,如果當(dāng)前正在使用的棧指針不是8字節(jié)對齊,則先把SP-4,調(diào)整為8字節(jié)對齊,參考cortex-m3 棧的8字節(jié)對齊一文
補充:startup.s定義了中斷處理函數(shù)
Assembly代碼??
NMI_Handler?????PROC????????;過程的開始?? ????????????????EXPORT??NMI_Handler??[WEAK]?? ????????????????B????.??????;原地跳轉(zhuǎn)(即無限循環(huán)),?? ????????????????ENDP????????;過程的結(jié)束?? ?其中WEAK作為若定義,意思是如果在別處也定義該symbol,在鏈接時用別處的地址,而stm3210x_it.c這個文件通常會進(jìn)行這些中斷處理函數(shù)的重新定義
總結(jié)
以上是生活随笔 為你收集整理的STM32启动过程分析 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。