一般 MCU 包含的存储空间有:片内 Flash 与片内 RAM, RAM 相当于内存, Flash 相当于硬盘。编译器会将一个程序分类为好几个部分,分别存储在 MCU 不同的存储区。
之前分享过C程序的编译过程的笔记:C程序的编译过程是怎样的?
这里先简单看一下MDK的编译过程(它与其它编译器的工作过程是类似的 ):
(1) 编译, MDK 软件使用的编译器是 armcc
和 armasm
,它们根据每个 c/c++和汇编源文件编译成对应的以“.o”为后缀名的对象文件(Object Code,也称目标文件
),其内容主要是从源文件编译得到的机器码,包含了代码、数据以及调试使用的信息;
(2) 链接,链接器 armlink
把各个.o 文件及库文件链接成一个映像文件.axf
或.elf
;
(3) 格式转换,一般来说 Windows 或 Linux 系统使用链接器直接生成可执行映像文件 elf后,内核根据该文件的信息加载后,就可以运行程序了,但在单片机平台上,需要把该文件的内容加载到芯片上,所以还需要对链接器生成的 elf 映像文件利用格式转换器fromelf
转换成.bin
或.hex
文件,交给下载器下载到芯片的 FLASH 或 ROM 中。
这些编译工具都存在于我们MDK的安装目录下,如:
程序在我们的MDK编译后,Build Output
窗口显示信息如下:
Program Size 包含以下几个部分:
1) Code:代码段,存放程序的代码部分; 2) RO-data:只读数据段,存放程序中定义的常量; 3) RW-data:读写数据段,存放初始化为非 0 值的全局变量; 4) ZI-data: 0 数据段,存放未初始化的全局变量及初始化为 0 的变量;
编译完工程会生成一个. map
的文件,该文件说明了各个函数占用的尺寸和地址,在文件的最后几行也说明了上面几个字段的关系,如:
1) RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小; 2) RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小; 3) ROM Size 包含了 Code、 RO Data 以及 RW Data,表示烧写程序所占用的 Flash 空间的大小;
程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中
,一般是 bin
或者 hex
文件,该被烧录文件称为可执行映像文件
。STM32程序内存分布如:
左图是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了Code、 RO-data 的数据, RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
其中动态内存堆为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。如下面的例子:
代码中的 msg_ptr 指针指向的 128 字节内存空间位于动态内存堆空间中。
而一些全局变量则是存放于 RW 段和 ZI 段中, RW 段存放的是具有初始值的全局变量(而常量形式的全局变量则放置在 RO 段中,是只读属性的), ZI 段存放的系统未初始化的全局变量,如下面的例子:
sensor_value 存放在 ZI 段中,系统启动后会自动初始化成零(由用户程序或编译器提供的一些库函数初始化成零)。 sensor_inited 变量则存放在 RW 段中,而 sensor_enable 存放在 RO 段中。