stm32专题三十六:MDK编译过程和文件类型(一)

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,就可以节省空间。

实际测试一下:

#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工程开始的,如下所示:


 

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值