了解STM32的啟動過程
我們平時在使用STM32時,寫程式一般都是直接從main函數開始,初始化系統時鐘以及各個外設,然後進入while循環,執行邏輯功能函數。但是知不知道在main函數之前,晶片都做了什麼呢?讓我們大緻了解一下STM32的啟動過程。
首先看一下CM3權威指南對于複位的描述:
意思大緻就是複位後,會從0x0000 0000位址取出棧的初始值(該值在後面初始化棧會用到),從0x0000 0004取出複位向量指派PC指針,然後程式跳轉到複位函數的入口位址,開始往後執行程式。
我們使用的是從Flash啟動的方式,是以根據存儲映射是位址從從0x00000000映射到0x08000000。是以當STM32晶片上電或複位時,會從0x0800 0004處,取出四個位元組指派給PC指針,進而程式跳轉至複位函數的入口位址。這裡我們看一下調試狀态下的彙程式設計式:先進去軟體仿真模式,在彙編視窗,定位到0x0800 0000位址處
可以看到,位址0x0800 0000處存放的是0x2000 0748(該晶片為小端模式,高位元組存放在高位址),0x0800 0004存放的是0x0800 01CD。是以,這個0x0800 01CD應該就是我們複位函數的入口位址了。我們繼續定位程式到該位址:
仔細一看,會發現找不到0x0800 01CD,隻有0x0800 01CC。沒錯,複位函數的入口就是0x0800 01CC。且看CM3權威指南對于複位向量的描述:
LSB必須為1,就是位址的bit0必須為1,是以0x0800 01CC存放的形式會是0x0800 01CD。這裡猜測由于STM32是32bit的,取址時會四位元組對齊,是以PC指針會跳轉至0x0800 01CC處而不是0x0800 01CD處。後面的内容我們可以看到,就是執行複位函數裡的内容,SystemInit函數和__main函數,我們下面來詳細看一下啟動檔案。
STM32啟動檔案
順着上面的内容,我們暫時不看其他地方,直接來到複位函數吧
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
PROC和ENDP表示函數定義的開頭和結尾,中間時函數定義内容
EXPORT表示該标号可以供外部其他子產品調用
IMPORT表示定義來自外部,類似C語言的extern
接下來的兩行表示程式調用SystemInit函數,我們可以全局搜尋該函數名,會發現該函數的定義位于ST提供的庫檔案system_stm32f10x.c中:
該函數對時鐘源、系統時鐘等進行了配置,具體可以檢視對應的寄存器含義。
繼續看複位函數,後面是__main函數,這個函數就複雜了,并不是我們main函數的main,它做了很多事情,最後才調用main函數的main。看一下彙編:
可以看出,要去0x0800 01F8處取出值作為位址,并跳轉到該位址。找到0x0800 01F8,該位址存放的值為0x0800 0131:
繼續,定位到0x0800 0131位址處:
我們會發現調用了__scatterload函數,查了一下map檔案,發現該函數來自__scatter.o,應該是編譯器帶的庫有的。後面的程式還會調用很多其他庫裡的相關函數。
具體的彙程式設計式就不分析了。這裡總結一下__main完成的工作:
1.初始化.data區和.bss區
2.初始化堆棧
3.跳轉到main函數。
大緻說到這裡吧,有些地方還沒有去詳細研究,不太了解。