嵌入式MCU开发,通常受限于硬件资源,往往 RAM 和 ROM 空间有限,大一点的 MCU,RAM 空间也才 512KB,小的更是小的可怜,因此对于 RAM 空间和 ROM 空间是如何被分配走的,想要深入进阶的话是肯定需要了解的。
本文将采用STM32控制器为例,配合实验详细记录/阐述关于嵌入式MCU资源使用那些事!
比如我们使用 keil 编译完程序,编译成功之后,会在输出栏提示一行这样的信息
Program Size: Code=4620 RO-data=312 RW-data=16 ZI-data=1168
这便是我们编译的程序的大小,但这个 Program Size
又如何理解呢?其中的 Code
、 RO-data
、RW-data
、ZI-data
段分别代表的是什么含义呢?
首先,我们来看看各自段代表的都是什么含义:
Code
段:代表的就是我们所编写的代码所占用的 flash 空间。当然需要注意的是,代码段的内容是经过编译器优化以及汇编之后的大小,你在代码里添加注释,或者一些无意义的代码是不会增大code段的大小的。RO-data
段:即read only - data
,只读数据段。在代码中定义的 const
常量,printf
打印的固定字符等,这些均会被存放到此数据段内。RW-data
段:即 read write - data
,可读可写段。代码中的变量存放在此数据段内。ZI-data
段:即 Zero Initial - data
,初始化为0的可读写变量。明白了每个字段代表的是什么含义,那我们定义的局部变量、全局变量以及常量是如何占用的ram空间的呢?以及具体真正占用 ram 和 rom 空间又是如何计算的呢?
关于占用 ram 和 rom 空间又是如何计算,直接提供给大家以下公式:
Total ROM = Code + RO-data + RW-data
Total RAM = RW-data + ZI-data
关于局部变量、全局变量、常量如何占用ram,我们接下来采用实验验证推理。
在弄清楚我们定义变量如何消耗 RAM 空间之前,我们先要弄清楚栈大小Stack_Size
和堆大小Heap_Size
这两个重要概念!
MCU的RAM空间被划分为多个区域,其中有两个重要组成:堆和栈(当前还有其他区域),此两个区域的大小设置可在对应的启动文件内查看并设置。
这里我们简单描述下栈和堆是如何使用的:
栈的使用:
堆的使用:
此外针对裸机程序,整个程序中使用的栈,均从启动文件startup_xxxx.s
文件中 Stack_Size 设置的栈空间中分配;而RTOS程序,各线程具有独立的栈空间,这一点需要注意。
接着我们进行如下测试验证。
Stack_Size EQU 0x800
,程序编译结果:Program Size: Code=3620 RO-data=380 RW-data=20 ZI-data=2124
Stack_Size EQU 0x400
,程序编译大小:Program Size: Code=3620 RO-data=380 RW-data=20 ZI-data=1100
ZI-data
数据段的变化。Stack_Size EQU 0x400
,即分配的栈空间为 0x400 = 1024Byte
__attribute__((unused))
修饰数组HardFault_Handler()
中断综上,可得出以下结论:局部变量会占用栈Stack空间,栈大小Stack_Size在启动文件内设置,当栈空间不足时会导致程序运行崩溃
局部变量占用的是系统栈空间,那全局变量是不是也是如此呢?接着我们同样进行验证。
注意,采用全局变量进行验证时,为了让编译器不进行优化,故需要在main函数内增加一行对此数组的使用,我们此处在main函数内增加一行代码如下:printf("%s", a);
定义数组a为全局变量,大小1Byte,使用__attribute__((unused))
修饰,防止被编译器优化,编译查看程序编译结果,并运行
编译结果为:Program Size: Code=3600 RO-data=380 RW-data=24 ZI-data=1096
,程序正常运行
修改数组a的大小为400Byte,继续验证
编译结果为:Program Size: Code=3600 RO-data=380 RW-data=20 ZI-data=1500
,此时我们可以发现Total RAM增大了,Add Total Ram = (The current RW-data + The current ZI-data ) - (The laster RW-data + The laster ZI-data) = (1500 + 20) - (1096+ 24) = 400Byte
(注意此处400Byte != (400 - 1)Byte 与字节对齐有关) ,程序正常运行
继续修改数组a大小为1030,使其超过系统栈大小(当前配置的系统栈大小0x400=1024Byte
),继续验证
编译结果为:Program Size: Code=3600 RO-data=380 RW-data=20 ZI-data=2132
,程序正常运行,并没有之前的死机现象,同时我们可以发现Total RAM继续增大,相比上次增大了 Add Total Ram = (The current RW-data + The current ZI-data ) - (The laster RW-data + The laster ZI-data) = (2132 + 20) - (1500 + 20) = 632Byte
(注意此处632Byte != (1030 - 400)Byte与字节对齐有关)
综上,通过实验验证可知,定义全局变量不会占用系统栈空间,全局变量不同于局部变量,全局变量从RAM中单独申请空间。
如果是const常量会怎样呢?我们在以上两个实验的基础上,增加const修饰变量
关于堆的分配及使用比栈就简单多了,如下:
Heap_Size EQU 0x200
。malloc
动态申请,如果有足够大小的堆空间的话,则成功分配,并返回首地址指针;如果堆空闲空间不够,则失败,返回NULL;free
动态释放,使用的时候注意不要重复释放,否则容易导致bug!以上便是针对我们在编程中,编写代码时所会涉及到的RAM空间使用的分析,特别是变量及常量对于ram的占用,总结来说有以下几点:
这些都是很细节的东西,想要成为一名资深开发者所必须了解的内容。希望对你有所帮助。
此外需要注意的是,以上是针对裸机系统进行的分析,在RTOS系统中,会有些许差别,主要在于在RTOS中会有系统栈和线程栈,不过也都是栈~
如果你觉得我文章写的不错,欢迎点赞、关注+收藏,谢谢~,以下我的一些推荐:
相关推荐:
专栏:文件系统专栏(点击跳转)
专栏:电机控制专栏(点击跳转)
其他专栏,去主页看看吧~
博客主页:爱出名的狗腿子(点击跳转)
创作不易,转载请注明出处!
关注、点赞+收藏,可快速查收博主有关分享!