一、使用的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由用户自己实现。