历史上的今天
今天是:2024年12月14日(星期六)
2021年12月14日 | STM32启动文件执行步骤分析
2021-12-14 来源:eefocus
一、启动文件简介
启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
1、初始化堆栈指针 SP=_initial_sp
2、初始化 PC 指针=Reset_Handler
3、初始化中断向量表
4、配置系统时钟
5、调用 C 库函数_main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界
二、启动文件代码讲解
1.Stack—栈
1 Stack_Size EQU 0x00000400
2
3 AREA STACK, NOINIT, READWRITE, ALIGN=3
4 Stack_Mem SPACE Stack_Size
5 __initial_sp
开辟栈的大小为 0X00000400(1KB),名字为 STACK,NOINIT 即不初始化,可读可写,8(2^3)字节对齐。
栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。
EQU:宏定义的伪指令,相当于等于,类似与 C 中的 define。
AREA:告诉汇编器汇编一个新的代码段或者数据段。STACK 表示段名,这个可以任
意命名;NOINIT表示不初始化;READWRITE表示可读可写,ALIGN=3,表示按照 2^3对齐,即 8 字节对齐。
SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于 Stack_Size。
标号__initial_sp紧挨着 SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高
向低生长的。
2. Heap 堆
1 Heap_Size EQU 0x00000200
2
3 AREA HEAP, NOINIT, READWRITE, ALIGN=3
4 __heap_base
5 Heap_Mem SPACE Heap_Size
6 __heap_limit
PRESERVE8
THUMB
开辟堆的大小为 0X00000200(512 字节),名字为 HEAP,NOINIT 即不初始化,可读可写,8(2^3)字节对齐。
堆主要用来动态内存的分配,像 malloc()函数申请的内存就在堆上面。这个在 STM32里面用的比较少。
PRESERVE8:指定当前文件的堆栈按照 8 字节对齐。
THUMB:表示后面指令兼容 THUMB 指令。
3. 向量表
1 AREA RESET, DATA, READONLY
2 EXPORT __Vectors
3 EXPORT __Vectors_End
4 EXPORT __Vectors_Size
定义一个数据段,名字为 RESET,可读。
当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定 ESR的入口地址, 内核使用了“向量表查表机制”。
EXPORT:声明一个标号可被外部的文件使用,使标号具有全局属性。如果是 IAR 编译器,则使用的是 GLOBAL 这个指令。
4. 复位程序
1 AREA |.text|, CODE, READONLY
定义一个名称为.text 的代码段,可读。
1 Reset_Handler PROC
2 EXPORT Reset_Handler [WEAK]
3 IMPORT SystemInit
4 IMPORT __main
5
6 LDR R0, =SystemInit
7 BLX R0
8 LDR R0, =__main
9 BX R0
10 ENDP
复位子程序是系统上电后第一个执行的程序,调用 SystemInit 函数初始化系统时钟,然后调用 C 库函数_mian,最终调用 main 函数去到 C 的世界。
WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。
IMPORT:表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似。
SystemInit()是一个标准的库函数,在 system_stm32f10x.c这个库文件总定义。主要作用是配置系统时钟,这里调用这个函数之后,单片机的系统时钟配被配置为 72M。
__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,并在函数的最后调用main 函数去到 C 的世界。这就是为什么我们写的程序都有一个 main 函数的原因。
5. 中断服务程序
在启动文件里面已经帮我们写好所有中断的中断服务函数,跟我们平时写的中断服务函数不一样的就是这些函数都是空的,真正的中断复服务程序需要我们在外部的 C 文件里面重新实现,这里只是提前占了一个位置而已。
1 NMI_Handler PROC ;系统异常
2 EXPORT NMI_Handler [WEAK]
3 B .
4 ENDP
5
18 LTDC_IRQHandler
19 LTDC_ER_IRQHandler
20 DMA2D_IRQHandler
21 B .
B:跳转到一个标号。这里跳转到一个‘.’,即表示无线循环。
6. 用户堆栈初始化
1 ALIGN
ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示 4 字节对齐。
1 ;用户栈和堆初始化,由 C 库函数_main 来完成
2 IF :DEF:__MICROLIB ;这个宏在 KEIL 里面开启
3
4 EXPORT __initial_sp
5 EXPORT __heap_base
6 EXPORT __heap_limit
7
首先判断是否定义了__MICROLIB ,如果定义了这个宏则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用。然后堆栈的初始化就由 C 库函数_main 来完成。
一、收获
1.栈溢出
如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。如果某一天,你写的程序出现了莫名奇怪的错误,并进入了硬 fault 的时候,这时你就要考虑下是不是栈不够大,溢出了。
史海拾趣
|
我的电路板用一个1085-5V芯片转换成5V,用一个1085-3.3V转换成3.3V,5V给AD等外围芯片供电,3.3主要给核心板供电,我的程序运行一会,核心板,电源芯片就会很烫,电源芯片带的底板也很烫。功率也满足要求。请问各位高手该如何解决发热问题?… 查看全部问答> |
|
将内存单元1000-1030H中的内容复制到3000-3030H单元中去,原地址清0 我的代码在下面,下载地址1000H,自修改代码,他说改下载地址,自己内存放点东西,他要看到结果,不放东西全部是0,复制过去也没效果。老师很BT,要求十分严格。EMBESTIDE环境 ...… 查看全部问答> |
|
本人的工作是将ucgui移植到nucleus系统上,nucleus系统时间的获取是通过将时钟节拍转化而来的,但本人对时钟节拍不怎么熟悉,按照自己的思路问题主要是出在到时钟节拍超出时钟节拍临界值时(其实本人也不清楚时钟节拍复位为0是怎么处理的),时 ...… 查看全部问答> |
|
TIM4的4路输出能从PB6..PB9,但是不能从PD12...PD15输出 端口的配置完全一样,且增加的函数GPIO_PinRemapConfig(GPIO_Remap_TIM4, ENABLE);… 查看全部问答> |
|
请版主帮指个路。 各位代理有的话,欢迎报价。 我手头的STM8L101都连不上SWIM,用ST-Link也不行,郁闷。 STM8L152可以。 已经有客户求购EP868用于STM8L了。。。。。… 查看全部问答> |




