电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
STM32第二章-启动过程详解
分 享
扫描二维码分享
STM32第二章-启动过程详解
嵌入式
STM32
C语言
果果小师弟
关注
发布时间: 2020-06-16
丨
阅读: 1272
讲解启动过程之前先简单了解一下内存五区: **1.栈区stack**:由编译器自动分配释放,存放函数的参数值,局部变量的值。 **2.堆区heap**:由程序员分配和释放,若程序员不释放,程序结束时由OS回收。 **3.全局区(静态区 static)**:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。 **4.文字常量区**:常量字符串就是放在这里的。 **5.程序代码区** : 存放函数体的二进制代码。 话不多说先看一段代码: ```c int a = 0; //全局初始化区, 可以被其他c文件 extern 引用 static int ss = 0; //静态变量,只允许在本文件使用 char *p1; //全局未初始化区 void main(void) { int b; //栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //123456\0在常量区,p3在栈上。 static int c =0; //全局(静态)初始化区 p1 = (char *)malloc(10); //在堆区申请了10个字节空间 p2 = (char *)malloc(20); //在堆区申请了20个字节空间 strcpy(p1, "123456"); /@@* 123456字符串(结束符号是0,总长度7)放在常量区,编译器可能会 将它与p3所指向的"123456"优化成一个地方 */ } ``` 简单的了解了内存五区之后再来看看STM32 的启动过程。 STM32 的启动过程是指从 CPU 上电复位执行第 1 条指令开始到进入 C 程序 main()函数入口之间的部分。启动过程相对来说还是比较重要的,虽然不好理解但必须了解掌握。 1. 不同的系列芯片的的启动代码不同。 2. 启动过程主要完成的工作:(startup_stm32xxxx.s) ![ ](https://cdn.jsdelivr.net/gh/xiaoshidi-hub/PicGO/picture/qidong.png) STM32 的启动过程,启动过程是指从 CPU 上电复位执行第 1 条指令开始(汇编文件)到进入 C 程序 main()函数入口之间的部分。启动过程相对来说还是比较重要的,虽然难但必须了解掌握。 3.打开你的工程,鼠标双击工程文件。就会出来对应的.out文件查看中断向量列表在内部flash的存储。 ![ ](https://img-blog.csdnimg.cn/20200324085710862.png) ![ ](https://img-blog.csdnimg.cn/20200324085822158.png) 4.复位序列 硬件复位之后,就是按下复位开关后。CPU 内的时序逻辑电路首先完成如下两个工作(程序代码下载到内部flash为例,flash首地址 0x0800 0000) * 将 0x08000000 位置存放的堆栈栈顶地址存放到 SP 中(MSP)。 * 将 0x08000004 位置存放的向量地址装入 PC 程序计数器。 CPU 从 PC 寄存器指向的物理地址取出第 1 条指令开始执行程序,也就是开始执行复位中断服务程序 Reset_Handler。复位中断服务程序会调用SystemInit()函数来配置系统时钟、配置FMC总线上的外部SRAM/SDRAM,然后跳转到 C 库中__main 函数。由 C 库中的__main 函数完成用户程序的初始化工作(比如:变量赋初值等),最后由__main 函数调用用户写的 main()函数开始执行 C 程序。 学习启动文件之前先来了解一下汇编语言中的一些指令操作。 ![启动文件使用的 ARM 汇编指令汇总](https://img-blog.csdnimg.cn/2020032320502673.png) ### 第 1 部分代码分析 下面的代码实现开辟**栈(stack)空间**,用于局部变量、函数调用、函数的参数等。 栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。如果某一天,你写的程序出现了莫名奇怪的错误,并进入了硬 fault的时候,这时你就要考虑下是不是栈不够大,溢出了。 ![ ](https://img-blog.csdnimg.cn/20200323202329836.png) **第 7 行**:EQU 是表示宏定义的伪指令,**类似于 C 语言中的#define**。伪指令的意思是指这个“指令”并不会生成二进制程序代码,也不会引起变量空间分配。0x00008000 表示栈大小,注意这里是以字节为单位。0x00008000 =32768字节=32KB **第 8 行**:开辟一段数据空间可读可写,段名 STACK,按照 8 字节对齐。ARER 伪指令表示下面将开始定义一个代码段或者数据段。此处是定义数据段。ARER 后面的关键字表示这个段的属性。 * STACK :表示这个段的名字,可以任意命名。 * NOINIT:表示此数据段不需要填入初始数据。 * READWRITE:表示此段可读可写。 * ALIGN=3 :表示首地址按照 2 的 3 次方对齐,也就是按照 8 字节对齐(地址对 8 求余数等于0)。 **第 9 行**:SPACE 这行指令告诉汇编器给 STACK 段分配 0x00000800 字节的连续内存空间。 **第 10 行**: __initial_sp 紧接着 SPACE 语句放置,表示了栈顶地址。__initial_sp 只是一个标号,标号主要用于表示一片内存空间的某个位置,等价于 C 语言中的“地址”概念。地址仅仅表示存储空间的一个位置,从 C 语言的角度来看,变量的地址,数组的地址或是函数的入口地址在本质上并无区别。 ### 第 2 部分代码分析 下面的代码实现开辟**堆(heap)空间**,主要用于动态内存分配,像 malloc,calloc, realloc 等函数分配的变量空间是在堆上。 ![ ](https://img-blog.csdnimg.cn/202003232024220.png) 这几行语句和上面第 1 部分代码类似。分配一片连续的内存空间给名字叫 HEAP 的段,也就是分配堆空间。堆的大小为 0x00000400,也就是1024字节=1KB。 __heap_base 表示堆的开始地址。 __heap_limit 表示堆的结束地址。 ### 第 3部分代码分析 ![ ](https://img-blog.csdnimg.cn/20200323202924525.png) **第 24 行**:PRESERVE8 指定当前文件**保持堆栈8字节对齐**。 **第 25 行**:THUMB 表示后面的指令是 THUMB 指令集 ,CM4 采用的是 THUMB - 2 指令集。 **第 29 行**:AREA 定义一块代码段,只读,段名字是 RESET。READONLY 表示只读,缺省就表示代码段了。 **第 30-32 行**:3 行 EXPORT 语句将 3 个标号申明为可被外部引用, 主要提供给链接器用于连接库文件或其他文件。当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定 ESR的入口地址, 内核使用了―向量表查表机制‖。这里使用一张向量表。向量表其实是一个WORD( 32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地 址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向 量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0)处必须包 含一张向量表,用于初始时的异常分配。要注意的是这里有个另类: 0 号类型并不是什么 入口地址,而是给出了复位后 MSP 的初值。 ### 第 4部分代码分析 ![ ](https://img-blog.csdnimg.cn/2020032320342853.png) ![向量表](https://img-blog.csdnimg.cn/20200323210115983.png) 上面的这段代码是建立中断向量表,中断向量表定位在代码段的最前面。具体的物理地址由链接器的配置参数(IROM1 的地址)决定。如果程序在 Flash 运行,则中断向量表的起始地址是 0x08000000。以 MDK 为例,就是如下配置选项: ![ ](https://img-blog.csdnimg.cn/20200323203532358.png) DCD 表示分配 1 个 4 字节的空间。每行 DCD 都会生成一个 4 字节的二进制代码。中断向量表 存放的实际上是中断服务程序的入口地址。当异常(也即是中断事件)发生时,CPU 的中断系统会将相应的入口地址赋值给 PC 程序计数器,之后就开始执行中断服务程序。 ### 第 5 部分代码分析 ![ ](https://img-blog.csdnimg.cn/20200323203655398.png) **第 53 行**:AREA 定义一块代码段,只读,段名字是 .text 。READONLY 表示只读。 **第 56 行**:利用 PROC、ENDP 这一对伪指令把程序段分为若干个过程,使程序的结构加清晰。 **第 57 行**:WEAK 声明其他的同名标号优先于该标号被引用,就是说如果外面声明了的话会调用外面的。 这个声明很重要,它让我们可以在 C 文件中任意地方放置中断服务程序,只要保证 C 函数的名字和向量表中的名字一致即可。 **第 58 行**:IMPORT:伪指令用于通知编译器要使用的标号在其他的源文件中定义。但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。 **第 61 行**:SystemInit()是一个标准的库函数,在 system_stm32f4xx.c这个库文件总定义。主要作用是配置系统时钟,这里调用这个函数之后,F429的系统时钟配被配置为 180M。 **第 63 行**:__main 标号表示 C/C++标准实时库函数里的一个初始化子程序__main 的入口地址。该程序的一个主要作用是初始化堆栈,并初始化映像文件,最后跳转到 C 程序中的 main 函数。这就解释了为何所有的 C 程序必须有一个 main 函数作为程序的起点。因为这是由 C/C++标准实时库所规,并且不能更改。如果我们在这里不调用__main,那么程序最终就不会调用我们 C文件里面的 main,如果是调皮的用户就可以修改主函数的名称,然后在这里面 IMPORT 你写的主函数名称即可。这个时候你在 C文件里面写的主函数名称就不是 main 了,而是 __main 了。 LDR、BLX、BX 是 CM4内核的指令: ![内核的指令](https://img-blog.csdnimg.cn/20200323210702699.png) ### 第 6 部分代码分析 ![ ](https://img-blog.csdnimg.cn/2020032320414956.png) **第 71 行**:死循环,用户可以在此实现自己的中断服务程序。不过很少在这里实现中断服务程序,一般多是在其它的 C 文件里面重新写一个同样名字的中断服务程序,因为这里是 WEEK 弱定义的。如果没有在其它文件中写中断服务器程序,且使能了此中断,进入到这里后,会让程序卡在这个地方。 **第 81 行**:缺省中断服务程序(开始) 第 92 行:死循环,如果用户使能中断服务程序,而没有在 C 文件里面写中断服务程序的话,都会进入到这里。比如在程序里面使能了串口 1 中断,而没有写中断服务程序 ART1_IRQHandle,那么串口中断来了,会进入到这个死循环。 **第 94 行**:缺省中断服务程序(结束)。 ### 第 7 部分代码分析 启动代码的最后一部分:![ ](https://img-blog.csdnimg.cn/2020032320441862.png) **第 101 行**:简单的汇编语言实现 IF…….ELSE…………语句。如果定义了 MICROLIB,那么程序是不会执行 ELSE分支的代码。__MICROLIB 可能大家并不陌生,就在 MDK 的 Target Option 里面设置。 ![ ](https://img-blog.csdnimg.cn/20200323204606339.png) 局外话,这一步的配置工作很重要,很多人串口用不了 printf 函数,编译有问题,下载有问题,都是这个步骤的配置出了错。Target中选中微库“ Use MicroLib”,为的是在日后编写串口驱动的时候可以使用printf 函数。而且有些应用中如果用了 STM32 的浮点运算单元 FPU,一定要同时开微库,不然有时会出现各种奇怪的现象。FPU 的开关选项在微库配置选项下方的“Use Single Precision”中,默认是开的。 ![ ](https://cdn.jsdelivr.net/gh/xiaoshidi-hub/PicGO/picture/chatb.png)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
果果小师弟
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交