MCU(Cortex - M3/M4)启动加载过程和内存分配原理 笔记

news/2024/11/24 1:31:10/

        最近发现对基础不太熟悉,写篇笔记记录一下MCU启动到用户C语言运行,之前做了那些工作,同时flash和Ram又分别保存了那个数据,每一段又是什么意义,方便后续自己忘记了,查阅。

一、 MCU启动

       在MCU上电/复位之后到程序开始运行前,Cortex - M处理器会从存储器中读取出两个字(注意在32位系统中一个字位32bit),即读取地址0x000000000x00000004的数据,这两个地址存放的值分别为MSP(Main stack pointer)主栈指针的初始值,以及代表复位处理处理起始地址的复位向量。处理器读出这两个字用于初始化MSP主栈指针,和PC(Program Counter)程序计数器。

Cortex - M3和M4的寄存器组如下图所示。(图片参考了《ARM Cortex - M3和Cortex - M4权威指南》第三版

 

需要注意的是,主流MCU的启动方式一般有3种,GD32F4为例,通过配置外部引进BOOT0、BOOT1的电平状态选择启引导方式。区别就是将0x00000000映射到不同的地址,如果选择FLASH启动处理器对0x00000000的读取其实就是对0x08000000地址的读取。

下图为GD32F4xx的启动引导配置说明(截图来源《GD32F4xx_User_Manual_Rev2.8_CN》

 二、启动文件 -> 用户主函数执行

从上电开始,这期间MCU在跑的程序,其实是启动文件.s编写的程序内容。在MCU工程中,会有一个.s的启动文件,一般这个启动文件由器件厂商提供。

启动文件的功能主要为:

1–初始化栈指针MSP=_initial_sp。

简单来讲就是在程序第一个字放置了堆栈指针MSP的值。

2–初始化复位程序计数寄存器值=Reset_Handler(弱函数)。

简单来讲就是在程序第二个字放置了Reset_Handler复位中断的函数入口地址

3–初始化异常/ 中断向量表。

简单来讲就是在第3字后,放各个系统各种类型中断的函数入口地址(弱函数,如果用户未定义默认指向Default_Handler ),这些各种中断的入口地址,我们一般称之为中断向量表。

4–系统时钟配置。

这个是在Reset_Handler函数里面,里面会调用一个函数,有些启动文件没有默认定义这个函数,导致有些工程的SystemInit函数必须由用户定义。

5– C库函数_main初始化用户堆栈的调用。

_main是进入main函数之前的初始化函数,可以理解为,进入main之前先调用了__main()函数去初始化一些内容,然后再到我们可以在C环境中编写的程序。

 具体启动文件内容详解,可以去看其他博主的介绍,这里列出链接:

STM32启动文件讲解_XYJ_Tiger的博客-CSDN博客

STM32F10x启动文件详解_stm32f10x_md_超级网吧的博客-CSDN博客

 3-STM32启动文件详解_南山府嵌入式的博客-CSDN博客

需要注意的是上面介绍的是.s文件的功能,实际程序跑起来后运行顺序如下:

1.赋值MSP。

2.进入Reset_Handler去处理

        Reset_Handler中会进入SystemInit 和 __main函数,然后就进入C语言运行环境。

注意:如果Reset_Handler处理期间有中断的话,就会跳去中断向量表然后找到中断处理函数地址,再跳过去处理中断。这也是Cortex - M3/M4内核先赋值MSP的原因所在。

那问题来了,__main的函数源码在哪呢?参考其他博主的博客。

MDK __main()代码执行分析_keil _maim_TS_up的博客-CSDN博客

三、 MCU内部Flash、RAM中的数据

我们写个程序,用编译后最终会生成一个可以烧写入芯片flash的可执行文件,常见的两种,一种是.bin,一种是.hex文件。

STM32的内存又有了6个储存数据段3种储存属性区的概念

参考下文链接:

两种存储器,三种内存大小,六段段

MDK编译出来后,会分为4个中类型

Code、RO Data、RW Data、ZI Data

Code :代表执行的代码,程序中所有的函数,存在flash中;
RO-data :代表只读数据,程序中所定义的全局常量数据(局部常量放在栈区),存在flash中;
RW-data:代表已初始化的读写数据,程序中定义并且初始化的全局变量和静态变量,存在flash中,程序运行后(具体由__main来实现,会将在Flash区域中的RW-data拷贝到RAM中);
ZI-data:zero initial,就是程序中用到的变量并且被系统初始化为0的变量的字节数 (在ram中)
 

其实我主要关心的是,程序编译出来后,数据是怎么放置的,程序运行之后数据又是怎么放置的。

 如图,编译出来的数据会放到Flash中,运行的时候,由用户的配置,启动文件会设置堆栈指针,__main函数会将RW-data搬运到RAM中。Heap根据程序的运行申请的内存会增长,Stack向下增长,,当两者指针交叉时,系统出栈回的数据就是混乱的,就会死机。

事实上,在IAR或者MDK编译器中,可以特别声明某段代码,将他们整个拷贝到RAM中,在RAM里面运行,因为RAM的读写速度快很多,以此加快该断代码的运行速度。

 


http://www.ppmy.cn/news/534749.html

相关文章

Matplotlib---散点图

1. 散点图 scatter函数用于绘制散点图。下面是scatter函数的语法格式: scatter(x, y, sNone, cNone, markerNone, cmapNone, normNone, vminNone, vmaxNone, alphaNone, linewidthsNone, edgecolorsNone, **kwargs)参数解释: x:指定散点的…

软件项目管理 第三章软件项目的启动过程课后习题参考答案——主编:李冰、张桥珍、刘玉娥

第三章 软件项目的启动过程 课后习题参考答案 1.选择题 (1)乙方在项目初始阶段的主要任务不包含以下哪一项(D)。 A. 项目分析 B. 竞标 C. 合同签署 D. 合同管理 (2)项目章程中不…

Redis入门(2)-字符串

String是Redis最基础、最常见的类型,string类型的value中可存放任意数据,包括数值型、二进制的图片、音频、视频、序列化对象等。一个String类型的value最大是512M. 1.getset k v 若key存在返回之前的值,若不存在返回nil 2.strlen key 返…

Spark RDD | 常用函数讲解与代码实践

😄 因为spark里用的就是RDD数据结构来存储数据,所以对数据处理离不开RDD的各种函数操作咯!这一节就跟着梁云大佬打卡下如何处理RDD。【下面章节有🔥的是用的比较多的函数】 文章目录 0、初始化pyspark环境与driver介绍0.1、初始化0.2、driver是啥?1、创建RDD1.1、法1:通…

独家:海尔消金“重仓”医美,月放款量惊人

作者 | 周古 来源 | 镭射财经(leishecaijing) 边补充线下直营团队,边大规模扩张市场,海尔消费金融的医美分期业务正在组织一场大跃进。 继度小满退出医美分期直营市场后,海尔消费金融加快布局医美分期业务。海尔消费…

海尔微型计算机硬盘如何拆卸,海尔a62的详细拆机步骤【图文教程】

随着社会的不断发展,台式电脑已经无法满足市场的需求了,现在 笔记本电脑 非常地流行,它以轻薄的机身和过高的配置赢得了很多顾客的喜爱,所以市场上也出现了各种品牌的笔记本电脑。大家知道吧,任何东西都是优势互补的&a…

【数据挖掘工程师-笔试】2022年海尔 公司

公司:海尔 数据挖掘工程师 1 选择题(18个) 可以学习的题库:https://brainly.in/ 艾斯曼德的逻辑题,注意只有20分钟,没有时间提示,我都没有做完到时间直接结束了 (1)5…

青梅产业成立“国家队”,溜溜梅迎来树立品牌良机?

扩大内需在今年政府工作报告中被频频提及。国务院发展研究中心原副主任王一鸣认为,激活潜在消费需求,将释放中国超大规模市场的经济增长潜力。 如今,休闲零食市场也正在经历这样一场激活潜在需求的变革。无论是洽洽食品、三只松鼠、良品铺子…