STM32 内存分配解析及变量的存储位置

发布者:温馨的家庭最新更新时间:2022-04-27 来源: eefocus关键字:STM32  内存分配  变量  存储位置 手机看文章 扫描二维码
随时随地手机看文章

内存映射

在一些桌面程序中,整个内存映射是通过虚拟内存来进行管理的,使用一种称为内存管理单元(MMU)的硬件结构来将程序的内存映射到物理RAM。在对于 RAM 紧缺的嵌入式系统中,是缺少 MMU 内存管理单元的。因此在一些嵌入式系统中,比如常用的 STM32 来讲,内存映射被划分为闪存段(也被称为Flash,用于存储代码和只读数据)和RAM段,用于存储读写数据。


STM32 的 Flash 和 RAM 地址范围

笔者标题所说的内存是指 STM32 的 Flash 和 RAM,下图是 ARM Cortex M3 的地址映射图:

在这里插入图片描述

从图中我们可以看到 RAM 地址是从 0x2000 0000 开始的,Flash地址是从 0x0800 0000 开始的,笔者将在下文中着重对这两部分进行剖析。


Flash

代码和数据是存放在 flash 中的,下面是将 flash 内部进行细分之后的一张图,图中标明了代码段,数据段以及常量在 flash 中的位置。

在这里插入图片描述

如上图所示,Flash 又可以细分为这么几个部分,分别是文本段 (Text),其中文本段中又包含可执行代码 (Executable Code)和常量 (Literal Value),在文本段之后就是只读数据区域 (Read Only Data),当然并不是所有架构的单片机都满足这样一个排布规律,这里只针对ARM Cortex M3 系列,只读数据段后面接着的就是数据复制段 (Copy of Data Section),第一次遇到这个概念的朋友看到数据复制可能会有所疑惑,其实这个段充当的作用是存放程序中初始化为非 0 值的全局变量的初始值,之所以要将初始值存放到这里,是因为全局变量是存放在 RAM 上的,RAM 上的值掉电便丢失,每次上电后这些变量是要进行重新赋值的,而重新赋的值就存放在这里。那为什么不存放初始化为 0 的全局变量初始值呢,原因也很简单,既然是初始化为 0,那么在上电后统一对存放初始化为 0 的全局变量的那块区域清0就好了。下面举一个例子分析各个变量在上述中的存储位置:


#include

const int read_only_variable = 2000;

int data = 500;


void my_function(void)

{

int x = 200;

char *str = "string";

}


在上述代码中,read_only_variable 是一个用 const 修饰的全局变量,它是只读的,存放在 flash 中的只读数据区域,编译器会给 read_only_variable 分配一个地址,并将 2000 这个数据存放到这个位置。data 这个变量将存放到 RAM 中的RW区域中 (后面将会进行详细讲解),但是 data 后面的初始值 500 将会被存放到数据复制区域中, 也就是上图中从下往上的第三个区域。在 my_function 中的变量 x 将会被存放到 RAM 中的堆栈中,将 x 赋值为 200 ,200 将被存储到 flash 里的 Text 中的常量区 (Literal Valu) 中。str 是一个 char 型的指针变量,它指向的是字符串第一个字符存放的位置,然而对于字符串 string 来讲,它是存放在Text常量区的,所以指针变量指向这个区域的一个地址,但是因为它终归中局部变量,它指向 Flash 的一个地址,但是其本身还是存放于 RAM 中的堆栈上的。


RAM

STM32单片机的片内RAM会被链接文件“分区”为如下几个段:

在这里插入图片描述

如上图所示,RAM 中包含了如下几个部分:


栈 (Stack) : 存放局部变量和函数调用时的返回地址

堆 (heap) : 由 malloc 申请,由 free 释放

bss : 存放未初始化或者是初始化为 0 的全局变量

data : 存放初始化为非 0 值的全局变量

下面举一个简单的例子来说明变量在各个段中的存储位置:


#include

#include

int data_var = 500;

int bss_var0;

int bss_var1 = 0;

static int static_var;


void my_function(void)

{

static int static_var1 = 0;

int stack = 0;

char *buffer;

const int value = 1;

buffer = malloc(10);

}


上述变量的命名已经很清楚地表明了变量处于 RAM 中的哪一个段,data_var 是已经初始化的全局变量,存放在 RAM 的 data 区,bss_var0 和 bss_var1是未初始化和初始化为0的全局变量,他们都存放于 RAM 中的 bss段,由 static 修饰的static_var 和 static_var1 都存放于 bss段,区别只在于两个变量的作用域不同。stack 是在函数内部定义的局部变量,其存放于 RAM 的栈区域,用 const 修饰的局部变量 value ,虽然他是只读的,但是它是存储于 RAM 中的栈中的,这里也说明一点,并不是所有用 const 修饰的变量都是存放于只读变量区的。buffer指针变量用 malloc 函数申请了 10 字节的内存空间,那这10字节的内存空间位于堆中。


堆栈溢出

如果在程序运行的过程中,堆的空间也一直在消耗,同时栈的空间也在增加,那么这时堆和栈如果碰到一起,那么就会造成堆栈溢出,从而导致我们的程序跑飞。


STM32中的map文件分析

在用 keil 编译 STM32 工程之后,我们会得到一个 map 文件,map 文件的最底部有这么一个信息:

在这里插入图片描述

上图中的各个段是和上文所述是能够进行对应起来的,正如下面这张表所示:

image.png

总结

对于 RAM 和 flash 空间都有限的 MCU 来讲,了解各个变量在内存中的存储位置是很有必要的,他能够很好地帮助我们去解决很多问题。


关键字:STM32  内存分配  变量  存储位置 引用地址:STM32 内存分配解析及变量的存储位置

上一篇:中断服务子程序是如何被执行的
下一篇:重构外部中断回调函数来区分外部中断具体引脚做具体对应的任务

推荐阅读最新更新时间:2024-11-04 18:37

STM32进入低功耗模式以及唤醒(RTC+中断)
此文献给做单片机工作的同僚,希望大家在嵌入式硬件的道路上越走越远,第一篇,望大家多多指导,不喜勿喷! 这里主要说一下,本人在调试STM32低功耗功能时遇到的问题以及解决思路与大家分享一下。 在确认使用低功耗功能后,大家铁定会通过不同途径查找相关的资料,了解到什么是低功耗、低功耗有哪几种以及不同模式之间的区别,最后还有一些程序截取、说明,资料不少,但是能让人一目了然理解的不太多,尤其是刚接触这个功能的新手,或许是资历尚浅,编程以及检索资料能力还有待提高。 再此,我抛砖引玉,发表一篇我的调试感受,我从 低功耗初体验、低功耗功能说明、低功耗总结三个方面进行说明。 首先,低功耗初体验。跟大家一样,查到STM
[单片机]
<font color='red'>STM32</font>进入低功耗模式以及唤醒(RTC+中断)
STM32学习笔记之时钟系统
首先 时钟是STM32单片机的脉搏,是单片机的驱动源。使用任何一个外设都必须打开相应的时钟。这样的好处就是,如果不使用一个外设的时候,就把它的时钟关掉,从而可以降低系统的功耗,达到节能,实现低功耗的效果。 1. STM32的时钟系统 在STM32中,一共有5个时钟源,分别是HSI、HSE、LSI、LSE、PLL (1)HSI是高速内部时钟,RC振荡器,频率为8MHz; (2)HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围是4MHz – 16MHz; (3)LSI是低速内部时钟,RC振荡器,频率为40KHz; (4)LSE是低速外部时钟,接频率为32.768KHz的石英晶体; (5)PLL为锁相环倍频
[单片机]
<font color='red'>STM32</font>学习笔记之时钟系统
STM32学习之DMAM-M模式
由于是M-M模式,所以只能选择DMA2 一、首先应该初始化DMA结构体 1.选择通道DMA_Channel,根据数据流选择对应的通道, 2.选择源数据地址DMA_PeripheralBaseAddr 3.目标地址DMA_Memory0BaseAddr 3.选择传输方向DMA_DIR,根据实际情况有这三种DMA_DIR_PeripheralToMemory DMA_DIR_MemoryToPeripheral DMA_DIR_MemoryToMemory 4.设定待传输数据数目DMA_BufferSize,源数据大小 5.使能源数据地址,目标地址递增, 6.源数据和目标宽度DMA_PeripheralDataSize和D
[单片机]
STM32 DAC详解
上一篇介绍了《 STM32ADC详解 》,既然有模拟转数字的ADC模块,那么就必然有数字转模拟的DAC模块。顾名思义,该模块仅具有ADC的补充功能。它将数字二进制值转换为模拟电压输出。DAC模块具有多种用途,包括音频生成,波形生成等。通常在大多数8位微控制器中,此模块不可用,并且通过脉宽调制(PWM)可以稍微满足其需求。部分原因是由于它们的硬件资源和运行速度相对较低。所有STM32单片机都具有PWM模块,但大容量STM32也具有DAC模块。STM32DAC模块不是很复杂,并且在工作原理方面与ADC模块相似。 01、DAC简介 从STM32F207数据手册看到,STM32F207具有两个DAC模块。 每个DAC具有独立的通道
[单片机]
<font color='red'>STM32</font> DAC详解
Keil MDK下学习STM32注意事项
在MDK下学习使用STM32也快两个礼拜了,基本掌握了STM32在MDK环境下的开发流程和基本硬件程序的书写。其间,遇到了许多问题,其中不少问题花费了我相当多得时间才得以解决,现将这些问题和解决方法列出。其中也有一些是学习过程中我觉得需要记下的注意事项。 1、STM32固件库使用外围设备的主要思路 在STM32中,外围设备的配置思路比较固定。首先是使能相关的时钟,一方面是设备本身的时钟,另一方面如果设备通过IO口输出还需要使能IO口的时钟;最后如果对应的IO口是复用功能的IO口,则还必须使能AFIO的时钟。 其次是配置GPIO,GPIO的各种属性由硬件手册的AFIO一章详细规定,较为简单。 接着相关设备需要
[单片机]
STM32小知识笔记
APB2负责AD,I/O,高级TIM(TIM1,TIM8),串口1。 APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。 110:PWM模式1- 在向上计数时,一旦TIMx_CNT TIMx_CCR1时通道1为有效电平,否则为 无效电平;在向下计数时,一旦TIMx_CNT TIMx_CCR1时通道1为无效电平(OC1REF=0),否 则为有效电平(OC1REF=1)。 111:PWM模式2- 在向上计数时,一旦TIMx_CNT TIMx_CCR1时通道1为无效电平,否则为 有效电平;在向下计数时,一旦TIMx_CNT TIMx_CCR1时通道1为有效电平,否则为无效电 平。 关于有效电平这句可以设定:
[单片机]
STM32 基础系列教程 6 - PWM
前言 学习stm32 基本定时器的使用,用基本定时器产生定时PWM, 来控制LED,通过并控制PWM波的占空比,实现呼吸灯效果 示例详解 基于硬件平台: STM32F10C8T6最小系统板, MCU 的型号是 STM32F103c8t6, 使用stm32cubemx 工具自动产生的配置工程,使用KEIL5编译代码。 本示例所用的最小系统板原理图: 关于CUBEMX工具及KEIL工具的操作将不再细讲,如果还有不熟悉的可以查看之前的教程文档。下面直接介绍工程配置: 系统时钟树 定时器配置 开启PWM通道1 设置定时器周期为100us, 计数时钟间隔为 1us. 引脚配置 工程代码 分别在
[单片机]
<font color='red'>STM32</font> 基础系列教程 6 - PWM
KUKA机器人smartPAD屏上空间鼠标的相关变量介绍
空间鼠标操作显示: 选项卡鼠标 激活运行模式 “ 空间鼠标 ” 配置 空间鼠标 用空间鼠标选择运行的坐标系 轴、全局、基坐标 或 工具 复选框 同步: 未勾选 (默认):在选项卡 按键 和 鼠标 中可以选择不同的坐标系。 勾选:在选项卡 按键 和 鼠标 中只能选择一个相同的坐标系。如果在一个选项卡中更改了坐标系,则另一个选项卡中的设置会自动调整。 审核编辑:刘清
[机器人]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved