本文對(duì)STM32啟動(dòng)文件startup_stm32f10x_hd.s的代碼進(jìn)行講解,此文件的代碼在任何一個(gè)STM32F10x工程中都可以找到。
Stack——棧
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=
Stack_Mem SPACE Stack_Size
__initial_sp
開(kāi)辟棧的大小為 0X00000400(1KB),名字為 STACK, NOINIT 即不初始化,可讀可寫(xiě), 8(2^3)字節(jié)對(duì)齊。
棧的作用是用于局部變量,函數(shù)調(diào)用,函數(shù)形參等的開(kāi)銷(xiāo),棧的大小不能超過(guò)內(nèi)部SRAM 的大小。如果編寫(xiě)的程序比較大,定義的局部變量很多,那么就需要修改棧的大小。如果某一天,你寫(xiě)的程序出現(xiàn)了莫名奇怪的錯(cuò)誤,并進(jìn)入了硬 fault 的時(shí)候,這時(shí)你就要考慮下是不是棧不夠大,溢出了。
EQU:宏定義的偽指令,相當(dāng)于等于,類(lèi)似于C 中的 define。
AREA:告訴匯編器匯編一個(gè)新的代碼段或者數(shù)據(jù)段。STACK 表示段名,這個(gè)可以任意命名;NOINIT 表示不初始化;READWRITE 表示可讀可寫(xiě), ALIGN=3,表示按照 2^3對(duì)齊,即 8 字節(jié)對(duì)齊。
SPACE:用于分配一定大小的內(nèi)存空間,單位為字節(jié)。這里指定大小等于 Stack_Size。
標(biāo)號(hào)__initial_sp 緊挨著 SPACE 語(yǔ)句放置,表示棧的結(jié)束地址,即棧頂?shù)刂罚瑮J怯筛呦虻蜕L(zhǎng)的。
Heap——堆
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
heap_limit
開(kāi)辟堆的大小為 0X00000200(512 字節(jié)),名字為 HEAP, NOINIT 即不初始化,可讀可寫(xiě), 8(2^3)字節(jié)對(duì)齊。__heap_base 表示對(duì)的起始地址, __heap_limit 表示堆的結(jié)束地址。堆是由低向高生長(zhǎng)的,跟棧的生長(zhǎng)方向相反。
堆主要用來(lái)動(dòng)態(tài)內(nèi)存的分配,像 malloc()函數(shù)申請(qǐng)的內(nèi)存就在堆上面。這個(gè)在 STM32里面用的比較少。
PRESERVE8
THUMB
PRESERVE8:指定當(dāng)前文件的堆棧按照 8 字節(jié)對(duì)齊。
THUMB:表示后面指令兼容 THUMB 指令。THUBM 是 ARM 以前的指令集, 16bit,現(xiàn)在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。
向量表
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
定義一個(gè)數(shù)據(jù)段,名字為 RESET,可讀。并聲明 __Vectors、 __Vectors_End 和__Vectors_Size 這三個(gè)標(biāo)號(hào)具有全局屬性,可供外部的文件調(diào)用。
EXPORT:聲明一個(gè)標(biāo)號(hào)可被外部的文件使用,使標(biāo)號(hào)具有全局屬性。如果是 IAR 編譯器,則使用的是 GLOBAL 這個(gè)指令。
當(dāng)內(nèi)核響應(yīng)了一個(gè)發(fā)生的異常后,對(duì)應(yīng)的異常服務(wù)例程(ESR)就會(huì)執(zhí)行。為了決定 ESR的入口地址, 內(nèi)核使用了―向量表查表機(jī)制‖。這里使用一張向量表。向量表其實(shí)是一個(gè)WORD(32 位整數(shù))數(shù)組,每個(gè)下標(biāo)對(duì)應(yīng)一種異常,該下標(biāo)元素的值則是該 ESR 的入口地址。向量表在地址空間中的位置是可以設(shè)置的,通過(guò) NVIC 中的一個(gè)重定位寄存器來(lái)指出向量表的地址。在復(fù)位后,該寄存器的值為 0。因此,在地址 0 (即 FLASH 地址 0) 處必須包含一張向量表,用于初始時(shí)的異常分配。要注意的是這里有個(gè)另類(lèi):0 號(hào)類(lèi)型并不是什么入口地址,而是給出了復(fù)位后 MSP 的初值。下圖是F103的向量表。
__Vectors DCD __initial_sp ;棧頂?shù)刂?DCD Reset_Handler ;復(fù)位程序地址
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
;外部中斷開(kāi)始
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 為向量表結(jié)束地址,兩個(gè)相減即可算出向量表大小。
向量表從 FLASH 的 0 地址開(kāi)始放置,以 4 個(gè)字節(jié)為一個(gè)單位,地址 0 存放的是棧頂?shù)刂罚?0X04 存放的是復(fù)位程序的地址,以此類(lèi)推。從代碼上看,向量表中存放的都是中斷服務(wù)函數(shù)的函數(shù)名,可我們知道 C 語(yǔ)言中的函數(shù)名就是一個(gè)地址。
DCD:分配一個(gè)或者多個(gè)以字為單位的內(nèi)存,以四字節(jié)對(duì)齊,并要求初始化這些內(nèi)存。在向量表中, DCD 分配了一堆內(nèi)存,并且以 ESR 的入口地址初始化它們。
復(fù)位程序
AREA |.text|, CODE, READONLY
定義一個(gè)名稱(chēng)為.text 的代碼段,可讀。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
復(fù)位子程序是系統(tǒng)上電后第一個(gè)執(zhí)行的程序,調(diào)用 SystemInit 函數(shù)初始化系統(tǒng)時(shí)鐘,然后調(diào)用 C 庫(kù)函數(shù)_mian,最終調(diào)用 main 函數(shù)去到 C 的世界。
WEAK:表示弱定義,如果外部文件優(yōu)先定義了該標(biāo)號(hào)則首先引用該標(biāo)號(hào),如果外部文件沒(méi)有聲明也不會(huì)出錯(cuò)。這里表示復(fù)位子程序可以由用戶(hù)在其他文件重新實(shí)現(xiàn),這里并不是唯一的。
IMPORT:表示該標(biāo)號(hào)來(lái)自外部文件,跟 C 語(yǔ)言中的 EXTERN 關(guān)鍵字類(lèi)似。這里表示 SystemInit 和__main 這兩個(gè)函數(shù)均來(lái)自外部的文件。
SystemInit()是一個(gè)標(biāo)準(zhǔn)的庫(kù)函數(shù),在 system_stm32f10x.c 這個(gè)庫(kù)文件中定義。主要作用是配置系統(tǒng)時(shí)鐘,這里調(diào)用這個(gè)函數(shù)之后,單片機(jī)的系統(tǒng)時(shí)鐘配被配置為 72M。__main 是一個(gè)標(biāo)準(zhǔn)的 C 庫(kù)函數(shù),主要作用是初始化用戶(hù)堆棧,并在函數(shù)的最后調(diào)用main 函數(shù)去到 C 的世界。這就是為什么我們寫(xiě)的程序都有一個(gè) main 函數(shù)的原因。
LDR、 BLX、 BX 是 CM4 內(nèi)核的指令,可在《CM3 權(quán)威指南 CnR2》第四章-指令集里面查詢(xún)到,具體作用見(jiàn)下表:
中斷服務(wù)程序
在啟動(dòng)文件里面已經(jīng)幫我們寫(xiě)好所有中斷的中斷服務(wù)函數(shù),跟我們平時(shí)寫(xiě)的中斷服務(wù)函數(shù)不一樣的就是這些函數(shù)都是空的,真正的中斷服務(wù)程序需要我們?cè)谕獠康?C 文件里面重新實(shí)現(xiàn),這里只是提前占了一個(gè)位置而已。
如果我們?cè)谑褂媚硞€(gè)外設(shè)的時(shí)候,開(kāi)啟了某個(gè)中斷,但是又忘記編寫(xiě)配套的中斷服務(wù)程序或者函數(shù)名寫(xiě)錯(cuò),那當(dāng)中斷來(lái)臨的時(shí),程序就會(huì)跳轉(zhuǎn)到啟動(dòng)文件預(yù)先寫(xiě)好的空的中斷服務(wù)程序中,并且在這個(gè)空函數(shù)中無(wú)線(xiàn)循環(huán),即程序就死在這里。
NMI_Handler PROC ;系統(tǒng)異常
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:跳轉(zhuǎn)到一個(gè)標(biāo)號(hào)。這里跳轉(zhuǎn)到一個(gè)‘.’,即表示無(wú)線(xiàn)循環(huán)
用戶(hù)堆棧初始化
ALIGN
ALIGN:對(duì)指令或者數(shù)據(jù)存放的地址進(jìn)行對(duì)齊,后面會(huì)跟一個(gè)立即數(shù)。缺省表示 4 字節(jié)對(duì)齊。
;用戶(hù)棧和堆初始化,由 C 庫(kù)函數(shù)_main 來(lái)完成
IF :DEF:__MICROLIB ;這個(gè)宏在 KEIL 里面開(kāi)啟
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory ; 這個(gè)函數(shù)由用戶(hù)自己實(shí)現(xiàn)
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 ,如果定義了這個(gè)宏則賦予標(biāo)號(hào)__initial_sp(棧頂?shù)刂罚?__heap_base(堆起始地址)、 __heap_limit(堆結(jié)束地址)全局屬性,可供外部文件調(diào)用。有關(guān)這個(gè)宏我們?cè)?KEIL 里面配置,具體見(jiàn)圖 15-2。然后堆棧的初始化就由 C 庫(kù)函數(shù)_main 來(lái)完成。
如果沒(méi)有定義__MICROLIB,則才用雙段存儲(chǔ)器模式,且聲明標(biāo)號(hào)__user_initial_stackheap 具有全局屬性,讓用戶(hù)自己來(lái)初始化堆棧。
IF,ELSE,ENDIF:匯編的條件分支語(yǔ)句,跟 C 語(yǔ)言的 if ,else 類(lèi)似
END:文件結(jié)束
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。
舉報(bào)投訴
-
STM32
+關(guān)注
關(guān)注
2270文章
10900瀏覽量
355985 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62610 -
代碼
+關(guān)注
關(guān)注
30文章
4788瀏覽量
68603
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
詳解STM32啟動(dòng)文件
本文對(duì)STM32啟動(dòng)文件startup_stm32f10x_hd.s的代碼進(jìn)行講解,此
發(fā)表于 06-10 17:19
?1000次閱讀
匯編編寫(xiě)的啟動(dòng)文件startup_stm32f10x_hd.s
匯編編寫(xiě)的啟動(dòng)文件startup_stm32f10x_hd.s: 設(shè)置堆棧指針、設(shè)置 PC 指針、初始化中斷向量表、配置系統(tǒng)時(shí)鐘、調(diào)用 C 庫(kù)函數(shù) _main 最終去到 C 的世界。時(shí)鐘配置文
發(fā)表于 08-23 07:03
startup_stm32f10x_hd.s啟動(dòng)文件中使用的ARM匯編指令都有哪些呢
startup_stm32f10x_hd.s啟動(dòng)文件中使用的ARM匯編指令都有哪些呢?
發(fā)表于 11-26 07:33
如何去實(shí)現(xiàn)startup_stm32f10x_hd.s的匯編文件代碼呢
如何去實(shí)現(xiàn)startup_stm32f10x_hd.s的匯編文件代碼呢?STM32匯編指令與C語(yǔ)言是如何配合的?怎樣對(duì)其進(jìn)行仿真?
發(fā)表于 11-26 06:30
startup_stm32f10x_hd.s啟動(dòng)文件有何功能呢
startup_stm32f10x_hd.s是什么?startup_stm32f10x_hd.s啟動(dòng)文件有何功能呢?
發(fā)表于 11-26 06:38
如何去編寫(xiě)startup_stm32f10x_hd.s啟動(dòng)文件的代碼呢
startup_stm32f10x_hd.s啟動(dòng)文件有何作用呢?如何去編寫(xiě)startup_stm32f10x_hd.s啟動(dòng)
發(fā)表于 11-26 06:44
談一談startup_stm32f10x_hd.s啟動(dòng)文件
startup_stm32f10x_hd.s啟動(dòng)文件有何功能?STM32F10x.s啟動(dòng)文件有何
發(fā)表于 11-26 07:13
如何對(duì)startup_stm32f10x_hd.s啟動(dòng)文件進(jìn)行注釋呢
如何對(duì)startup_stm32f10x_hd.s啟動(dòng)文件進(jìn)行注釋呢?
發(fā)表于 11-26 07:50
怎么去編寫(xiě)startup_stm32f10x_hd.s 文件的源代碼呢
startup_stm32f10x_hd.s是什么?有何作用?怎么去編寫(xiě)startup_stm32f10x_hd.s 文件的源代碼呢?
發(fā)表于 11-26 08:07
stm32的啟動(dòng)文件startup_stm32f10x_hd.s主要包含哪幾個(gè)部分
stm32的啟動(dòng)文件startup_stm32f10x_hd.s主要包含哪幾個(gè)部分?怎樣去編寫(xiě)startup_stm32f10x_hd.s
發(fā)表于 11-29 06:16
關(guān)于stm32啟動(dòng)代碼startup_stm32f10x_hd.s的知識(shí)點(diǎn)匯總
關(guān)于stm32啟動(dòng)代碼startup_stm32f10x_hd.s的知識(shí)點(diǎn)匯總
發(fā)表于 11-30 07:18
startup_stm32f10x_hd.s啟動(dòng)文件有何功能呢
startup_stm32f10x_hd.s是什么?startup_stm32f10x_hd.s啟動(dòng)文件有何功能呢?
發(fā)表于 01-19 06:13
關(guān)于STM32啟動(dòng)文件startup_stm32f10x_hd.s的代碼
本文對(duì)STM32啟動(dòng)文件startup_stm32f10x_hd.s的代碼進(jìn)行講解,此
評(píng)論