从地址和存储器角度重新认识微控制器,分析堆栈 分析代码

版权声明: https://blog.csdn.net/xiaoxilang/article/details/86093637

代码分析基础:从底层去分析代码的内存管理、和设备资源

1.存储器器和地址

1)存储器

存储器是计算机结构的重要组成部分。存储器是用来存储程序代码和数据的部件,有了存储器计算机才具有记忆功能。基本的存储器种类见图 (SRAM及 FLASH 作为内存和程序存储空间--类似于电脑的内存条大小和硬盘大小,外部扩展sram类似于电脑加内存条

RAM是“Random Access Memory”的缩写,被译为随机存储器。所谓随机存取,指的是当存储器中的消息被读取或写入时,所需要的时间与这段信息所在的位置无关。 RAM 可随读取其内部任意地址的数据,时间都是相同的,因此得名根据 RAM的存储机制,又分为动态随机存储器 DRAM(Dynamic RAM)以及静态随机存储器 SRAM(Static RAM)两种;

      做为嵌入式方面的开发人员,拿到一个芯片后,我们首先看它的关键参数指标之一,就是该IC有多少多少容量的RAM,多少多少容量的ROM(主要是EEPROM)和Flash,多少片上外设资源。当然,前提是芯片自带这两个模块。

2)地址

举例stm32f407--32位控制器(0-0xFFFFFFFF)

0xFFFFFFFF是一个32位16进制地址。这个地址代表4GB大小;

连接被控总线的是 FLASH,RAM和片上外设,这些功能部件共同排列在该4GB 的地址空间内

3)Block 0位置(代码区,eeprom存储区等)从0x0开始

Block 0位置:STM32F4 的闪存模块block 0 由:主存储器、系统存储器、OPT 区域和选项字节等 4 部分组成

flash:1M(默认的条件下,图中 STM32F407ZGT6 IROM1 的起始地址(Start)一般为 0X08000000,大小(Size)为 0X100000,即从 0X08000000 开始的 1024K 空间为我们的程序存储区==>设定bootloader, FLASH 的 APP大小会关注这块)

但是,往往写的代码用不了这么多空间,

如下例子,由于该芯片没有eeprom,可以令0x2000=131072bit / 1024==128k,然后可以分出部分空间来做模拟eeprom;

有、无eeprom:STM32F4 本身没有自带 EEPROM,但是 STM32F4 具有 IAP(在应用编程)功能,所以我可以把它的 FLASH 当成 EEPROM 来使用,或者外部加入一个eeprom芯片;又比如stm32l151等系统有了eeprom

4)ram(内存区)--属于block1:地址从0x20000000开始

ram(内存): STM32F407ZGT6 的SRAM 大小(不算 CCM)为 128K 字节;设定bootloader, SRAM 的 APP大小会关注这块,比如下图

    如下是ram的128K的使用情况如下

5)外设区(操作GPIOa/b/c..口各种外设区域-各种重映射)-block2从0x40000000开始

Block2 用于设计片内的外设,根据外设的总线速度不同,Block 被分成了 APB和 AHB两部分,其中 APB 又被分为 APB1 和 APB2,AHB 分为 AHB1 和 AHB2,具体见表格 6-4。还有一个 AHB3包含了 Block3/4/5,AHB3包含的 3个 Block用于扩展外部存储器,如SRAM,NORFLASH 和 NANDFLASH 等;

                         =====================其他的ic=================

eg.stm32l151c8t6的存储器和地址映射:eeprom:EEPROM地址在0x0808 0000 — 0x0808 0FFF 共4096个字节

========================接下来,进一步分析用如上的基础知识来解决问题============================

2.STM32堆栈大小及位置(针对stm32内存分配、内存溢出等问题分析)

计算机的bss段,data段、text段、堆(heap)和栈(stack)的了解

参考资料:https://blog.csdn.net/shenghuaday/article/details/78877949

1).基本概念:MDK(keil)的代码四段分类

keil编译后code(存储程序代码的),RO-data(存储const常量和指令),RW-data(存储初始化值不为0的全局变量),ZI-data含义(存储未初始化的全局变量或初始化值为0的全局变量),所以有:

mcu的 flash实际存储数据=Code + RO Data + RW Data(存储)  ;  RAM大小= RW-data+ZI-data(内存);

这个是MDK编译之后能够得到的每个段的大小,也就能得到占用相应的FLASH和RAM的大小,但是还有两个数据段也会占用RAM,但是是在程序运行的时候,才会占用,那就是堆和栈。在stm32的启动文件.s文件里面,就有堆栈的设置,其实这个堆栈的内存占用就是在上面RAM分配给RW-data+ZI-data之后的地址开始分配的。

:是编译器调用动态内存分配的内存区域。
:是程序运行的时候局部变量的地方,所以局部变量用数组太大了都有可能造成栈溢出。
堆栈的大小在编译器编译之后是不知道的,只有运行的时候才知道,所以需要注意一点,就是别造成堆栈溢出了,不然就等着hardfault找你吧。==>不理解的,也可以去看我之前的博客,有一个关于hardfault的查询帮助信息!

2)一个程序本质上都是由 bss段、data段、text段三个组成的

     在当前的计算机程序设计中是很重要的一个基本概念。而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题;

    C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中,text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化;

bss段:
  bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
  bss是英文Block Started by Symbol的简称。
  bss段属于静态内存分配。 

data段:
  数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
  数据段属于静态内存分配。 
  
text段:
  代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。
  这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
  在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。 
堆(heap):
  堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
  当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
  当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
  
栈(stack):
   栈又称堆栈,是用户存放程序临时创建的局部变量,
  也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
  除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
  由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场。
  从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

3)堆栈的例子--中断压入

Flash,SRAM寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。可访问的存储器空间被分成8个主要块,每个块为512MB。
FLASH存储下载的程序。
SRAM是存储运行程序中的数据。
所以,只要你不外扩存储器,写完的程序中的所有东西也就会出现在这两个存储器中。

Stm32有通用寄存器 R0‐ R15 以及一些特殊功能寄存器,其中包括了堆栈指针寄存器。当stm32正常运行程序的时候,来了一个中断,CPU就需要将寄存器中的值压栈到RAM里,然后将数据所在的地址存放在堆栈寄存器中。等中断处理完成退出时,再将数据出栈到之前的寄存器中,这个在C语言里是自动完成的。

4)malloc 和 free实现内存管理(管理ram)

内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源

分配malloc 原理

当指针 p 调用 malloc 申请内存的时候,先判断 p 要分配的内存块数(m),然后从第 n 项开始,向下查找,直到找到 m 块连续的空内存块(即对应内存管理表项为 0),然后将这 m 个内存管理表项的值都设置为 m(标记被占用),最后,把最后的这个空内存块的地址返回指针 p,完成一次分配。注意,如果当内存不够的时候(找到最后也没找到连续的 m 块空闲内存),则
返回 NULL 给 p,表示分配失败

释放free原理
当 p 申请的内存用完,需要释放的时候,调用 free 函数实现。free 函数先判断 p 指向的内存地址所对应的内存块,然后找到对应的内存管理表项目,得到 p 所占用的内存块数目 m(内存管理表项目的值就是所分配内存块的数目),将这 m 个内存管理表项目的值都清零,标记释放,完成一次内存释放。

猜你喜欢

转载自blog.csdn.net/xiaoxilang/article/details/86093637