startup_stm32f407xx.s分析

一、使用的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由用户自己实现。
发布了81 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_33575901/article/details/103135465