理解STM32内存分配、堆栈与变量存储位置的分析

目录

  • 1、STM32内存(FLASH和RAM)
  • 2、FLASH
  • 3、RAM
  • 大部分参考自以下链接,并结合自己的实践与理解形成本文:
    1、博客园
    2、正点原子论坛

    1、STM32内存(FLASH和RAM)

      一般来说单片机的内存指的是FLASH和RAM,当在程序中定义了全局变量、局部变量、只读变量等参数时都是会存放到对应的FLASH或者是RAM中。具体对单片机FLASH和RAM的介绍之后再写,这里只对单片机内存分配,对堆和栈以及变量的存储做一个梳理和记录。

    2、FLASH

      FLASH主要是存放代码和数据的。下面是将 flash 内部进行细分之后的一张图。(图来源于1、博客园

      STM32的flash是从地址0x0800 0000开始的,是向上增长的。Flash又可以细分为这么几个部分,分别是文本段 (Text),其中文本段中又包含可执行代码 (Executable Code)和常量 (Literal Value),在文本段之后就是只读数据区域 (Read Only Data),只读数据段后面接着的就是数据复制段 (Copy of Data Section),这个段充当的作用是存放程序中初始化为非 0 值的全局变量的初始值,之所以要将初始值存放到这里,是因为全局变量是存放在 RAM 上的,RAM 上的值掉电便丢失,每次上电后这些变量是要进行重新赋值的,而重新赋的值就存放在这里。
      可以看到STM32的flash中存放了代码、常量、只读数据和数据复制段。
      如何理解这些我们来做个实验。简单写一个点亮LED的测试工程,定义由const修饰的一个常量TEST_DATA1,和一个有初始值的全局变量TEST_DATA2。

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "usart.h"	
    
    const int TEST_DATA1= 4660; //一个常量
    
    int TEST_DATA2=0x1112;//一个有初始值的全局变量
    
    int main(void)
    { 
    	delay_init(168);		  //初始化延时函数
    	LED_Init();		        //初始化LED端口
    	uart_init(115200);
    
    	printf("%p\r\n",&TEST_DATA1);
    	printf("%p\r\n",&TEST_DATA2);
    	while(1)
    	{
    	  GPIO_ResetBits(GPIOF,GPIO_Pin_9);  //LED0对应引脚GPIOF.9拉低,亮  等同LED0=0;
    	  GPIO_SetBits(GPIOF,GPIO_Pin_10);   //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
    	  delay_ms(500);  		   //延时300ms
    	  GPIO_SetBits(GPIOF,GPIO_Pin_9);	   //LED0对应引脚GPIOF.0拉高,灭  等同LED0=1;
    	  GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
    	  delay_ms(500);                     //延时300ms
    	}
    }
    

    验证过程
      1、通过代码里printf把TEST_DATA1和TEST_DATA2的地址打印出来,通过串口助手可以看到:

      TEST_DATA1存放在flash的0x0800 0d4c中,而TEST_DATA2存在ram中。这里解释一下,TEST_DATA1是一个常量存放在flash中是没错的,而TEST_DATA2是一个有初始值的全局变量,应该和上面说的一样存放在flash中然后在上电时把值搬到ram中,但是为什么打印出来是在ram呢?原因应该是它已经搬完了。接下来进一步去flash中找这两个值。
      2、在MAP文件中查找,通过双击“LED”,会跳转到MAP文件,我们使用快捷键“ctrl+f”在MAP中查找TEST_DATA变量,如下图


    在这里依然是串口里打印出来的地址,那再换一种方式。
      3、通过ST-LINK Utility软件直接查看芯片的flash。先定位到TEST_DATA1所在的0x0800 0d4c:

    在flash的0x0800 0d4c处找到了定义的常量TEST_DATA1:4660(0x1234),通过全局搜索定义的TEST_DATA2:0x1112,在flash的最后找到了这个全局变量的初始值0x1112。

      这里已经验证过这两个值就是定义的TEST_DATA1和TEST_DATA2,还想测试的话可以多定义几个,结果是一样的。
      这就是flash中变量的存储,除了代码外,还存在一些常量、只读变量以及有初始值的全局变量。

    3、RAM

      相对对flash来说,ram主要就是用来存储数据了,如下是STM32中ram的分区(图来源于1、博客园

      ram中包含了如下几个部分:
        1、data : 存放初始化为非 0 值的全局变量
        2、bss : 存放未初始化或者是初始化为 0 的全局变量
        3、堆 (heap) : 由 malloc 申请,由 free 释放
        4、栈 (Stack) : 存放局部变量和函数调用时的返回地址
      其中data和bss比较好理解就是一些全局变量。堆和栈的空间可以由我们来自由设定如下图所示,只要这些部分加起来不超过STM32的RAM空间。

      在STM32的启动文件(.s)中,刚开头就有对堆和栈空间的定义描述。如图定义了栈的大小是(0x400),堆的大小是(0x200)。里面存放的是一些函数执行时的局部变量、中断入口等,函数执行结束时这些存储单元自动被释放。是程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。再来看一下MAP文件,如下图


      可以看到在整个ram空间(0x2000 0000起)依次存放了data、bss、HEAP、STACK,其中堆和栈的大小和我们定义的是一致的。
      在ram中值得关注的是堆和栈的空间,堆是向上增长的而栈是向下生长的,如果一个函数运行的时候有大量的局部变量(栈向下增长),同时程序在整个过程中malloc申请了大量的堆空间而没有释放(堆向上增长),造成堆和栈空间的冲突,一旦堆栈冲突,系统就崩溃了。
      特别说明一下,我使用了很多正点原子的工程,他们里面使用的自定义的mymalloc函数(区别于C的malloc)存放在bss字段里而不是HEAP字段里,使用的时候别搞混了。
      综上就是STM32内存分配、堆栈以及变量存储位置的理解。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 理解STM32内存分配、堆栈与变量存储位置的分析

    发表评论