单片机 内存管理

首先,我们用keil编译器将写完的单片机代码全部编译后,编译窗口会输出以下信息:
在这里插入图片描述

可以看到,编译之后的运行文件被分成Code, RO-data, RW-data, ZI-data四部分存放于单片机的SRAM和FLASH中。

Code:代码占用的flash大小。

RO-data[read-only data]:只读常量(包括const修饰变量和define定义的常量)

RW-data[read and write data]:初始化过的变量

ZI-data[zero initialized data]:没有初始化的变量,堆栈数据也属于它

在此之上,我们又可以将这四部分分为 Total RO Size(Code+RO-data), Total RW Size(RW-data+ZI-data), Total ROM Size(Code+RO-data+RW-data)。在keil生成的xxx.map文件的最后可以看到各部分具体的数据大小。
在这里插入图片描述
其中,RAM和FLASH组成如下:
在这里插入图片描述

注意,RW-data在FLASH和RAM中均会存在一份。

这里RW-data为什么既占用FLASH,又占用RAM呢?

这是因为RAM是随机存取存储器(random access memory),也就是手机上经常说的运行内存,RAM是易失性存储,断电会丢失数据。所以已初始化的数据会存储在Flash中,上电会从FLASH搬移至RAM中。

那为什么ROM Size不包括ZI Data?

这是因为已经初始化的数据,在掉电后需要保存初始值,以便上电运行后重载,因此存在rom中。而ZI Data数据都是0,上电运行后直接清零即可,包含进去反而浪费存储空间。

之后,在我们的keil编译器的设置选项卡中可以看到,在STM32F407VET中,FALSH(ROM)的存储起始地址是0x8000000,存储大小是512KB(0x80000)。同时SRAM(RAM)有两块,第一块(IRAM1)的存储起始地址是0x20000000,存储空间大小是112KB(0x1C000),第二块(IRAM2)的存储起始地址是0x2001C000,紧接着第一块,存储大小是16KB(0x4000),两块加起来一共有128KB。
block 1内有SARM1(112KB,0x2000 00000 ~ 0x2001 BFFF)和SRAM2(16KB, 0x2001 C000 ~ 0x2001 FFFF)两块连续的SRAM,可供所有的AHB 主控总线访问。
为什么是两块SRAM?这是因为主总线支持并发SRAM访问,提高执行效率。例如当 CPU 对 112 KB SRAM 进行读/写操作时,以太网MAC 可以同时对 16 KB SRAM 进行读/写操作;或者CPU和DMA可以同时访问不同的SRAM。

在这里插入图片描述
以上计算结果和芯片STM32F4芯片数据手册中的存储地址划分一致。
在这里插入图片描述

block 0共有512M,其中Reserved即为保留区,即可以使用外部flash扩展这个块的存储空间,只要地址在范围内就可以。
block 1名称就是SRAM,所以这个区域就是用来放置RAM。而图中的64KBSRAM就是stm32内置的RAM,MCU的基本配置就是得有CPU+RAM+外设。可以看到这个块也有512MB,所以这个RAM也可以通过外部扩展至512MB,当然外部扩展的肯定不如内置的好用了。
SRAM就是运行内存,就和电脑、手机上经常说的几G内存是同一个东西。它的作用就是把flash(这里只对于stm32来说)里正在运行的代码段(函数、变量等等)放进这个内存里,然后CPU对这里面的数据进行读写操作。
搞清楚RAM和FLASH的组成后,再把目光投向单片机的堆和栈。单片机产生的堆和栈数据属于ZI-data,所以这块数据也是存放在RAM中。其中堆是向上增长,栈是向下减少。如果堆空间使用过大(malloc开辟的空间过大),那会造成堆栈冲突,此时离程序崩溃已经不远了。左图是标准c程序内存组成,右图是stm32的程序内存组成。
在这里插入图片描述

MCU的堆、栈也是属于这片区域的,这里所说的堆栈和数据结构的堆栈概念不能混为一谈,当然也是类似的。堆空间存放上电后长久存在的值,即程序员malloc申请的存储空间,栈空间则是存放一些临时值,包括函数的参数值、局部变量值。而其他未初始化和初始化全局变量以及静态变量(属于已初始化全局变量)则是放在ZI-data,RW-data段(bss和data段)里。
在这里插入图片描述

通过keil编译器中ST官方编写的汇编启动文件,可以找到对应芯片的堆栈大小。下图可以看到,STM32F407的堆空间大小是1KB(0x400),栈空间大小是512B(0x200)。也就意味着,我们创建的局部变量和使用的函数临时参数不能超过栈内存空间大小1KB,malloc申请的内存空间大小不能超过512B。
在这里插入图片描述

在keil编译器生成的list文件(xxx.map)中同样可以看到代码段(Text)、数据段(data,包括所谓的静态存储区)、堆栈的具体存储起始地址。可以看到堆的起始位置是被分配在data段之后,如果我们申请的data段更大,那么堆的位置就会向后延伸,直至到达SRAM的理论上限(128KB)。下图看到堆的起始位置是0x20000588,存储大小是512B;栈的起始位置是0x20000788,存储大小是1KB。
在这里插入图片描述

注意,堆之前的bss段起始地址是0x20000588,占用存储空间是96B,所以下一个堆空间的起始位置应该是0x20000524+96D=0x20000584。但是从map文件中我们看到定义的堆空间起始是0x20000588,是因为0x20000588可以被8整除,即满足地址8字节对齐的需求。所以我们在之后使用RAM时也要遵循8字节对齐的原则分配存储空间。
现在,如果我们想要开辟一块存储空间上电后临时存储我们想要的数据,例如开辟一块数组,那么开辟的全局数组变量将会被存放在data段的起始位置,即0x20000000往后的128KB内存地址块内。如果我们要在不使用外部flash的情况下将数据永久保存,理论情况下可以将数据存放在0x80000000+Total ROM Size的后面,当然通常情况下代码段是不断扩张的,保险起见我们需要将断电保存的数据尽量保存在FLASH最后地址,即0x80080000附近。

参考多份博客整理,如有错误,请指正。
参考博客1 【STM32单片机入门-1】堆栈/全局变量,局部变量,静态全局变量,局部静态变量等
参考博客2 【单片机】MCU内存管理

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值