文章内容根据野火学习教程进行整理,仅仅是学习记录。
开发板: 野火STM32F429-挑战者V2
官方固件库版本: STM32F4xx_DSP_StdPeriph_Lib_V1.8.0
启动文件介绍
位置
启动文件(例如:startup_stm32f429_439xx.s),该文件存在于官方固件库的目录位置为:STM32F4xx_DSP_StdPeriph_Lib_V1.8.0LibrariesCMSISDeviceSTSTM32F4xxSourceTemplatesarm
具体选择哪份文件就根据所使用的芯片版本来决定。
作用
启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
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 的世界。
文章内容根据野火学习教程进行整理,仅仅是学习记录。
开发板: 野火STM32F429-挑战者V2
官方固件库版本: STM32F4xx_DSP_StdPeriph_Lib_V1.8.0
启动文件介绍
位置
启动文件(例如:startup_stm32f429_439xx.s),该文件存在于官方固件库的目录位置为:STM32F4xx_DSP_StdPeriph_Lib_V1.8.0LibrariesCMSISDeviceSTSTM32F4xxSourceTemplatesarm
具体选择哪份文件就根据所使用的芯片版本来决定。
作用
启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
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 的世界。
举报