tft每日頭條

 > 科技

 > stm32單片機啟動文件講解

stm32單片機啟動文件講解

科技 更新时间:2025-02-24 06:50:44

想要深入學習STM32單片機,就必須要去研究STM32單片機的啟動代碼,否則你就無法從整體框架上去了解它,所以STM32啟動代碼早晚都是要研究學習的,避不掉的坑。啟動代碼裡主要是由彙編和僞指令構成的,下面我們從頭到尾來理一遍這些神秘的代碼究竟是什麼含義。


stm32單片機啟動文件講解(STM32單片機啟動代碼你不會不知道吧)1

圖一 申請棧

圖一這段代碼開辟了一個大小是0x00001000的棧,大小可以根據實際情況去調整,棧主要用于保存函數内局域變量和内核寄存器,在調用函數時就會将一些數據保存在棧内,占用一定的棧空間,在調用函數結束返回到上一層函數時就會歸還調用函數所占用的棧空間,所以棧的大小決定了你能申請的局域變量的大小,如果函數内定義了較多局域變量、大數組或多層函數嵌套的情況時建議将棧調大,否則運行時可能會由于棧溢出而導緻的HardFault_Handler異常。__initial_sp就是這個棧指向地址的一個标号,後面會用到。


stm32單片機啟動文件講解(STM32單片機啟動代碼你不會不知道吧)2

圖二 申請堆

圖二這段代碼開辟了一個大小是0x00000000的堆,即沒有分配堆大小,一般未涉及操作系統時,不需要使用堆。如果用到堆也可以直接申請全局變量效果等效于此,所以為了靈活使用,如果程序裡用到動态内存分配,直接在使用全局變量定義一個數組效果一樣。

需要強調的是ALIGN=3表示8字節對齊,所以自己用全局變量創建數組用作堆時也要保證8字節對齊,可以使用__align(8)來前綴修飾,8字節對齊是為了保證必須使用8字節對齊的函數能正常運行,例如像printf("%.3f",test)這樣的函數輸出浮點型變量值時,就要保證定義test是8字對齊的。編程時隻要保證堆的起始地址是8字節對齊,編譯器會自動保證後面在堆裡申請的變量也是8字節對齊的。


stm32單片機啟動文件講解(STM32單片機啟動代碼你不會不知道吧)3

省略中間部分......,可自行參考啟動代碼

stm32單片機啟動文件講解(STM32單片機啟動代碼你不會不知道吧)4

圖三 中斷向量表

圖三這段代碼是定義中斷向量表,針對STM32單片機中斷向量一般表默認是從單片機保存代碼的開始位置即0x8000000地址(也可以從映射啟動地址,這裡不做拓展)。不要糾結這個先後順序為什麼要這樣,這是芯片生産廠規定好的向量表第一位就是__initial_sp即棧指針放在0x8000000地址處占4個字節。第二個就是複位中斷函數的入口地址放在0x8000004地址處占4個字節。說白了DCD就是申請一個占4字節的坑,每個坑放一個指針,__initial_sp用于放棧指針,其他坑分别放指向一個中斷服務函數的入口指針。這樣一旦觸發中斷,硬件上就會來到對應中斷的坑,然後從坑裡找到對應的中斷服務函數的入口地址,跳轉到對應中斷服務函數運行。


stm32單片機啟動文件講解(STM32單片機啟動代碼你不會不知道吧)5

圖四 複位中斷服務函數

圖四這段代碼比較重要,這就是複位中斷的服務函數,Reset_Handler标号正好對應上面說的中斷向量表裡的那個标号一緻,編譯器就會把這段彙編程序的入口地址編譯放到中斷向量表裡複位中斷那個坑裡。一旦觸發複位,硬件就會立馬在中斷向量表裡從找到複位中斷這個坑,從坑裡放的指針地址就能跳轉到這裡來運行。可以看出單片機在複位後,并不是直接從main函數開始運行,而是最先進入複位中斷。從複位中斷裡跳轉到__main函數運行,注意__main函數不等于main函數。__main函數是由編譯器提供的一個庫函數,進入這個函數首先會初始化一些全局變量,堆棧裡的數據,比如你定義一個全局變量int x=10;在函數裡用到x這個變量時,程序怎麼知道他初始值是多少,這個初始10就是在__main函數裡進行賦值的,當一切準備工作都做好了__main函數會調用main函數,至此進入到你熟悉的世界裡來了,多麼美好!


stm32單片機啟動文件講解(STM32單片機啟動代碼你不會不知道吧)6

圖五 堆棧初始化

圖五為用戶堆和棧的初始化

IF :DEF:__MICROLIB ;類似if語句,:DEF:X 就是說X定義了則為真,否則為假。

EXPORT __initial_sp ;棧頂地址。

EXPORT __heap_base ;堆起始地址。

EXPORT __heap_limit ;堆末端地址。

ELSE ;如果沒定義__MICROLIB,則使用默認的C運行時庫

IMPORT __use_two_region_memory ;通知編譯器要使用的标号在其他源文件定義了__use_two_region_memory。

EXPORT __user_initial_stackheap ;聲明全局标号__user_initial_stackheap,這樣外程序也可調用此标号 ;則進行堆棧和堆的賦值,在__main函數執行過程中調用;如果使用默認的C庫,程序啟動過程中就不會執行該标号下的代碼。

__user_initial_stackheap;表示用戶堆棧初始化程序入口,在__main函數執行過程中調用。LDR R0, = Heap_Mem ;保存堆始地址

LDR R1, =(Stack_Mem Stack_Size) ;保存棧的大小

LDR R2, = (Heap_Mem Heap_Size) ;保存堆的大小

LDR R3, = Stack_Mem ;保存棧頂指針

BX LR

ALIGN ;填充字節使地址對齊

ENDIF

END


下面對其他一些關鍵詞定義做一個大概介紹:

1.[WEAK]:此修飾符修飾的函數為若函數,意為可以其他源文件中重新定義一個同名函數,最終編譯器編譯的時候,會選擇用戶定義的函數,如果用戶沒有重新定義這個函數,那麼編譯器就會執行WEAK聲明的函數。所以我們可以在别的地方定義一個相同名字的函數,而不必也盡量不要修改之前的函數,比如這裡就可以看出來啟動代碼裡已經有了USART1_IRQHandler函數,你在自己寫的代碼裡再定義一個USART1_IRQHandler函數,編譯也不會報錯,而是自動使用你自己編寫的函數。

2.IMPORT:表明要調用的函數、變量或其他标号為其他源文件定義,類似C裡的函數聲明。

3.EXPORT:表明該函數、變量或其他标号可以被其他源文件使用,類似于C中的extern功能。

4.AREA:僞指令,用于定義代碼段或數據段,後跟屬性标号。其中常用的有“ READONLY”表示該段為隻讀屬性,聯系到STM32的内部存儲介質,可知具有該屬性的段一般保存于FLASH區,而“READWRITE”表示該段為可讀寫屬性,可知可讀寫段一般保存于SRAM區。


,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved