历史上的今天
返回首页

历史上的今天

今天是:2024年11月01日(星期五)

正在发生

2021年11月01日 | stm32专题三十六:MDK编译过程和文件类型(一)

2021-11-01 来源:eefocus

MDK编译过程和文件类型

1 编译过程

(1)编译:MDK 软件使用的编译器是 armcc 和 armasm,它们根据每个 c / c++和汇编源文件编译成对应的以“.o”为后缀名的对


象文件(Object Code,也称目标文件),其内容主要是从源文件编译得到的机器码,包含了代码、数据以及调试使用的信息;


编译器:

.o文件(每个.c文件,编译完都会生成.o目标文件):


(2)链接:链接器 armlink 把各个.o 文件及库文件链接成一个映像文件 “.axf” (MDK)或 “.elf”(IAR) ;

(3)对链接器生成的 elf 映像文件利用格式转换器fromelf 转换成“.bin”或“.hex”文件,交给下载器下载到芯片的FLASH中;

MDK 软件的编译过程:

2 程序的组成、存储和运行


MDK的编译信息:

在工程的编译提示输出信息中有一个语句“ Program Size: Code=xx RO-data=xx RWdata=xx ZI-data=xx”,它说明了程序各个域的大小,编译后,应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域会呈现不同的状态,这些域的意义如下:

接下来,看一下const关键词和编译器优化,对代码大小的影响(均在优化等级 Level 0 下进行测试):

代码如下,很简单的电灯程序,编译完的大小为:

可以看到,RO_data只读数据域的大小为 320字节。接下来,进行测试,使用const关键词,定义一个大的全局数组,观察RO_data的大小变化:

可以看到,无论是代码域,还是只读数据域,所有的大小都没有发生改变。为什么?实际上是因为,我们虽然定义了这个数组,但并没有在程序中有效使用,所以这个数组会直接被编译器优化掉。再看一个例子:

我们虽然在程序中使用到了数组,但实际上,这个语句并没有起到任何作用,所以一样会被编译器优化。


如果我们真正的使用了这个数组,再看一下编译大小:

这次,编译的结果就明显不同。首先,代码域增大,这个很明显时因为我们增加了一些有效代码;比较重要的时,RO-data最开始为320字节,现在为1344字节,1344 - 320 = 1024,正好就是我们定义的const数组。


接下来,再看一下编译器优化等级对编译效果的影响(开到最高等级Level 3):

可以看到,代码域明显变小,其他的空间域无变化。

RW-data,正常时会存储到 Flash 空间,在程序运行时,会被加载到 RAM 区,此时这些数据就可以被修改,进行测试:


未定义前:

接下来,去掉前面定义的大数组的 const,看看编译结果:

可以看到,RW-data增加了1024字节。


ZI-data与RW-data不同,它不需要先存储在Flash中,而是在程序运行时,直接在RAM空间划分一个区域,把这些数据全部初始化为0,后续的运行就和RW-data一致。如果在定义变量时没有初始化,MDK 编译器会默认初始化为0。也就是说,ZI-data的数据不占用Flash空间,如果我们定义ZI-data,就可以节省空间。

后续的运行就和RW-data一致。如果在定义变量时没有初始化,MDK 编译器会默认初始化为0。也就是说,ZI-data的数据不占用Flash空间,如果我们定义ZI-data,就可以节省空间。

实际测试一下:


#define SOFT_DELAY Delay(0x0FFFFF);

uint8_t largeArray[1024] = {0, 1, 2};

编译结果如下,初始时ZI-data  = 1024字节

然后我们将变量的初始值设置为0,再编译:


#define SOFT_DELAY Delay(0x0FFFFF);

uint8_t largeArray[1024] = {0};

可以看到,ZI-data增加了1024字节。


总结一下,我们要存储到 Flash 中的数据大小总共为:Code + RO-data + RW-data(字节)


ZI-data 堆栈空间

stm32中,使用的堆栈空间,其实是输入ZI-data。为什么要使用栈和堆?首先是栈,在进行函数调用时,会使用到函数内部定义的局部变量,当然了,这些局部变量也需要临时存储,而且有一个特点,进入函数时申请,退出函数时释放,整一个的生命周期很短,使用栈可以自动申请和释放局部变量以节省内存空间。试想一下,我们定义的一个普通变量,如int a = 8; 它就会一直存在于内存中占用几个字节,没办法释放掉。而使用栈空间,就能够自动的存储和释放局部变量。堆空间的原理类似,主要用于动态内存分配。


注意,如果不使用malloc申请堆空间,则编译器会优化,不把堆空间计算到ZI-data中。我们来看一下,为什么ZI-data为1024?


这是启动文件中,默认定义的栈空间和堆空间的大小,可以看到,栈空间为1024字节,堆空间为512字节。而在我们的函数中,如果不使用malloc函数申请内存,堆空间会直接被编译器优化掉。

可以看到,ZI-data的大小就是分配的栈空间大小。我们尝试一下修改掉启动文件,增加栈的大小,看ZI-data会有什么变化?


如图所示,我们设置的栈大小为2048,将启动文件中的栈设置为0X800,再重新编译:

编译结果如下,与我们预期的结果相同。

接下来,我们在函数中添加动态内存相关的程序,看对编译结果的影响(栈设置为1024字节):


可以看到,当我们使用动态内存分配时,编译器的ZI-data就不再优化堆空间,此时ZI-data = 初始值为0的全局变量 + 栈空间 + 堆空间,如下所示:

编译结果如下所示,ZI-data = 1024(栈)+ 512(堆)+ 1000(0初始值全局变量)= 2536

程序的存储与运行:

stm32的程序运行描述:

总结一下:

在 MDK 编译器中,只要选择好了芯片型号,就会自动对应的给出Falsh起始地址+大小,内存起始地址+大小,下面以stm32vet6(64 + 512)为例说明:

首先是Flash,我们看一下中文参考手册中的描述:

类似的,关于SRAM:

编译工具链


1 Windows cmd路径命令(与Linux系统不太一样)


通常情况下,我们要进入其他盘符下的任意目录,需要在CMD窗口运行两次命令:第一次,进入盘符,第二次进入指定目录


#进入D盘


d: 


#进入D盘下的Keil5文件夹


cd test


cls  清除命令行


如下所示:

2 之前有提高编译器的文件夹,里面包含了好几个编译和链接工具,现在使用Windows PowerShell来查看:

可以看到,该文件夹下有5个编译和链接工具,我们尝试来运行一下 fromelf.exe,直接在命令行输入 .fromelf.exe,然后会打印出许多提示信息(包括MDK版本,编译器版本、功能选项等):

3 配置环境变量:


为什么要配置环境变量?刚才,我们是进入到/ARM/ARMCC/bin/路径下,打开的fromelf工具。那么,我们能不能任意路径下都直接打开fromelf呢?可以尝试一下

结果显示,我们直接在c盘下输入fromelf,并不能运行,因为系统在该目录下没找到对应的文件,所以提示错误。当我们输入正确的路径后,fromelf可以运行。那么,每次都要输入这么长的路径吗?看一下windows系统对于环境变量的说明:

很明显,我们可以通过将/ARM/ARMCC/bin/这个路径添加到系统的环境变量,这样就更方便我们程序的执行:


环境变量添加完成后(win10需要重启),直接就能任意路径下运行:

4 工具链的作用(armcc  armasm  armlink):


4.1 armcc


 用于把 c/c++文件编译成 ARM 指令代码,编译后会输出 ELF 格式的 O 文件(对象、目标文件)。


首先来看armcc(MDK推荐使用Level 1 一级优化):

命令说明:


使用 armcc --cpu list,打印当前所有的cpu列表.可以看到,其中包含了Cortex-M0 M3 M4等内核的CPU。

接下来看MDK对armcc编译器的控制命令:

当我们在 MDK 中进行勾选时,实际上就是对armcc编译器进行对应的配置:

4.2 armasm 


armasm 是汇编器,它把汇编文件编译成 O 文件。


然后看一下 armasm,其实和 armcc 非常类似:

这里,我们也是通过 MDK 进行勾选来配置:

4.3 armlink


armlink 是链接器,它把各个 O 文件链接组合在一起生成 ELF 格式的 AXF 文件,AXF文件是可执行的,下载器把该文件中的指令代码下载到芯片后,该芯片就能运行程序了;利用 armlink 还可以控制程序存储到指定的 ROM 或 RAM 地址。


armlink 链接器:

同样的,在 MDK 软件中进行勾选配置(注意,这里可以用来配置ROM和RAM的基地址):

链接器默认是根据芯片类型的存储器分布来生成程序的,该存储器分布被记录在工程里的 sct 后缀的文件中,有特殊需要的话可自行编辑该文件,改变链接器的链接方式。


4.4 armar


armar 工具用于把工程打包成静态链接库 .lib文件,.lib文件可以提供给别人使用(别人能够使用,但不能查看源代码),从而保护源代码,勾选可以生成 .lib文件。


4.5 fromelf


 fromelf 可根据 axf 文件生成 hex、bin文件, hex 和 bin 文件是大多数下载器支持的下载文件格式。可以在下图加入一些指令:

关于User配置的描述:

接下来,我们尝试利用fromelf 手动生成 bin文件和hex文件:


1 我们复制生成的 .axf到文件夹,如图所示,并在此路径下打开windows powershell:


可以看到,在此路径下,此时只有一个 流水灯.axf 文件,接下来,查看一下 fromelf 的说明:


根据说明提示,来生成bin文件,使用的命令如下:


fromelf --bin --output 流水灯.bin 流水灯.axf 

结果如下,bin文件生成成功。

同理,再生成 Intel 格式的hex文件:


fromelf --i32 --output 流水灯.hex 流水灯.axf  


hex文件已经生成了,我们还需要看一下,利用fromelf和MDK生成的hex,是不是完全一致,结果如下:


这里还有一个地方值得注意,那就是 .axf 的路径一定要正确。实际上,MDK的相对路径,是从uvprojx工程开始的,如下所示:

推荐阅读

史海拾趣

Allen Avionics Inc公司的发展小趣事

对不起,我无法提供关于Allen Avionics Inc 公司的相关故事。

胜利(VICTOR)公司的发展小趣事

进入21世纪,胜利公司加大了对技术研发的投入,不断推出具有创新性的产品。例如,公司研发的碳纤维羽毛球拍,不仅轻盈耐用,而且性能卓越,迅速成为市场上的热销产品。此外,公司还积极引入新材料、新工艺,不断提升产品的品质和性能。

Cressall Power Resistors公司的发展小趣事

随着国内市场的饱和,Cressall开始将目光投向国际市场。公司积极参与国际电子行业的展会和交流活动,与国际同行建立了广泛的联系。通过与国外企业的合作,Cressall不仅引进了先进的技术和管理经验,还成功打开了多个海外市场。同时,公司还加强了与国际知名企业的战略合作,共同开发新产品,进一步提升了公司的国际竞争力。

台湾固锝(GD)公司的发展小趣事
对于需要低噪声的应用场景,应选择低噪声的放大器芯片,并合理设计电路以减少噪声。
BAHCO公司的发展小趣事

随着全球化趋势的加速推进,BAHCO也在积极拓展全球市场。目前,公司已在多个国家和地区设立了分支机构或销售网络,实现了全球范围内的业务覆盖。未来,BAHCO将继续加大在研发、生产、销售等方面的投入力度,不断提升产品质量和服务水平。同时,公司还将积极探索新的商业模式和合作方式,以适应不断变化的市场环境并实现可持续发展。

这些故事虽然不直接涉及BAHCO在电子行业的发展,但它们展示了BAHCO在工具制造领域的辉煌历程和不断进取的精神。作为一家拥有百年历史的知名企业,BAHCO凭借其卓越的品质、创新精神和全球市场布局,成功地在工具制造业中占据了重要地位。未来,随着技术的不断进步和市场需求的不断变化,相信BAHCO将继续保持其领先地位并迎来更加美好的发展前景。

Euvis Inc公司的发展小趣事

为了进一步提升竞争力,Euvis Inc公司积极开展跨界合作,与互联网、通信、汽车等领域的企业建立战略合作关系。通过资源整合和优势互补,公司实现了在多个领域的快速发展,进一步巩固了其在电子行业中的领先地位。

问答坊 | AI 解惑

基于vs8的智能设备工程中使用CFile类打开文件

请问各位大侠,我在基于vs8的智能设备工程中使用CFile类打开文件时,发现打开不了文件,代码如下: CFile file ; CFileException ex; BOOL bBool = file.Open( _T(\"123.txt\"), CFile::modeRead, &ex); if ( bBool == 0 ) {     & ...…

查看全部问答>

驱动调试问题

我现在的情况是:没有目标板,只有一台PC,装了PB5.0,EVC4.0. 最近在学习WINCE的驱动开发,但由于没有开发板,想尝试着用模拟器调试运行驱动, 不知道有没有相应的模拟工具,可以用来调试驱动程序的? 如果有,希望能简单介绍下操作步骤!! 谢谢!! (得到 ...…

查看全部问答>

电容触摸感应原理与应用(4)

触摸按键的程序的实现: 7 研读数据手册 ①基本功能 在进行程序设计之前,必须仔细研读MSP430的数据手册。根据TOUCH PAD手册中所说,只有2211这块MCU具有比较器的功能。下面让我们来看看这块芯片的一些详细参数。 工作电压1.8V~3.6V 16位的计 ...…

查看全部问答>

关于2812烧写的问题

请问EEWORLD的工程师,小弟第一次烧写2812,结果程序运行到一个FOR循环时卡住出不来了(我在FOR循环后加了个亮灯的语句,结果没有亮灯,去掉FOR循环,亮灯正常,故得出程序卡在FOR循环里了)还请达人指点一下啊。我在编译时,曾报这样的警告: > ...…

查看全部问答>

关于FPGA启动时间以及启动时的端口状态

比如Altera 的 EP1C6, 在刚上电加载配置文件期间,这个加载到完成的时间大概有多长,有些可能要说和配置文件的大小有关系,目前这个文件大小还真不好说,只是想问一下有没有用过的人,按照你们用过的那个,大概的时间范围就行?是豪秒级的,还是几 ...…

查看全部问答>

MSP430F5529时钟100KHZ

目标环境需要个100KHZ的信号 好象外接入到XT1,XT2的范围都不能满足100KHZ,如果接入个100K的晶振会怎么样的结果呢! …

查看全部问答>

如何使用论坛的发帖功能之分割线使用疑问?

本帖最后由 Sur 于 2014-7-6 20:42 编辑 如何使用论坛的发帖功能之分割线 这里只要选择自己看重的分割线图形就点击下就可以了 下面是各个分割线使用效果,发现分割线不能铺满,如何解决求指导。而且长短不一 ...…

查看全部问答>

LPC1500体验+系统搭建验证:下载程序运行 傻瓜篇(XP系统MDK5)续

本帖最后由 gaon 于 2014-9-3 13:33 编辑 前几天发了个贴子: LPC1500体验+系统搭建 傻瓜篇(XP系统MDK5) https://bbs.eeworld.com.cn/forum.php?mod=viewthread&tid=445456&fromuid=440826 记录了一下学习系统搭建的过程。发现还有人看,就 ...…

查看全部问答>