一直以来本人对MCU的FLASH和SRAM(RAM存储器一种)是如何分配的只是知道个大概,而不尽祥。不是非常了解内存分配大多数时候对我们进行裸机程序编写是没有太大影响的。
但一旦上升到使用嵌入式操作系统,这个问题就难以回避,因为操作系统没有任何应用编译之后都要占用至少10几KB,这对RAM紧张的MCU来说是致命的,
我们要想尽办法来省RAM,而办法是基于你知道内存是怎么分配的。
ps:有些只有10KB RAM的单片机就不要打OS的主意了,还是老老实实的用时间片轮询吧,效率也是不错的。
以下偏重RAM的分析
一、已经了解的
如图是MDK编译之后的消息
从中知道
占用的FLASH = Code + RO-data + RW-data
stm32 FLASH的起始地址是0x08000000,当然也可以自定义起始地址,不过记得在main函数中定义变量后加一句SCB->VTOR=FLASH_BASE | OFFSET;OFFSET是想要偏移的量,可宏定义或直接0xXX。
当然也可以调用库函数 NVIC_SetVectorTable()进行偏移,效果一样。IAP升级这样用的多。
占用的SRAM = RW-data + ZI-data
stm32 SRAM的起始地址是0x20000000。看MDK的魔法棒设置target选项也是可以设置RAM起始地址和大小的,不过一般我不动RAM。
我一般是这样记忆的,前面3个是给FLASH,后面2个是给SRAM。
含义:
Code :代码
RO-data :只读数据,字符串常量(const修饰的)
RW-data:可读可写数据,已经初始化的全局变量 或者 static修饰的变量(不管局部变量还是全局变量,没有显示地初始化的话会初始化为0)
ZI-data :没有进行初始化的全局变量
二、基于.map文件的分析,我逐个试出来的结果如下,右边是.map文件的内容。
PAD是垫子、卫生巾的意思,占用两个字节,姑且理解为隔开各个段吧。
宏定义、结构体、联合体、枚举类型等都属于代码。RAM只存放变量。但FLASH不只是存放代码,还有变量,就是和RAM都有的交集,即MDK生成的 RW-data (全局变量、static变量)
这么多的变量,他们是怎么排列在RAM中的呢,看上面.map文件中的地址就知道了。就是已初始化的变量在前面,然后跟着是未初始化的全局变量,然后是堆,最后是栈。
也就是说栈和堆的起始地址是不可控的,是根据我们程序的全局变量多少计算出来的,一句话,吃剩下的。
三、裁剪栈和堆的大小
1.当我们程序运行到一个函数中莫名其妙的就崩了,进入HardFault_Handler,数组也没有越界啊。
其中可能的一个原因就是局部变量过多,栈溢出,不够用了,需要我们Tailor(裁剪)一下RAM。ps:数组越界也属于栈溢出。在启动文件中进行裁剪。HardFault_Handler网上也有很多分析方法。
2.例如,如下启动文件我们可以吧Stack_Size从0x400(1KB)改到0x800(2KB)一般就不会出现崩溃了,如果还崩,那要找找其他原因(是不是函数使用了非法的指针(即非法地址,0xE0000000之类的MCU不认,不能访问的地址))等等。
3.当然有时候栈加上其他的,大小超过了RAM总大小,编译器就会提示不够,就要一点一点加着试。
这时大小超过了RAM总大小,也能从一定程度说明程序设计存在不合理,几乎用光了RAM。比如全局变量设置的过大,实际没用这么多,也设置这么多,一般多实际最大使用量加2个字节都没问题。大型的全局变量(即结构体全局变量、联合体全局变量)过多,重复繁杂,共用率不高。就要逐个文件删繁就简,砍掉能砍的全局变量,缩小能缩的结构体,字节对齐,各种看家本领使出来。砍到不能再砍只能考虑砍功能或者换芯片了。
四、FREERTOS、emwin等第三方库的内存占用情况
1.freertos分配得到的是ZI-DATA,表示未初始化的全局数组(以下简称内存池),存放于SRAM。emwin分配内存也是编译生成ZI-DATA,存放在SRAM。其他第三方库,如LWIP,猜想也是如此,需要大量内存,可能要通过stm32的FSMC接口外挂SRAM。待更新。。。
2.在有freertos的情况下,OS函数pvPortMalloc是从内存池中申请内存,OS函数 xPortGetFreeHeapSize 得到的是内存池剩余字节数。而使用<stdlib.h>中的malloc是直接从SRAM中申请,不影响内存池的大小。
3.总结一下总体SRAM的分配:例如,STM32自带片内SRAM共64K。我的分配方案如下:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) ) //freertos的
#define GUI_NUMBYTES (10*1024) // emwin的
所以要根据项目的实际情况,协调各个第三方库的占用空间。尽量使用pvPortMalloc申请内存,用尽内存池再用malloc,或者不用malloc,只留下全局变量的空间,其余全部给freertos。