热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于STM32启动文件的几个小问题

关注星标公众号,不错过精彩内容转自|茶话MCU基于STM32芯片的工程代码里有个很重要的文件,即启动文件。该文件主要由汇编语言写成,文件名

关注+星标公众,不错过精彩内容

55df4fe8c1293c8492bab4a418a042c6.gif

转自 | 茶话MCU

基于STM32芯片的工程代码里有个很重要的文件,即启动文件。该文件主要由汇编语言写成,文件名冠以.s结尾,它是芯片程序运行首先要执行的一个文件。其功能及作用简单点说就是做执行用户程序前的基本准备,比方执行复位程序初始化栈、堆,做时钟系统的默认配置、中断矢量表的定义与分配等。

网络上有些文章对该文件做了不错的整体性介绍,这里仅就该文件中的几个小问题一起交流分享下。【注:下面用到的工程是基于STM32F429的,使用IDE为ARM MDK】

怎么在编译后的MAP文件里看不到变量__heap_base?

有人发现在启动文件里明明有定义__initial_sp和__heap_base,可在MAP文件里只看到__initial_sp的地址安排,却死活找不到__heap_base的影子。为什么呢?

这是因为我们目前的工程代码里没有涉及到堆操作。虽然启动文件里有针对HEAP的配置,但由于代码里实际上没用到堆,编译时候就没分配堆的地址相关信息,自然在MAP文件里找不到__heap_base。

487a426bd864a2f723a5f62c2e51e162.png

以上图为例,其实此时Heap_Size写多大是没啥实际意义的,写0x200和写0没差别。这就像我们在代码定义一些完全不会被用到的变量一样,编译时是不会被安排内存空间的。

然而,如果我们在代码里有涉及HEAP操作,若启动文件里的HEAP配置依然如上图所示,那情况就不一样了。比方我们在用户代码做动态内存分配,这里使用malloc函数进行动态内存分配示例下。

63b74c3bc600500bf213518f735b5f8c.png

这时我们再去查看MAP文件,就可看到堆的地址信息了,跟启动文件里分配的一致。

79093a12a94b93864cdbe8f4bbbb50c2.png

顺便提醒下,我们在用户代码里做内存动态分配时注意分配的空间大小要遵循启动文件里预设的HEAP大小限制,必要时需做适当调整。

启动文件里怎么用掉了1KB的RAM?

有人在查看STM32程序代码编译后的MAP文件时,发现启动文件就用掉了1024B的RAM,如下图所示。觉得有点奇怪,想知道这1KB用到哪里去了?是不是固定的?

74249793c26bc4d0b4c6112be7f33a95.png

我们知道启动文件主要基于汇编写成,实现些最基本的软硬件初始化工作,似乎用不到这么多RAM。

其实,这里1KB初始值为0的RAM,是安排给栈用的,而这个栈大小的配置就是在启动文件里实现,但并非仅限于用在启动文件里。MAP文件里显示的启动文件所用ZI数据大小,跟下面栈配置是关联的。

9740aa94ea81eeeed8f53e910582f99e.png

显然,这个RAM开销并非固定的。

尽管我们建立工程时可能有些默认配置或经验配置,但我们完全可以结合自身工程代码的需要灵活调整。如果说,代码里函数调用涉及到的局部变量较少、中断嵌套情形也不多,你完全可以将栈数据设计得小些,或许很多时候512B【0x200】都绰绰有余了。总之,这个数据不是固定不变的,具体开发时按需调整。

比方,我将上面的栈空间大小改为512B,再看看编译后的结果。那个ZI Data大小也随之而变了。

9910f5be2d8252aee475adb8de92070a.png

上面是基于栈空间大小的调整来解释启动文件里ZI数据的大小变化。如果说我们的代码里还用到堆,这时启动文件里的ZI数据就不仅仅是栈空间大小的数据了,还会包括堆空间的大小。比方,我们在代码里启用动态内存分配使用到堆。在启动文件里对堆、栈的配置如下:

f3742c5e29d5beb08cf22a0b5ae33a06.png

按照上面配置并启用动态内存分配,再去查看编译结果,基于启动文件所用到的ZI数据大小变成了栈与堆空间二者之和。如下图所示【1024+512=1536】:

d8f8cc215e7a69bfb8c866b3ba5bc386.png

启动文件里的RO Data是怎么产生的?

有人在查看MAP文件时,发现启动文件里产生了一批RO只读数据,如下图所示:

334e02b52ecf22f7c142f14c082a7a73.png

上图是基于STM32F429的工程编译后产生的,那个428 Bytes只读数据怎么来的?

其实,这个数据就是存放中断向量地址表所用到的,不同的系列这个数据会有差异。该向量表除了第一个字单元存放MSP栈顶地址外,其它均为系统异常或中断入口地址,作为常量数据存放在FLASH里。我们具体看看这里的428B怎么来的。打开启动文件,我们可以看到一串连续的DCD操作,如下图所示:

adcfa50337bcd423406cfdf66942f34c.png

以STM32F429为例,在启动文件里稍微数数可得知这里共有107个地址入口项,每项用到一个4字节字,刚好对应上面的428 Bytes.

启动文件里的__main函数跟用户的main()有关系吗?

我们知道,启动文件的主要功能就是为用户程序的正常运行做最基本的初始化准备工作,__main()函数就是完成该使命的重要一环。

0163587b80fe8911530028819afa3ff6.png

2607618b60b5355b44c0935679a3fe14.png

它是一个C库初始化函数入口,主要负责执行一些必要的代码及数据从装载区到执行区的拷贝,将ZI内存区的数据初始化为0。对C库函数进行初始化,初始化堆、栈等,有时还可能包括一些代码解压操作,最后跳转至用户man()函数运行用户程序。

一般来讲,关于这个__main()函数的功能及作用大致了解就好,通常将其视为黑盒子。

启动文件里对中断矢量表起始地址进行初始化是在哪里实现的?

在STM32 MCU家族里,除了基于Cortex M0内核的STM32F0系列外,都有个中断矢量寄存器【SCB->VTOR】用来初始化中断矢量表的起始地址。它的初始化一般在启动文件的复位程序里的SystemInit()函数完成。

77b016a687443a4e07f3fd3835fb9167.png

a456af9375c416c90c199b006b616fde.png

最后基于该话题顺便给些提醒作为本篇结尾。我们在基于STM32芯片做IAP应用时,对于APP代码记得做好VTOR的重定位【注:F0系列操作例外】;在从BOOT区跳转到APP区之前先将刚才开启过的所有中断使能都禁用掉;如果开启了Cache的话,也将Cache禁用掉;保证跳转时清清爽爽,不捎一滴水,不带一片云。

------------ END ------------

后台回复『STM32』『嵌入式软件设计与开发』相关文章。

欢迎关注我的公众号,回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。

欢迎关注我的视频号:

b525f952e274a573161ae11bbf88c9be.png

点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。


推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Mono为何能跨平台
    概念JIT编译(JITcompilation),运行时需要代码时,将Microsoft中间语言(MSIL)转换为机器码的编译。CLR(CommonLa ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • Python中sys模块的功能及用法详解
    本文详细介绍了Python中sys模块的功能及用法,包括对解释器参数和功能的访问、命令行参数列表、字节顺序指示符、编译模块名称等。同时还介绍了sys模块中的新功能和call_tracing函数的用法。推荐学习《Python教程》以深入了解。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • JVM:33 如何查看JVM的Full GC日志
    1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr ... [详细]
author-avatar
魅客网小魅
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有