一、使用的ARM彙編指令
指令名稱 |
指令作用 |
EQU |
給數字常量取一個符號名,相當於C語言中的define |
AREA |
彙編一個新的代碼段或者數據段 |
SPACE |
分配內存空間 |
PRESERVE8 |
當前文件堆棧需按照8字節對齊 |
EXPORT |
聲明一個具有全局屬性的標號,可被外部文件使用 |
IMPORT |
聲明一個來自外部文件的標號 |
DCD |
以字爲單位分配內存,要求4字節對齊,並要求初始化這些內存 |
PROC |
定義子程序,與ENDP成對使用 |
ENDP |
表示子程序結束 |
WEAK |
弱定義,如果外部文件定義了一個標號,則優先使用外部文件的標號,是ARM僞指令 |
ALIGN |
編譯器對指令或數據的存放地址進行對齊,缺省表示4字節對齊,是ARM僞指令 |
IF,ELSE,ENDIF |
彙編條件分支語句,同C語言的類似 |
LDR |
從存儲器中加載字到一個寄存器中 |
B |
跳轉到一個標號 |
BL |
跳轉到由寄存器/標號給出的地址,並保存跳轉前的下條指令至LR中 |
BX |
跳轉到由寄存器/標號給出的地址,並根據寄存器的LSE確定處理器的狀態 |
BLX |
跳轉到由寄存器/標號給出的地址,並根據寄存器的LSE確定處理器的狀態,同時保存跳轉前的下條指令至LR中 |
END |
到達文件的末尾,文件結束 |
二、文件的主要功能
- 堆棧以及堆的初始化
- 初始化中斷向量表
- 調用Reset_Handler
- 其他代碼
三、文件內容剖析
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
初始化棧:
開闢棧大小爲0x00000400(1KB),名字爲STACK,NOINIT不初始化,可讀可寫,8(2^3)字節對齊。 棧的作用是用於局部變量,函數調用,函數形參等的開銷,棧的大小不能超過內部SRAM 的大小。如果編寫的程序比較大,定義的局部變量很多,那麼就需要修改棧的大小。如果某一天,你寫的程序出現了莫名奇怪的錯誤,並進入了硬fault 的時候,這時你就要考慮下是不是棧不夠大而溢出了。
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
初始化堆:
開闢堆大小爲0x00000200(512字節),名字爲HEAP,不初始化,可讀可寫,8字節對齊。堆主要用於動態內存的分配。
PRESERVE8
THUMB
指定當前文件的堆棧按照8字節對齊,且後面的指令兼容THUMB指令
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
定義一個名爲RESET的數據段,只讀。並聲明 __Vectors、__Vectors_End 和__Vectors_Size 三個具有全局屬性的標號,可供外部的文件調用。
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
......
DCD HASH_RNG_IRQHandler ; Hash and Rng
DCD FPU_IRQHandler ; FPU
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
__Vectors 爲向量表起始地址,__Vectors_End 爲向量表結束地址,兩個相減即可算出向量表大小。向量表從FLASH 的0地址開始放置,以4個字節爲一個單位,地址0 存放的是棧頂地址,0X04存放的是復位程序的地址,以此類推。
AREA |.text|, CODE, READONLY
定義一個名爲.text的代碼段,只讀。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
復位子程序是系統上電後第一個執行的程序,調用SystemInit函數初始化系統時鐘,然後調用C庫函數__main,最終調用main函數去到C的世界。
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
......
B .
ENDP
B .表示無限循環
在啓動文件裏面已經幫我們寫好所有中斷的中斷服務函數,跟我們平時寫的中斷服務函數不一樣的就是這些函數都是空的,真正的中斷復服務程序需要我們在外部的C 文件裏面重新實現,這裏只是提前佔了一個位置而已。
如果我們在使用某個外設的時候,開啓了某個中斷,但是又忘記編寫配套的中斷服務程序或者函數名寫錯,那當中斷來臨的時,程序就會跳轉到啓動文件預先寫好的空的中斷服務程序中,並且在這個空函數中無限循環,即程序就死在這裏。
IF :DEF:__MICROLIB
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
用戶堆棧初始化由C庫函數__main完成,__MICROLIB由KEIL裏面開啓,__use_two_region_memory由用戶自己實現。