文章下方附學習資源,自助領取
本文對stm32啟動文件startup_stm32f10x_hd.s的代碼進行講解,此文件的代碼在任何一個STM32F10x工程中都可以找到。
啟動文件使用的ARM彙編指令彙總
Stack——棧
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=
Stack_Mem SPACE Stack_Size
__initial_sp
開辟棧的大小為 0X00000400(1KB),名字為 STACK, NOINIT 即不初始化,可讀可寫, 8(2^3)字節對齊。
棧的作用是用于局部變量,函數調用,函數形參等的開銷,棧的大小不能超過内部SRAM 的大小。如果編寫的程序比較大,定義的局部變量很多,那麼就需要修改棧的大小。如果某一天,你寫的程序出現了莫名奇怪的錯誤,并進入了硬 fault 的時候,這時你就要考慮下是不是棧不夠大,溢出了。
EQU:宏定義的僞指令,相當于等于,類似于C 中的 define。
AREA:告訴彙編器彙編一個新的代碼段或者數據段。STACK 表示段名,這個可以任意命名;NOINIT 表示不初始化;READWRITE 表示可讀可寫, ALIGN=3,表示按照 2^3對齊,即 8 字節對齊。
SPACE:用于分配一定大小的内存空間,單位為字節。這裡指定大小等于 Stack_Size。
标号__initial_sp 緊挨着 SPACE 語句放置,表示棧的結束地址,即棧頂地址,棧是由高向低生長的。
Heap——堆
開辟堆的大小為 0X00000200(512 字節),名字為 HEAP, NOINIT 即不初始化,可讀可寫, 8(2^3)字節對齊。__heap_base 表示對的起始地址, __heap_limit 表示堆的結束地址。堆是由低向高生長的,跟棧的生長方向相反。
堆主要用來動态内存的分配,像 malloc()函數申請的内存就在堆上面。這個在 STM32裡面用的比較少。
PRESERVE8
THUMB
PRESERVE8:指定當前文件的堆棧按照 8 字節對齊。
THUMB:表示後面指令兼容 THUMB 指令。THUBM 是 ARM 以前的指令集, 16bit,現在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。關于堆棧的文章:關于C語言堆棧的經典講解。
向量表
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
定義一個數據段,名字為 RESET,可讀。并聲明 __Vectors、 __Vectors_End 和__Vectors_Size 這三個标号具有全局屬性,可供外部的文件調用。
嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和内容,導緻工資要不上去!
無償分享大家一個資料包,差不多150多G。裡面學習内容、面經、項目都比較新也比較全!某魚上買估計至少要好幾十。
點擊這裡找小助理0元領取:嵌入式物聯網學習資料(頭條)
EXPORT:聲明一個标号可被外部的文件使用,使标号具有全局屬性。如果是 IAR 編譯器,則使用的是 GLOBAL 這個指令。
當内核響應了一個發生的異常後,對應的異常服務例程(ESR)就會執行。為了決定 ESR的入口地址, 内核使用了―向量表查表機制‖。這裡使用一張向量表。向量表其實是一個WORD(32 位整數)數組,每個下标對應一種異常,該下标元素的值則是該 ESR 的入口地址。向量表在地址空間中的位置是可以設置的,通過 NVIC 中的一個重定位寄存器來指出向量表的地址。在複位後,該寄存器的值為 0。因此,在地址 0 (即 FLASH 地址 0) 處必須包含一張向量表,用于初始時的異常分配。要注意的是這裡有個另類:0 号類型并不是什麼入口地址,而是給出了複位後 MSP 的初值。下圖是F103的向量表。
__Vectors DCD __initial_sp ;棧頂地址
DCD Reset_Handler ;複位程序地址
DCD NMI_Handler
DCD HardFault_Handler
DCD MemManage_Handler
DCD BusFault_Handler
DCD UsageFault_Handler
DCD 0 ; 0 表示保留
DCD 0
DCD 0
DCD 0
DCD SVC_Handler
DCD DebugMon_Handler
DCD 0
DCD PendSV_Handler
DCD SysTick_Handler
;外部中斷開始
DCD WWDG_IRQHandler
DCD PVD_IRQHandler
DCD TAMPER_IRQHandler
;限于篇幅,中間代碼省略
DCD DMA2_Channel2_IRQHandler
DCD DMA2_Channel3_IRQHandler
DCD DMA2_Channel4_5_IRQHandler
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
__Vectors 為向量表起始地址, __Vectors_End 為向量表結束地址,兩個相減即可算出向量表大小。
向量表從 FLASH 的 0 地址開始放置,以 4 個字節為一個單位,地址 0 存放的是棧頂地址, 0X04 存放的是複位程序的地址,以此類推。從代碼上看,向量表中存放的都是中斷服務函數的函數名,可我們知道 C 語言中的函數名就是一個地址。
DCD:分配一個或者多個以字為單位的内存,以四字節對齊,并要求初始化這些内存。在向量表中, DCD 分配了一堆内存,并且以 ESR 的入口地址初始化它們。
複位程序
AREA |.text|, CODE, READONLY
定義一個名稱為.text 的代碼段,可讀。
複位子程序是系統上電後第一個執行的程序,調用 SystemInit 函數初始化系統時鐘,然後調用 C 庫函數_mian,最終調用 main 函數去到 C 的世界。
WEAK:表示弱定義,如果外部文件優先定義了該标号則首先引用該标号,如果外部文件沒有聲明也不會出錯。這裡表示複位子程序可以由用戶在其他文件重新實現,這裡并不是唯一的。
IMPORT:表示該标号來自外部文件,跟 C 語言中的 EXTERN 關鍵字類似。這裡表示 SystemInit 和__main 這兩個函數均來自外部的文件。
SystemInit()是一個标準的庫函數,在 system_stm32f10x.c 這個庫文件中定義。主要作用是配置系統時鐘,這裡調用這個函數之後,單片機的系統時鐘配被配置為 72M。__main 是一個标準的 C 庫函數,主要作用是初始化用戶堆棧,并在函數的最後調用main 函數去到 C 的世界。這就是為什麼我們寫的程序都有一個 main 函數的原因。
LDR、 BLX、 BX 是 CM4 内核的指令,可在《CM3 權威指南 CnR2》第四章-指令集裡面查詢到,具體作用見下表:
中斷服務程序
在啟動文件裡面已經幫我們寫好所有中斷的中斷服務函數,跟我們平時寫的中斷服務函數不一樣的就是這些函數都是空的,真正的中斷服務程序需要我們在外部的 C 文件裡面重新實現,這裡隻是提前占了一個位置而已。
如果我們在使用某個外設的時候,開啟了某個中斷,但是又忘記編寫配套的中斷服務程序或者函數名寫錯,那當中斷來臨的時,程序就會跳轉到啟動文件預先寫好的空的中斷服務程序中,并且在這個空函數中無線循環,即程序就死在這裡。
NMI_Handler PROC ;系統異常
EXPORT NMI_Handler [WEAK]
B .
ENDP
;限于篇幅,中間代碼省略
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC ;外部中斷
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
;限于篇幅,中間代碼省略
LTDC_IRQHandler
LTDC_ER_IRQHandler
DMA2D_IRQHandler
B .
ENDP
B:跳轉到一個标号。這裡跳轉到一個‘.’,即表示無線循環
用戶堆棧初始化
ALIGN
ALIGN:對指令或者數據存放的地址進行對齊,後面會跟一個立即數。缺省表示 4 字節對齊。
;用戶棧和堆初始化,由 C 庫函數_main 來完成
IF :DEF:__MICROLIB ;這個宏在 KEIL 裡面開啟
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory ; 這個函數由用戶自己實現
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
ALIGN
ENDIF
END
首先判斷是否定義了__MICROLIB ,如果定義了這個宏則賦予标号__initial_sp(棧頂地址)、 __heap_base(堆起始地址)、 __heap_limit(堆結束地址)全局屬性,可供外部文件調用。有關這個宏我們在 KEIL 裡面配置,具體見下圖。然後堆棧的初始化就由 C 庫函數_main 來完成。
如果沒有定義__MICROLIB,則才用雙段存儲器模式,且聲明标号__user_initial_stackheap 具有全局屬性,讓用戶自己來初始化堆棧。
前文的彙編代碼,需要注意:
文章來源于STM32嵌入式開發本文轉載自“STM32嵌入式開發”,如有侵權,請聯系删除原文鍊接:詳解STM32啟動文件
版權聲明:本文來源網絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯系我進行删除。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!