Stack_Size EQU 0x00000400
AREA STACK, NOINIT,READWRITE,ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
定义栈空间
1. EQU:宏定义的伪指令,相当于c语言中的#define。
规定栈的大小是0x00000400(1024)字节。栈的大小是可以更改的,如果程序较大或者定义的局部变量很多,则可能在运行时发生栈的溢出,这时需要修改栈的大小。
2. AREA:告诉汇编器汇编一个新的代码段或数据段。
定义栈空间,命名为STACK,NOINIT不初始化,READWRITE可读可写,ALIGN=3表示8
(2的3次方)字节对齐。
3. SPACE:用于分配一定大小的内存空间,单位字节。分配的栈的大小空间是Stack_Size。
__initial_sp 表示栈的结束地址,栈是由高地址向低地址生长的,所以__initial_sp 指向的是栈底指针。
Heap_Size EQU 0x00000C00
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
定义堆空间
- 堆的大小设置为0xC00(3k)
- 定义一个命名为HEAP的区域,不做初始化,可读可写,对齐方式为8字节对齐。
- __heap_base为堆的起始地址,Heap_Size为堆区的大小,__heap_limit为堆的结束地址。
- 如果在程序员编写程序并没有使用到堆,即没有向堆区申请数据区,那么编译之后堆区的大小为0.
堆区和栈区的数据区域分配情况可以查看.map文件。
PRESERVE8
THUMB
这两条语句有承接上下文的作用:
PRESERVE8 表示当前文件的堆栈按照8字节对齐,THUMB 表示后面的指令兼容THUMB指令集。
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY, ALIGN=8
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __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 MemMange_Handler ;MPU Fault Handler
DCD BusFault_Handler ;Bus Fault Handler
DCD UsageFault_Handler ;Usage Fault Handler
DCD 0 ;Reserved
DCD 0 ;Reseverd
DCD 0 ;Reserved
DCD 0 ;Reseverd
DCD SVC_Handler ;SVCall Handler
DCD DebugMon_Handler ;Debug Monitor Handler
DCD 0 ;Reseved
DCD PendSV_Handler ;PendSV Handler
DCD SysTick_Handler ;Systick Handler
; External Interrupts
DCD DMA_IRQHandler ; 0: DMA Interrupt
DCD GPIO_EVEN_IRQHandler ; 1: GPIO_EVEN Interrupt
DCD TIMER0_IRQHandler ; 2: TIMER0 Interrupt
DCD USART0_RX_IRQHandler ; 3: USART0_RX Interrupt
DCD USART0_TX_IRQHandler ; 4: USART0_TX Interrupt
DCD USB_IRQHandler ; 5: USB Interrupt
DCD ACMP0_IRQHandler ; 6: ACMP0 Interrupt
DCD ADC0_IRQHandler ; 7: ADC0 Interrupt
DCD DAC0_IRQHandler ; 8: DAC0 Interrupt
DCD I2C0_IRQHandler ; 9: I2C0 Interrupt
DCD I2C1_IRQHandler ; 10: I2C1 Interrupt
DCD GPIO_ODD_IRQHandler ; 11: GPIO_ODD Interrupt
DCD TIMER1_IRQHandler ; 12: TIMER1 Interrupt
DCD TIMER2_IRQHandler ; 13: TIMER2 Interrupt
DCD TIMER3_IRQHandler ; 14: TIMER3 Interrupt
DCD USART1_RX_IRQHandler ; 15: USART1_RX Interrupt
DCD USART1_TX_IRQHandler ; 16: USART1_TX Interrupt
DCD LESENSE_IRQHandler ; 17: LESENSE Interrupt
DCD USART2_RX_IRQHandler ; 18: USART2_RX Interrupt
DCD USART2_TX_IRQHandler ; 19: USART2_TX Interrupt
DCD UART0_RX_IRQHandler ; 20: UART0_RX Interrupt
DCD UART0_TX_IRQHandler ; 21: UART0_TX Interrupt
DCD UART1_RX_IRQHandler ; 22: UART1_RX Interrupt
DCD UART1_TX_IRQHandler ; 23: UART1_TX Interrupt
DCD LEUART0_IRQHandler ; 24: LEUART0 Interrupt
DCD LEUART1_IRQHandler ; 25: LEUART1 Interrupt
DCD LETIMER0_IRQHandler ; 26: LETIMER0 Interrupt
DCD PCNT0_IRQHandler ; 27: PCNT0 Interrupt
DCD PCNT1_IRQHandler ; 28: PCNT1 Interrupt
DCD PCNT2_IRQHandler ; 29: PCNT2 Interrupt
DCD RTC_IRQHandler ; 30: RTC Interrupt
DCD BURTC_IRQHandler ; 31: BURTC Interrupt
DCD CMU_IRQHandler ; 32: CMU Interrupt
DCD VCMP_IRQHandler ; 33: VCMP Interrupt
DCD LCD_IRQHandler ; 34: LCD Interrupt
DCD MSC_IRQHandler ; 35: MSC Interrupt
DCD AES_IRQHandler ; 36: AES Interrupt
DCD EBI_IRQHandler ; 37: EBI Interrupt
DCD EMU_IRQHandler ; 38: EMU Interrupt
__Vectors_End
__Vectors_Size EQU __Vectors_End – Vectors
初始化复位向量表
1. 定义一个数据段,命名为RESET,只读模式,256字节对齐。
EXPORT:声明一个符号具有全局属性,可以被外部文件调用。
声明了__Vectors, __Vetors_End, __Vectors_Size 这三个具有全局属性的符号。
2.向量表从 0地址开始放置,以4字节为一个单位,地址0存放的是栈顶地址(此时栈为空),0x4存放的是复位程序的地址,以此类推。
DCD:以4字节对齐的方式分配内存,并对这些内存进行初始化。
AREA |.text|, CODE, READONLY
; Reset_Handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
定义复位中断向量
- 定义一个代码段,只读。
PROC:定义子程序,与ENDP一同使用表示程序结束
2. 定义全局属性的Reset_Handler。
[WEAK]:弱定义,如果外部文件声明了这个标号,优先使用外部文件的定义,如果外部文件没有定义也不会出错。
3. IMPORT:声明标号来自于外部文件,类似于c语言的extern
LDR:加载指令,从存储器中加载一个指令到寄存器中。
BLX:跳到由寄存器给出的地址,并根据寄存器的LES确定处理器的状态,把跳转前的下一条指令地址保存到LR
BX:跳转到由寄存器给出的地址,不用返回。
4. 这里的_main不是用户程序里面的main(),这是编译系统提供的一个函数,__main负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main(),不再返回。
__main函数中有两个大的函数:
(1)__scatterload():负责把RW/RO输出段从装载域地址复制到运行域地址,并完成ZI运行域的初始化工作。RO是程序中的指令和变量,RW是程序中的已初始化变量,ZI是程序中的未初始化的变量。
(2)__rt_entry():负责初始化堆栈,完成库函数的初始化,最后自动跳转到main()函数。
其中初始化堆栈调用的是__user_initial_stackheap()
;Dummy Exception Handlers (infinite loops which can be modified)
;下面的这些函数都是空的,真正的中断服务函数需要我们在用户的c文件里面重新实现,
;如果用户没有实现,就会进到这里无限循环。
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
;默认的中断服务程序开始,以下是外设中断服务程序,当CPU检查到某个中断产生时,硬
;件会根据提供的中断号自动跳转到向量表中与相对应的中断服务函数的入口地址。
Default_Handler PROC
EXPORT DMA_IRQHandler [WEAK]
EXPORT GPIO_EVEN_IRQHandler [WEAK]
EXPORT TIMER0_IRQHandler [WEAK]
EXPORT USART0_RX_IRQHandler [WEAK]
EXPORT USART0_TX_IRQHandler [WEAK]
EXPORT USB_IRQHandler [WEAK]
EXPORT ACMP0_IRQHandler [WEAK]
EXPORT ADC0_IRQHandler [WEAK]
EXPORT DAC0_IRQHandler [WEAK]
EXPORT I2C0_IRQHandler [WEAK]
EXPORT I2C1_IRQHandler [WEAK]
EXPORT GPIO_ODD_IRQHandler [WEAK]
EXPORT TIMER1_IRQHandler [WEAK]
EXPORT TIMER2_IRQHandler [WEAK]
EXPORT TIMER3_IRQHandler [WEAK]
EXPORT USART1_RX_IRQHandler [WEAK]
EXPORT USART1_TX_IRQHandler [WEAK]
EXPORT LESENSE_IRQHandler [WEAK]
EXPORT USART2_RX_IRQHandler [WEAK]
EXPORT USART2_TX_IRQHandler [WEAK]
EXPORT UART0_RX_IRQHandler [WEAK]
EXPORT UART0_TX_IRQHandler [WEAK]
EXPORT UART1_RX_IRQHandler [WEAK]
EXPORT UART1_TX_IRQHandler [WEAK]
EXPORT LEUART0_IRQHandler [WEAK]
EXPORT LEUART1_IRQHandler [WEAK]
EXPORT LETIMER0_IRQHandler [WEAK]
EXPORT PCNT0_IRQHandler [WEAK]
EXPORT PCNT1_IRQHandler [WEAK]
EXPORT PCNT2_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT BURTC_IRQHandler [WEAK]
EXPORT CMU_IRQHandler [WEAK]
EXPORT VCMP_IRQHandler [WEAK]
EXPORT LCD_IRQHandler [WEAK]
EXPORT MSC_IRQHandler [WEAK]
EXPORT AES_IRQHandler [WEAK]
EXPORT EBI_IRQHandler [WEAK]
EXPORT EMU_IRQHandler [WEAK]
DMA_IRQHandler
GPIO_EVEN_IRQHandler
TIMER0_IRQHandler
USART0_RX_IRQHandler
USART0_TX_IRQHandler
USB_IRQHandler
ACMP0_IRQHandler
ADC0_IRQHandler
DAC0_IRQHandler
I2C0_IRQHandler
I2C1_IRQHandler
GPIO_ODD_IRQHandler
TIMER1_IRQHandler
TIMER2_IRQHandler
TIMER3_IRQHandler
USART1_RX_IRQHandler
USART1_TX_IRQHandler
LESENSE_IRQHandler
USART2_RX_IRQHandler
USART2_TX_IRQHandler
UART0_RX_IRQHandler
UART0_TX_IRQHandler
UART1_RX_IRQHandler
UART1_TX_IRQHandler
LEUART0_IRQHandler
LEUART1_IRQHandler
LETIMER0_IRQHandler
PCNT0_IRQHandler
PCNT1_IRQHandler
PCNT2_IRQHandler
RTC_IRQHandler
BURTC_IRQHandler
CMU_IRQHandler
VCMP_IRQHandler
LCD_IRQHandler
MSC_IRQHandler
AES_IRQHandler
EBI_IRQHandler
EMU_IRQHandler
B .
ENDP
定义其它中断向量
B:跳转到一个标号,. :代表当前地址,B . :代表执行一个死循环。
上面这些中断服务程序不管是系统的还是外设的,都用[WEAK]声明,我们写中断服务函数的时候,其实都会自己实现。
ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数,不写默认表示4字节对齐。
; User Initial Stack & Heap
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 PROC
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ENDP
ALIGN
ENDIF
END
堆和栈的初始化
上面我们只是开辟了堆和栈的空间并没有进行初始化,
IF、ELSE、ENDIF和c语言的#if、#else、#endif一样
如果定义了_MICROLIB就把__initial_sp、__heap_limit、__heap_base声明为全局文件,如果没有定义就执行__user_initial_stackheap这段代码。
__user_initial_stackheap PROC ;定义子程序
LDR R0, = Heap_Mem ;将堆的起始地址给R0
LDR R1, =(Stack_Mem + Stack_Size) ;将栈的结束地址给R1
LDR R2, = (Heap_Mem + Heap_Size) ;将堆的结束地址给R2
LDR R3, = Stack_Mem ;将栈的起始地址给R3
BX LR ;子程序返回
ENDP ;子程序结束
stm32的启动文件所做的工作
- 初始化堆和栈的指针
- 初始化PC指针,指向复位程序
- 初始化中断向量表
- 设置系统时钟
- 调用__main (1)负责相关代码和数据的复制(2)负责ZI数据段的初始化
- 调用__rt_entry,负责设置运行环境(在c库函数中)(1)设置应用程序堆栈(2)初始化相应的库函数
- 调用main函数开始执行用户代码