内存映射
在一些桌面程序中,整个内存映射是通过虚拟内存来进行管理的,使用一种称为内存管理单元(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 文件的最底部有这么一个信息: 上图中的各个段是和上文所述是能够进行对应起来的,正如下面这张表所示: 总结 对于 RAM 和 flash 空间都有限的 MCU 来讲,了解各个变量在内存中的存储位置是很有必要的,他能够很好地帮助我们去解决很多问题。
上一篇:中断服务子程序是如何被执行的
下一篇:重构外部中断回调函数来区分外部中断具体引脚做具体对应的任务
推荐阅读最新更新时间:2024-11-04 18:37
设计资源 培训 开发板 精华推荐
- OM11084: Keil LPC1769评估板
- 用于 300Khz 自举 N 沟道晶体管的 Tc110501Ect Pfm/Pwm 升压直流/直流控制器的典型应用
- 使用 Analog Devices 的 LT1764AEFE-1.5 的参考设计
- L7806C的典型应用通过降压电阻降低功耗
- AD5332 并行接口、双电压输出、8 位 DAC 的典型应用
- esp01s物联网桌面摆件
- LT3091MPFE 高效低噪声单电感正负转换器的典型应用,具有 LDO 输入至输出控制
- R_08_V30基于IPS2电机换向传感器的设计
- AD5326 四路电压输出、12 位数模转换器的典型应用
- IS31AP4996-SLS2-EB,演示板具有 IS31AP4996、1.1W AB 类音频功率放大器 IC,具有高电平有效待机模式
- 德州仪器为降低噪声和系统功耗推出新一代逻辑单元
- 罗姆推出专为智能电表优化的新型多频段无线通信LSI
- 一图看懂新能源汽车动力蓄电池回收服务网点建设和运营指南
- Phillips-Medisize 赢得合同, 将生产电子功能组合给药设备
- 贸泽开售Laird Connectivity带功率放大器的BL654PA模块
- vivo Y73s发布 配天玑720延续轻薄机身 售价1998元
- Nokia 225 4G发布售价为349元!支持与智能机互连
- 小米手环5古铜紫腕带上架!售价仅19.9元还有7色可选
- realme Q2现身GeekBench!测试结果让人期待新机
- OPPO A15主要规格解密 厚8mm重175g售价不超过1000