【正点原子STM32连载】 第九章 STM32启动过程分析 摘自【正点原子】STM32F103 战舰开发指南V1.2

news2024/11/16 17:39:42

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html

第九章 STM32启动过程分析

本章给大家分析STM32F1的启动过程,这里的启动过程是指从STM32芯片上电复位执行的第一条指令开始,到执行用户编写的main函数这之间的过程。我们编写程序,基本都是用C语言编写,并且以main函数作为程序的入口。但是事实上,main函数并非最先执行的,在此之前需要做一些准备工作,准备工作通过启动文件的程序来完成。理解STM32启动过程,对今后的学习和分析STM32程序有很大的帮助。
注意:学习本章内容之前,请大家最好先阅读由正点原子团队编写的《STM32 启动文件浅析》和《STM32 MAP文件浅析》这两份文档(路径:A盘 1,入门资料)。
本章将分为如下几个小节:
9.1 启动模式
9.2 启动文件分析
9.3 map文件分析

9.1 启动模式

我们知道的复位方式有三种:上电复位,硬件复位和软件复位。当产生复位,并且离开复位状态后,CM3内核做的第一件事就是读取下列两个32位整数的值:
(1)从地址0x0000 0000处取出堆栈指针MSP的初始值,该值就是栈顶地址。
(2)从地址0x0000 0004处取出程序计数器指针PC 的初始值,该值指向复位后执行的第一条指令。下面用示意图表示,如图9.1.1所示。
在这里插入图片描述

图9.1.1 复位序列
上述过程中,内核是从0x0000 0000和0x0000 0004两个的地址获取堆栈指针SP和程序计数器指针PC。事实上,0x0000 0000和0x0000 0004两个的地址可以被重映射到其他的地址空间。例如:我们将0x0800 0000映射到0x0000 0000,即从内部FLASH启动,那么内核会从地址0x0800 0000处取出堆栈指针MSP 的初始值,从地址0x0800 0004处取出程序计数器指针PC的初始值。CPU会从PC寄存器指向的地址空间取出的第1条指令开始执行程序,就是开始执行复位中断服务程序Reset_Handler。将0x0000 0000和0x0000 0004两个地址重映射到其他的地址空间,就是启动模式选择。
对于STM32F1的启动模式(也称自举模式),我们看表9.1.1进行分析。
在这里插入图片描述

表9.1.1 启动模式选择表
注:启动引脚的电平:0:低电平;1:高电平;x:任意电平,即高低电平均可
由表9.1.1可以看到,STM32F1根据BOOT引脚的电平选择启动模式,这两个BOOT引脚根据外部施加的电平来决定芯片的启动地址。(0和1的准确电平范围可以查看F103系列数据手册I/O特性表,但我们最好是设置成Gnd和VDD的电平值)
(1)内部FLASH启动方式
当芯片上电后采样到BOOT0引脚为低电平时,0x00000000和0x00000004地址被映射到内部FLASH的首地址0x08000000和0x08000004。因此,内核离开复位状态后,读取内部FLASH的0x08000000地址空间存储的内容,赋值给栈指针MSP,作为栈顶地址,再读取内部FLASH的0x08000004地址空间存储的内容,赋值给程序指针PC,作为将要执行的第一条指令所在的地址。完成这两个操作后,内核就可以开始从PC指向的地址中读取指令执行了。
(2)内部SRAM启动方式
类似于内部Flash,当芯片上电后采样到BOOT0和BOOT1引脚均为高电平时,地址0x00000000和0x00000004被映射到内部SRAM的首地址0x20000000和0x20000004,内核从SRAM空间获取内容进行自举。在实际应用中,由启动文件starttup_stm32f103xe.s决定了0x00000000和0x00000004地址存储什么内容,链接时,由分散加载文件(sct)决定这些内容的绝对地址,即分配到内部FLASH还是内部SRAM。
(3)系统存储器启动方式
当芯片上电后采样到BOOT0 =1,BOOT1=0的组合时,内核将从系统存储器的0x1FFFF000及0x1FFFF004获取MSP及PC值进行自举。系统存储器是一段特殊的空间,用户不能访问,ST公司在芯片出厂前就在系统存储器中固化了一段代码。因而使用系统存储器启动方式时,内核会执行该代码,该代码运行时,会为ISP(In System Program)提供支持,在STM32F1上最常见的是检测USART1传输过来的信息,并根据这些信息更新自己内部FLASH的内容,达到升级产品应用程序的目的,因此这种启动方式也称为ISP启动方式。
9.2 启动文件分析
STM32启动文件由ST官方提供,在官方的STM32Cube固件包里,对于STM32F103系列芯片的启动文件,我们选用的是startup_STM32F103xe.s这个文件。启动文件用汇编编写,是系统上电复位后第一个执行的程序。
启动文件主要做了以下工作:
1、初始化堆栈指针SP = _initial_sp
2、初始化程序计数器指针PC = Reset_Handler
3、设置堆和栈的大小
4、初始化中断向量表
5、配置外部SRAM作为数据存储器(可选)
6、配置系统时钟,通过调用SystemInit函数(可选)
7、调用 C库中的_main函数初始化用户堆栈,最终调用main函数
9.2.1 启动文件中的一些指令
在这里插入图片描述

表9.2.1.1 启动文件的汇编指令
上表,列举了STM32启动文件的一些汇编和编译器指令,关于其他更多的ARM汇编指令,我们可以通过MDK的索引搜索工具中搜索找到。打开索引搜索工具的方法:MDK->Help->uVision Help,如图9.2.1.1所示。
在这里插入图片描述

图9.2.1.1打开索引搜索工具的方法
打开之后,我们以EQU为例,演示一下怎么使用,如图9.2.1.2所示。
在这里插入图片描述

图9.2.1.2 搜索EQU汇编指令
搜索到的结果有很多,我们只需要看位置为Assembler User Guide这部分即可。
9.2.2 启动文件代码讲解
(1)栈空间的开辟
栈空间的开辟,源码如图9.2.2.1所示:
在这里插入图片描述

图9.2.2.1 栈空间的开辟
源码含义:开辟一段大小为0x0000 0400(1KB)的栈空间,段名为STACK,NOINIT 表示不初始化; READWRITE 表示可读可写;ALIGN=3,表示按照 2^3对齐,即8字节对齐。
AREA汇编一个新的代码段或者数据段。
SPACE分配内存指令,分配大小为Stack_Size字节连续的存储单元给栈空间。
__initial_sp紧挨着SPACE放置,表示栈的结束地址,栈是从高往低生长,所以结束地址就是栈顶地址。
栈主要用于存放局部变量,函数形参等,属于编译器自动分配和释放的内存,栈的大小不能超过内部SRAM 的大小。如果工程的程序量比较大,定义的局部变量比较多,那么就需要在启动代码中修改栈的大小,即修改Stack_Size的值。如果程序出现了莫名其妙的错误,并进入了HardFault的时候,你就要考虑下是不是栈空间不够大,溢出了的问题。
(2)堆空间的开辟
堆空间的开辟,源码如图9.2.2.2所示:
在这里插入图片描述

图9.2.2.2堆空间的开辟
源码含义:开辟一段大小为0x0000 0200(512字节)的堆空间,段名为HEAP,不初始化,可读可写,8字节对齐。
__heap_base表示堆的起始地址,__heap_limit表示堆的结束地址。堆和栈的生长方向相反的,堆是由低向高生长,而栈是从高往低生长。
堆主要用于动态内存的分配,像malloc()、calloc()和realloc()等函数申请的内存就在堆上面。堆中的内存一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。
接下来是PRESERVE8和THUMB指令两行代码。如图9.2.2.3所示。
在这里插入图片描述

图9.2.2.3 PRESERVE8和THUMB指令
PRESERVE8:指示编译器按照8字节对齐。
THUMB:指示编译器之后的指令为THUMB指令。
注意:由于正点原子提供了独立的内存管理实现方式(mymalloc,myfree等),并不需要使用C库的malloc和free等函数,也就用不到堆空间,因此我们可以设置Heap_Size的大小为0,以节省内存空间。
(3)中断向量表定义(简称:向量表)
为中断向量表定义一个数据段,如图9.2.2.4所示:
在这里插入图片描述

图9.2.2.4 为中断向量表定义一个数据段
源码含义:定义一个数据段,名字为RESET, READONLY表示只读。EXPORT表示声明一个标号具有全局属性,可被外部的文件使用。这里是声明了__Vectors、__Vectors_End和__Vectors_Size三个标号具有全局性,可被外部的文件使用。
STM32F103的中断向量表定义代码,如图9.2.2.5所示。
在这里插入图片描述

图9.2.2.5中断向量表定义代码
__Vectors 为向量表起始地址,__Vectors_End为向量表结束地址,__Vectors_Size为向量表大小,__Vectors_Size = __Vectors_End - __Vectors。
DCD:分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。
中断向量表被放置在代码段的最前面。例如:当我们的程序在FLASH运行时,那么向量表的起始地址是:0x0800 0000。结合图9.2.2.5可以知道,地址0x0800 0000存放的是栈顶地址。DCD以四字节对齐分配内存,也就是下个地址是0x0800 0004,存放的是Reset_Handler中断函数入口地址。
从代码上看,向量表中存放的都是中断服务函数的函数名,所以C语言中的函数名对芯片来说实际上就是一个地址。
STM32F103的中断向量表可以在《STM32F10xxx参考手册_V10(中文版).pdf》的第9章的9.1.2小节找到,与中断向量表定义代码是对应的。
(4)复位程序
接下来是定义只读代码段,如图9.2.2.6所示:
在这里插入图片描述

图9.2.2.6 定义只读代码段
定义一个段命为.text,只读的代码段,在CODE区。
复位子程序代码,如图9.2.2.7所示:
在这里插入图片描述

图9.2.2.7 复位子程序代码
利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰。
复位子程序是复位后第一个被执行的程序,主要是调用SystemInit函数配置系统时钟、还有就是初始化FSMC总线上外挂的SRAM(可选)。然后在调用C库函数__main,最终调用main函数去到C的世界。
EXPORT声明复位中断向量Reset_Handler为全局属性,这样外部文件就可以调用此复位中断服务。
WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用外部定义的标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。
IMPORT表示该标号来自外部文件。这里表示SystemInit和__main这两个函数均来自外部的文件。
LDR、BLX、BX是内核指令,可在《Cortex-M3权威指南》第四章-指令集里面查询到。
LDR表示从存储器中加载字到一个存储器中。
BLX表示跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR。
BX表示跳转到由寄存器/标号给出的地址,不用返回。这里表示切换到__main地址,最终调用main函数,不返回,进入C的世界。
(5)中断服务程序
在这里插入图片描述

图9.2.2.8 中断服务程序
接下来就是中断服务程序了,如图9.2.2.8所示。
可以看到这些中断服务函数都被[WEAK]声明为弱定义函数,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不会出错。
这些中断函数分为系统异常中断和外部中断,外部中断根据不同芯片有所变化。B指令是跳转到一个标号,这里跳转到一个‘.’,表示无限循环。
在启动文件代码中,已经把我们所有中断的中断服务函数写好了,但都是声明为弱定义,所以真正的中断服务函数需要我们在外部实现。
如果我们开启了某个中断,但是忘记写对应的中断服务程序函数又或者把中断服务函数名写错,那么中断发生时,程序就会跳转到启动文件预先写好的弱定义的中断服务程序中,并且在B指令作用下跳转到一个‘.’中,无限循环。
这里的系统异常中断部分是内核的,外部中断部分是外设的。
(6)用户堆栈初始化
ALIGN指令,如图9.2.2.9所示:
在这里插入图片描述

图9.2.2.9 ALIGN指令
ALIGN表示对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示4字节对齐。要注意的是,这个不是ARM的指令,是编译器的。
接下就是启动文件最后一部分代码,用户堆栈初始化代码,如图9.2.2.10所示:
在这里插入图片描述

图9.2.2.10 用户堆栈初始化代码
IF, ELSE, ENDIF是汇编的条件分支语句。
588行判断是否定义了__MICROLIB。关于__MICROLIB这个宏定义,我们是在KEIL里面配置,具体方法如图9.2.2.11所示。
在这里插入图片描述

图9.2.2.11 __MICROLIB定义方法
勾选了Use MicroLIB就代表定义了__MICROLIB这个宏。
如果定义__MICROLIB,声明__initial_sp、__heap_base和__heap_limit这三个标号具有全局属性,可被外部的文件使用。__initial_sp表示栈顶地址,__heap_base表示堆起始地址,__heap_limit表示堆结束地址。
如果没有定义__MICROLIB,实际的情况就是我们没有定义__MICROLIB,所以使用默认的C库运行。那么堆栈的初始化由C库函数__main来完成。
IMPORT声明__use_two_region_memory标号来自外部文件。
EXPORT声明__user_initial_stackheap具有全局属性,可被外部的文件使用。
340行标号__user_initial_stackheap,表示用户堆栈初始化程序入口。
接下来进行堆栈空间初始化,堆是从低到高生长,栈是从高到低生长,是两个互相独立的数据段,并且不能交叉使用。
344行保存堆起始地址。345行保存栈大小。346行保存堆大小。347行保存栈顶指针。348行跳转到LR标号给出的地址,不用返回。354行END表示到达文件的末尾,文件结束。
Use MicroLIB
MicroLIB是MDK自带的微库,是缺省C库的备选库,MicroLIB进行了高度优化使得其代码变得很小,功能比缺省C库少。MicroLIB是没有源码的,只有库。
关于MicroLIB更多知识可以看官方介绍http://www.keil.com/arm/microlib.asp 。
9.2.3 系统启动流程
我们知道启动模式不同,启动的起始地址是不一样的,下面我们以代码下载到内部FLASH的情况举例,即代码从地址0x0800 0000开始被执行。
当产生复位,并且离开复位状态后,CM3内核做的第一件事就是读取下列两个32位整数的值:
(1)从地址 0x0800 0000 处取出堆栈指针MSP 的初始值,该值就是栈顶地址。
(2)从地址 0x0800 0004 处取出程序计数器指针PC 的初始值,该值指向中断服务程序 Reset_Handler。下面用示意图表示,如图9.2.3.1所示。
在这里插入图片描述

图9.2.3.1 复位序列
我们看看STM32F103开发板HAL库例程的实验1跑马灯实验中,取出的MSP和PC的值是多少,方法如图9.2.3.2所示。

图9.2.3.2 取出的MPS和PC的值
由图9.2.3.2可以知道地址0x0800 0000的值是0x2000 0788,地址0x0800 0004的值是0x0800 01CD,即堆栈指针SP =0x2000 0788,程序计数器指针PC = 0x0800 01CD(即复位中断服务程序Reset_Handler的入口地址)。因为CM3内核是小端模式,所以倒着读。
请注意,这与传统的ARM架构不同——其实也和绝大多数的其它单片机不同。传统的ARM架构总是从0地址开始执行第一条指令。它们的0地址处总是一条跳转指令。而在CM3内核中,0地址处提供MSP的初始值,然后就是向量表(向量表在以后还可以被移至其它位置)。向量表中的数值是32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令,就是Reset_Handler这个函数。下面继续以MINI开发板HAL库例程实验1跑马灯实验为例,代码从地址0x0800 0000开始被执行,讲解一下系统启动,初始化堆栈、MSP和PC后的内存情况。
在这里插入图片描述

图9.2.3.3 初始化堆栈、MSP和PC后的内存情况
因为CM3使用的是向下生长的满栈,所以MSP的初始值必须是堆栈内存的末地址加1。
举例来说,如果你的栈区域在0x2000 0388‐0x2000 0787(1KB大小)之间,那么MSP的初始值就必须是0x2000 0788。
向量表跟随在MSP的初始值之后——也就是第2个表目。
R15是程序计数器,在汇编代码中,可以使用名字“PC”来访问它。ARM规定:PC最低两位并不表示真实地址,最低位LSB用于表示是ARM指令(0)还是Thumb指令(1),因为 CM3 主要执行 Thumb指令,所以这些指令的最低位都是1(都是奇数)。因为 CM3 内部使用了指令流水线,读 PC 时返回的值是当前指令的地址+4。比如说:
0x1000: MOV R0, PC ; R0 = 0x1004
如果向PC写数据,就会引起一次程序的分支(但是不更新LR寄存器)。CM3中的指令至少是半字对齐的,所以PC的LSB总是读回 0。然而,在分支时,无论是直接写PC的值还是使用分支指令,都必须保证加载到PC的数值是奇数(即LSB=1),表明是在Thumb状态下执行。倘若写了0,则视为转入ARM模式,CM3将产生一个fault异常。
正因为上述原因,图9.2.3.3中使用0x0800 01CD来表达地址0x0800 01CC。当0x0800 01CD 处的指令得到执行后,就正式开始了程序的执行(即去到C的世界)。所以在此之前初始化 MSP 是必需的,因为可能第1条指令还没执行就会被 NMI 或是其它 fault 打断。MSP 初始化好后就已经为它们的服务例程准备好了堆栈。
STM32启动文件分析就给大家介绍到这里,更多内容请看 《STM32启动文件浅析》 。
9.3 map文件分析
9.3.1 MDK编译生成文件简介
MDK编译工程,会生成一些中间文件(如.o、.axf、.map 等),最终生成hex文件,以便下载到MCU上面执行,以STM32F103开发板HAL库例程的实验1跑马灯实验为例(其他开发板类似),编译过程产生的所有文件,都存放在 Output文件夹下,如图9.3.1.1所示:
在这里插入图片描述

图9.3.1.1 MDK编译过程生成的文件
这里总共生成了43个文件,共11个类型,分别是:.axf、.crf、.d、.dep、.hex、.lnp、.lst、.o、.htm、bulild_log.htm 和.map。43个文件(勾选Browse informatio-n时为59个)看着不是很多,但是随着工程的增大,这些文件也会越来越多,大项目编译一次,可以生成几百甚至上千个这种文件,不过文件类型基本就是上面这些。
对于MDK工程来说,基本上任何工程在编译过程中都会有这11类文件,常见的MDK编译过程生产文件类型如表 9.3.1.1所示:
文件类型 说明
.o 可重定向1对象文件,每个源文件(.c/.s 等)编译都会生成一个.o 文件
.axf 由ARMCC 编译生产的可执行对象文件,不可重定向2(绝对地址)
多个.o 文件链接生成.axf 文件,我们在仿真的时候,需要用到该文件
.hex Intel Hex格式文件,可用于下载到 MCU,.hex 文件由.axf文件转换而来
.crf 交叉引用文件,包含浏览信息(定义、标识符、引用)
.d 由ARMCC/GCC编译生产的依赖文件(.o 文件所对应的依赖文件)
每个.o 文件,都有一个对应的.d 文件
.dep 整个工程的依赖文件
.lnp MDK生成的链接输入文件,用于命令输入
.lst C语言或汇编编译器生成的列表文件
.htm 链接生成的列表文件
.build_log.htm 最近一次编译工程时的日志记录文件
.map 连接器生成的列表文件/MAP 文件, 该文件对我们非常有用
表 9.3.1.1 常见的中间文件类型说明
注 1,可重定向是指该文件包含数据/代码,但是并没有指定地址,它的地址可由后续链接的时候进行指定。
注 2,不可重定向是指该文件所包含的数据/代码都已经指定地址了,不能再改变。
9.3.2 map文件分析
.map文件是编译器链接时生成的一个文件,它主要包含了交叉链接信息。通过.map文件,我们可以知道整个工程的函数调用关系、FLASH和RAM占用情况及其详细汇总信息,能具体到单个源文件(.c/.s)的占用情况,根据这些信息,我们可以对代码进行优化。.map 文件可以分为以下5个组成部分:
1, 程序段交叉引用关系(Section Cross References)
2, 删除映像未使用的程序段(Removing Unused input sections from the image)
3, 映像符号表(Image Symbol Table)
4, 映像内存分布图(Memory Map of the image)
5, 映像组件大小(Image component sizes)
9.3.2.1 map 文件的 MDK 设置
要生成map文件,我们需要在MDK的魔术棒→Listing选项卡里面,进行相关设置,如图9.3.2.1.1所示:
在这里插入图片描述

图 9.3.2.1.1 .map文件生成设置
图9.3.2.1.1中红框框出的部分就是我们需要设置的,默认情况下,MDK 这部分设置就是全勾选的,如果我们想取消掉一些信息的输出,则取消相关勾选即可(一般不建议)。
如图9.3.2.1.1设置好MDK以后,我全编译当前工程,当编译完成后(无错误),就会生成.map文件。在MDK里面打开.map 文件的方法如图9.3.2.1.2所示:
在这里插入图片描述

图9.3.2.1.2 打开.map 文件
1,先确保工程编译成功(无错误)。
2,双击 LED,打开.map 文件。
3,map 文件打开成功。
9.3.2.2 map 文件的基础概念
为了更好的分析 map 文件,我们先对需要用到的一些基础概念进行一个简单介绍,相关概念如下:
Section:描述映像文件的代码或数据块,我们简称程序段
RO:Read Only 的缩写,包括只读数据(RO data)和代码(RO code)两部分内容,占用 FLASH 空间
RW:Read Write 的缩写,包含可读写数据(RW data,有初值,且不为 0),占用 FLASH(存储初值)和 RAM(读写操作)
ZI:Zero initialized 的缩写,包含初始化为 0 的数据(ZI data),占用 RAM 空间。
.text:相当于 RO code
.constdata:相当于 RO data
.bss:相当于 ZI data
.data:相当于 RW data
9.3.2.3 map文件的组成部分说明
我们前面说map文件分为5个部分组成,下面以STM32F103开发板HAL库例程的实验1跑马灯实验为例,简要讲解一下。
1.程序段交叉引用关系(S S ection Cross References s )
这部分内容描述了各个文件(.c/.s等)之间函数(程序段)的调用关系,举个例子如图9.3.2.3.1所示:
在这里插入图片描述

图9.3.2.3.1 程序段交叉引用关系图
上图中,框出部分:main.o(i.main) refers to sys.o(i.sys_stm32_clock_init) for sys_stm32_
clock_init表示:main.c文件中的main函数,调用了sys.c中的sys_stm32_clock_init 函数。其中:i.main表示 main 函数的入口地址,同理i.sys_stm32_clock_init 表示 sys_stm32_clock_init函数的入口地址。
2. 删除映像未使用的程序段(Removing Unused input sections from the image)
这部分内容描述了工程中由于未被调用而被删除的冗余程序段(函数/数据),如图
9.3.2.3.2所示:

在这里插入图片描述

图9.3.2.3.2 删除未用到的程序段
上图中,列出了所有被移除的程序段,比如usart.c里面的usart_init函数就被移除了,因为该例程没用到usart_init函数。
另外,在最后还有一个统计信息:216 unused section(s) (total 15556bytes) removed from the image.表示总共移除了216个程序段(函数/数据),大小为15556字节。即给我们的 MCU 节省了 15556字节的程序空间。
为了更好的节省空间,我们一般在 MDK→魔术棒→C/C++选项卡里面勾选:One ELF
Section per Function,如图9.3.2.3.3所示:
在这里插入图片描述

图9.3.2.3.3 MDK勾选One ELF Section per Function
3. 映像符号表(Image Symbol Table)
映像符号表(Image Symbol Table)描述了被引用的各个符号(程序段/数据)在存储器中的存储地址、类型、大小等信息。映像符号表分为两类:本地符号(Local Symbols)和全局符号(Global Symbols)。
本地符号(Local Symbols)记录了用 static 声明的全局变量地址和大小,c文件中函数的地址和用static声明的函数代码大小,汇编文件中的标号地址(作用域:限本文件)。
全局符号(Global Symbols)记录了全局变量的地址和大小,C文件中函数的地址及其代码大小,汇编文件中的标号地址(作用域:全工程)。
4. 映像内存分布图(Memory Map of the image)
映像文件分为加载域(Load Region)和运行域(Execution Region)。一个加载域必须有至少一个运行域(可以有多个运行域),而一个程序又可以有多个加载域。加载域为映像程序的实际存储区域,而运行域则是MCU上电后的运行状态。加载域和运行域的简化关系(这里仅表示一个加载域的情况)图,如图9.3.2.3.4所示:
在这里插入图片描述

图9.3.2.3.4 加载域运行域关系
由图可知,RW区也是存放在 ROM(FLASH)里面的,在执行main函数之前,RW(有初值且不为0的变量)数据会被拷贝到RAM区,同时还会在RAM里面创建ZI区(初始化为0的变量)。
5. 映像组件大小(Image component sizes)
映像组件大小(Image component sizes)给出了整个映像所有代码(.o)占用空间的汇总信息。这部分是程序实际功能可执行代码的存储空间。
由于篇幅较长,更多内容请大家查阅《STM32 MAP文件浅析》文档的内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis可持久化详解1

目录 Redis可持久化 以下是RDB持久化的代码示例: 面试常问 1什么是Redis的持久化机制? 2Redis支持哪些持久化机制?它们有什么区别? 3Redis的RDB持久化机制的原理是什么? 4Redis的AOF持久化机制的原理是什么&…

《三》包管理工具 npm

包管理工具 npm: npm:Node Package Manager,Node 包管理器,目前已经不仅仅作为 Node 的包管理工具,也作为前端的包管理工具来管理包。 npm 管理的包是存放在一个名为 registry 的仓库中的,发布一个包时是…

分享推荐32位MCU低成本替换8/16位升级完美之选——MM32G0001

灵动微在嵌入式闪存技术上做了优化,采用更稳定和经大规模量产验证的12寸晶圆工艺平台,对产品的功能、性能和成本进行了全方位的打磨,在保持MM32品质目标的前提下,推出了这款极具性价比、低成本的MM32G0001系列MCU产品。 不同于市…

Nuxt学习笔记

创建项目 npx create-nuxt-app projectName SSR 渲染流程 客户端发送 URL 请求到服务端,服务端读取对应的 URL 的模板信息,在服务端做出 HTML 和数据的渲染,渲染完成之后返回整个 HTML 结构给客户端。所以用户在浏览首屏的时候速度会比较快…

Scala学习(四)

文章目录 1.闭包2.函数式编程递归和尾递归2.1递归2.2 尾递归 3.控制抽象3.1 值调用3.2 名调用 4.惰性函数 1.闭包 如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和它所处的环境称之为闭包 //闭包练习def sumX(x:Int){def sumY(y:Int):Int{…

闲谈【Stable-Diffusion WEBUI】的插件:模型工具箱:省空间利器

文章目录 (零)前言(一)模型工具箱(Model Toolbox)(1.1)基本使用界面(1.2)高阶使用界面(1.3)自动修剪模型 (零)…

MyBatis基础介绍

目录 MyBatis是什么 怎么学MyBatis 第一个MyBatis查询 MyBatis的定位 创建数据库和表 搭建MyBatis环境 添加MyBatis框架支持 设置MyBatis的配置信息 设置数据库连接的相关信息 配置MyBatis xml的保存路径和xml命名规范 MyBatis模式开发 创建一个实体类 创建MyBatis…

AI换脸系统开发源码交付

AI换脸系统软件的发展趋势包括以下几个方面: 定制化和智能化:随着用户需求的不断增加,AI换脸系统将向更加定制化和智能化的方向发展,通过数据分析和用户画像等手段,为用户提供更加个性化的服务。 多模态应用&a…

通达信头肩底形态选股公式,突破波峰发出信号

本文将为大家介绍头肩底形态选股公式的编写方法,相较于前两篇文章介绍的N字形态和W底形态,头肩底形态更为复杂,包含3个波谷和2个波峰。 头肩底是一种反转形态,在下降趋势之后形成,其完成标志着趋势的改变。该形态包含三…

谷歌浏览器 | Chrome DevTools系统学习篇-Device Mode

大家好,文接上回谷歌浏览器 | Chrome DevTools系统学习篇-概述。所谓“工欲善其事,必先利其器”,我们进一步来熟悉谷歌开发者工具。今天分享的是Device Mode,使用设备模式来估算您的页面在移动设备上的外观和性能。 设备模式是 Ch…

java顺序表——ArrayList详解

1.顺序表的概念 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 2.自己实现一个顺序表——MyArrayList 2.1 顺序表成员变量的定义 public class MyArrayList {public static int FEFAU…

优思学院|什么是精益生产?企业如何实现精益生产?

简介 在现代工业社会中,企业的生产效率和质量管理是其生存和发展的关键因素之一。而精益生产作为一种高效的生产管理模式,已经成为了众多企业提升效率和质量的首选。优思学院[1]在本文将对精益生产进行详细的介绍,并提供企业实现精益生产的实…

【Java零基础入门篇】第 ④ 期 - 继承(二)

博主:命运之光 专栏:JAVA入门 学习目标 1.掌握继承性的主要作用、实现、使用限制; 2.掌握this和super的含义及其用法; 3.掌握方法覆写的操作; 4.掌握final关键字的使用; 5.掌握类变量、实例变量和局部变量的…

【应用场景详解】Web自动化测试适用于哪些场景?看完这篇文章你就知道了

【从入门到实战】WEB自动化测试基础教程,手把手教你封装自己的测试框架! 目录 前言: 一、什么是web自动化测试? 二、Web自动化测试的应用场景 三、Web自动化测试的实现方法 1.安装Selenium 2.编写测试用例 四、Web自动自动…

第十二章 使用DHCP动态管理主机地址

文章目录 第十二章 使用DHCP动态管理主机地址一、动态主机地址管理协议1、DHCP简介2、DHCP常见术语 二、部署DHCP服务程序1、安装DHCP服务程序2、配置文件参考模板3、dhcpd服务程序配置文件中常见参数及作用 三、自动管理IP地址1、机房所用的网络地址以及参数信息2、关闭虚拟网…

原装二手Anritsu S331E安立S331L 手持式电缆和天线分析仪

Anritsu S331E Site Master 手持式电缆和天线分析仪涵盖 2 MHz 至 4 GHz 频谱,是无线基站电缆和天线系统安装、配置、维护和故障排除的行业标准。Site Master 准确、多功能、价格合理、坚固耐用,是真正的手持设备,重量不到 5 磅(包…

跨越式升级,敏感数据动态脱敏系统全新而来!

“ 2020年 某医院疫情名单遭工作人员外泄至微信群,涉及6000余人个人身份信息,三人被拘 2021年 某银行未经客户本人授权查询并向第三方提供其个人银行帐户交易信息,被罚450万元 2022年 某银行未落实个人银行账户实名制管理规定&#xff…

民宿企业数字化最佳实践 :我终于跟线下表格和解了

“不由感叹,技术就是生产力啊!” 这是偶来民宿店长在使用了腾讯轻联后不由得发出的感慨。 偶来民宿是一家管理超过100套城市民宿和公寓房源的民宿运营公司,以经营特色化、年轻化、个性化民宿、公寓为主营业务。IF HOUSE为该公司旗下高端民宿…

github copilot如何帮助写代码

Github Copilot是一个基于人工智能的代码助手,可以帮助程序员在编写代码时提供自动补全和建议功能。使用Github Copilot需要以下步骤: 1.注册Github账号并安装Github Copilot插件 首先你需要注册一个Github账号,然后在你使用的代码编辑器中安…

基于netty框架不使用SSL证书,实现websocket数据加密传输

文章目录 简介实现方式主要代码调用方法 1、简介 2、实现方式 3、服务端主要代码 4、客户端主要代码 5、调用方式 简介 为什么不使用SSL证书? 1、服务器运行在专网环境,不能访问互联网。证书有有效期,CA机构规定,证书有效期…