STM32启动文件详解——学习笔记(3)

文章内容根据野火学习教程进行整理,仅仅是学习记录。

开发板: 野火STM32F429-挑战者V2
官方固件库版本: STM32F4xx_DSP_StdPeriph_Lib_V1.8.0


启动文件介绍

位置

启动文件(例如:startup_stm32f429_439xx.s),该文件存在于官方固件库的目录位置为:STM32F4xx_DSP_StdPeriph_Lib_V1.8.0\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm
在这里插入图片描述
具体选择哪份文件就根据所使用的芯片版本来决定。

作用

启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
1、 初始化堆栈指针 SP=_initial_sp
2、 初始化 PC 指针=Reset_Handler
3、 初始化中断向量表
4、 配置系统时钟
5、 调用 C 库函数_main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界

这个说明其实在每份启动文件的最前面都有英文注释
在这里插入图片描述


所使用到的汇编指令说明

启动文件中所使用到的汇编指令解释
要查找更详细的汇编指令解释可以打开ARM Development Tools进行搜索查询。
使用搜索功能,点开标题为Assembler User Guide(汇编用户指南)的主题。
ARM Development Tools
在这里插入图片描述
在这里插入图片描述


启动文件程序讲解

;分号是注释的意思,相当于C中的/* */ 或者 //。

栈初始化

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

解释:
1、第一行:定义Stack_Size这个变量为0x00000400即1K。
2、第二行:定义一个名字为STACK的代码段或数据段,不初始化,可读可写,8( 2^3)字节对齐。
3、第三行:分配一个大小为Stack_Size的内存空间,单位为字节。
4、第四行:__initial_sp 紧挨着 SPACE 语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。
5、栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。

堆初始化

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB
解释:
1、第一行:定义Heap_Size这个变量为0x00000200即512 字节。
2、第二行:定义一个名字为HEAP的代码段或数据段,不初始化,可读可写,8( 2^3)字节对齐。
3、第三行:堆的起始地址,用于计算。
4、第四行:分配一个大小为Heap_Size的内存空间,单位为字节。
5、第五行:堆的终止地址,用于计算。
6、第六行:指定当前文件的堆栈按照 8 字节对齐。
7、表示后面指令兼容 THUMB 指令。 THUBM 是 ARM 以前的指令集, 16bit,现在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。
8、用__heap_limit减去__heap_base即可得到堆的总空间。堆是由低向高生长的,跟栈的生长方向相反。
9、堆主要用来动态内存的分配,像 malloc()函数申请的内存就在堆上面。这个在 STM32里面用的比较少。

向量表

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                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     LTDC_ER_IRQHandler                ; LTDC error
                DCD     DMA2D_IRQHandler                  ; DMA2D
                                         
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

解释:
1、第一行注释:说明向量表映射到了RESET字段的地址0的位置。即第二行要定义的数据段。
2、第二行:定义一个数据段,名字为 RESET,可读
3、第三、四、五行:声明 __Vectors、 __Vectors_End 和__Vectors_Size 这三个标号具有全局属性,可供外部的文件调用。
如果是 IAR 编译器,则使用的是 GLOBAL 这个指令。
4、第六行:分配一个堆内存,以四个字节对齐,并以ESR的入口地址(即函数指针)初始化,并放在向量表的起始地址__Vectors位置。ESR对于内核来说就是异常,对外设来说就是中端。
5、第七、八、九、十、十一行:分配堆内存,以四个字节对齐,并以ESR的入口地址(即函数指针)初始化。
6、第十一行:向量表结束地址。
7、第十二行:向量表的大小,用起始地址 - 终止地址。

复位程序

                AREA    |.text|, CODE, READONLY

; Reset handler
;WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。
;IMPORT:表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似。这里表示 SystemInit 和__main 这两个函数均来自外部的文件。
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

解释:
1、第一行:定义一个名称为.text 的代码段,可读。
2、第二行:表示复位程序Reset_Handler的开始,PROC与第十行的ENDP成对出现,表示程序的开始与结束。
3、第三行:声明Reset_Handler函数为全局属性,可以被其他文件调用。后面的WEAK表示该定义是弱引用。
4、第四行:插入SystemInit这个函数,该函数在别的地方定义的,不在本汇编文件的当中。
5、第五行:插入__main这个函数,该函数在别的地方定义的,不在本汇编文件的当中。
6、第六行:把SystemInit这个函数加载到R0这个寄存器。
7、第七行:执行R0这个寄存器即SystemInit这个函数并返回。
8、第八行:把__main这个函数加载到R0这个寄存器。
9、第九行:执行R0这个寄存器即__main这个函数但是不返回。这个时候跳转到了C的世界,即C中的main函数。
10、第十行:与PROC成对出现,表示复位程序Reset_Handler结束。
11、IMPORT:表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似。这里表示 SystemInit 和__main 这两个函数均来自外部的文件。
12、表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。

中断程序

拿其中一个举例,与复位程序类似

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
                 省略。。。
                 ALIGN
解释:
1、第一行:表示中断程序NMI_Handler的开始,PROC与第四行的ENDP成对出现,表示程序的开始与结束。
2、第二行:声明NMI_Handler函数为全局属性,可以被其他文件调用。后面的WEAK表示该定义是弱引用。
3、第三行:跳转到标号(.),也就是进入了无限循环。
4、第四行:与PROC成对出现,表示中断程序NMI_Handler结束。
5、第五行:对指令或者数据存放的地址进行对齐,缺省表示 4 字节对齐(也就是ALIGN=2的意思)。
6、这里面的WEAK的意思是弱引用,详细的说就是如果别的地方有定义一个名字为NMI_Handler的函数则不会进入本汇编函数中写的NMI_Handler函数,也就不会执行到( B       .)这个命令。如果外部没有定义名字为NMI_Handler的函数则进入本汇编函数后会进入无限循环。

用户堆栈初始化

                 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

解释:
1、IF,ELSE,ENDIF:汇编的条件分支语句,跟 C 语言的 if ,else 类似。
2、END:文件结束。
3、如果定义了__MICROLIB则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、 __heap_limit(堆结束地址)全局属性,可供外部文件调用。
4、如果没有定义__MICROLIB则使用默认的 C 库,然后初始化用户堆栈大小,这部分有 C 库函数__main 来完成,当初始化完堆栈之后,就调用 main函数去到 C 的世界。

参考文档:【野火】零死角玩转STM32—F429挑战者V2
下载地址:http://products.embedfire.com/zh_CN/latest/stm32/ebf_stm32f429_tiaozhanzhe_v2.html
野火论坛:http://www.firebbs.cn/forum.php?fromuid=61258

发布了62 篇原创文章 · 获赞 106 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/lang523493505/article/details/104092791
今日推荐