• STM32内存知识


    在了解STM32内存之前需要了解 MCU 的型号和MDK 中的.map 文件,很多刚学习 stm32 时都不会过多的去了解 MCU 的选型,是在太枯燥了。这里在从新了解一下,久了就熟悉了。

    一、STM32命令规则

    二、MDK下生成.map文件

    1. 在MDK中勾选.map文件的生成,确认后编译一下工程即可生成,map文件。

    2. 打开.map文件

    三、MDK下文件基本概念

    在.map文件的最后可以看到文件信息的统计,如下图所示:

    当然每次编译完成后也可以看到统计信息,如下图所示:

    了解MDK下的一些常用变量名:

    变量 作用
    code 代码存储区,存放函数体的二进制代码
    Ro-data 只读数据存储区,存放字常量数据类型(如const类型)程序结束后有系统自动释放
    RW-data 初始化可读写变量的大小,程序结束后由系统自动释放。
    ZI-data 没有初始化的可读写变量大小,程序结束后由系统自动释放。
    heap 堆区,一般由程序员分配释放,若程序员不释放,程序结束时可能由OS释放。
    stack 栈区,由编译器自动分配释放,存放函数的参数值,局部变量的值等。
    .text 与RO-code同义
    .constdata 与RO-data同义
    .bss 未初始化的全局和静态变量,编译器自动初始化为0
    .data 初始化的全局和静态变量(与RW-data同义)
    PAD 地址空间对齐
    RO Size 包含Code及RO Data,表示只读数据占用Flash空间的大小。
    RW Size 包含RW Data及ZI Data,表示运行时占用的RAM的大小。
    ROM Size 包含Code,RO Data及RW Data,表示烧写程序所占用的Flash的大小。

    注意:栈向下生长,内存地址由高至低;堆向上,内存地址由低至高

    通过上面表格可知STM32在编程时所用RAM和ROM的大小:

    Flash(ROM)=Code+Ro-data
    Sram(RAM)=Rw-data+ZI-data
    

    四、STM32内存

    这里我找了一位大佬总结的博客“STM32内存知识你真的了解吗?”,感觉挺好的,所以我直接引用一下启动的图片,如下图所示:

    • STM32程序运行的流程
      程序在运行之前,需要可执行将镜像文件(一般是bin或hex文件),通过烧写工具写入STM32的Flash中。STM32上电启动(从Flash启动时)后会将RW段中的RW-data(初始化的全局变量)拷贝到RAM中,然后根据编译器给出的ZI地址和大小分配出ZI段,并将这块RAM区域清零。如下图所示:左边是每上电flash+ram的状态,右边是上电后运行时flash+ram的状态。

    注意:

    • 可执行映像文件烧录到 STM32 后的内存分布包含 RO 段和 RW 段两个部分,其中其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
    • STM32运行时不会拷贝RO段,因为CPU的可执行代码是直接从Flash中读取的。

    STM32编程时需要注意的事项

    • 堆栈的大小在编译器编译之后是不知道的,只有在运行时才知道,所以容易造成堆栈溢出(发生hardfault错误),那怎么知道自己的内存大小了,通过选型手册就知道了,比如我使用的是STM32F103C8T6,ROM是64k,RAM是20k,如下图所示:

    • 程序中的常量,如果没加const也会编译到SRAM里,加了const会被编译到flash中。

    • 栈向下生长,内存地址由高至低;堆向上,内存地址由低至高,堆栈之间没有固定的界限,堆栈冲突时会导致系统崩溃,如下图所示:

    五、.map文件

    1. 不同文件中函数调用的关系

      ==============================================================================
      
      Section Cross References
      
          startup_stm32f10x_hd.o(RESET) refers to startup_stm32f10x_hd.o(.text) for Reset_Handler
          startup_stm32f10x_hd.o(.text) refers to system_stm32f10x.o(.text) for SystemInit
          startup_stm32f10x_hd.o(.text) refers to entry.o(.ARM.Collect$$$$00000000) for __main
          stm32f10x_rcc.o(.text) refers to stm32f10x_rcc.o(.data) for APBAHBPrescTable
          stm32f10x_gpio.o(.text) refers to stm32f10x_rcc.o(.text) for RCC_APB2PeriphResetCmd
          stm32f10x_usart.o(.text) refers to stm32f10x_rcc.o(.text) for RCC_APB2PeriphResetCmd
          led.o(.text) refers to stm32f10x_rcc.o(.text) for RCC_APB2PeriphClockCmd
          led.o(.text) refers to stm32f10x_gpio.o(.text) for GPIO_Init
          main.o(.text) refers to led.o(.text) for LED_GPIO_Config
          main.o(.text) refers to stm32f10x_gpio.o(.text) for GPIO_ResetBits
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry10a.o(.ARM.Collect$$$$0000000D) for __rt_final_cpp
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry11a.o(.ARM.Collect$$$$0000000F) for __rt_final_exit
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry7b.o(.ARM.Collect$$$$00000008) for _main_clock
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry8b.o(.ARM.Collect$$$$0000000A) for _main_cpp_init
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry9a.o(.ARM.Collect$$$$0000000B) for _main_init
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry5.o(.ARM.Collect$$$$00000004) for _main_scatterload
          entry.o(.ARM.Collect$$$$00000000) refers (Special) to entry2.o(.ARM.Collect$$$$00000001) for _main_stk
          entry2.o(.ARM.Collect$$$$00000001) refers to entry2.o(.ARM.Collect$$$$00002712) for __lit__00000000
          entry2.o(.ARM.Collect$$$$00002712) refers to startup_stm32f10x_hd.o(STACK) for __initial_sp
          entry2.o(__vectab_stack_and_reset_area) refers to startup_stm32f10x_hd.o(STACK) for __initial_sp
          entry2.o(__vectab_stack_and_reset_area) refers to entry.o(.ARM.Collect$$$$00000000) for __main
          entry5.o(.ARM.Collect$$$$00000004) refers to init.o(.text) for __scatterload
          entry9a.o(.ARM.Collect$$$$0000000B) refers to main.o(.text) for main
          entry9b.o(.ARM.Collect$$$$0000000C) refers to main.o(.text) for main
          init.o(.text) refers to entry5.o(.ARM.Collect$$$$00000004) for __main_after_scatterload
      
      
      ==============================================================================
      
      

      main.o(.text) refers to led.o(.text) for LED_GPIO_Config,是main.c文件中调用了led.c文件中的LED_GPIO_Config函数

    2. 被删除的冗余函数

      ==============================================================================
      
      Removing Unused input sections from the image.
      
          Removing startup_stm32f10x_hd.o(HEAP), (0 bytes).
          Removing core_cm3.o(.emb_text), (32 bytes).
          Removing system_stm32f10x.o(.constdata), (20 bytes).
          Removing misc.o(.text), (220 bytes).
          Removing stm32f10x_usart.o(.text), (880 bytes).
      
      5 unused section(s) (total 1152 bytes) removed from the image.
      
      ==============================================================================
      
      

      删除冗余的函数,有效降低程序的代码量,MDK自动优化,可以通过“One ELF Section per Function”选项开启,开启后可以大大优化程序代码量,打开方式是如下图所示:

      打开后再次编译,看看.map文件中删除了81个函数,优化了2762字节,如下图所以:

    3. 局部标号和全局标号

      • 局部标号
        主要是在文件中用static声明的全局变量和函数。汇编文件中的标号地址(作用域限本文件)

      • 全局标号
        非static声明的变量和函数,汇编文件中的标号地址(作用域全局工程)

      注意:

      • Number 不站地址空间,大小为0。
      • DATA 只读数据
      • 文件中的标号再次用i.声明,说明在c文件中用static声明了的,如下图所示:

    4. 映像文件
      映像文件可以分为加载域和运行域
      加载域反应了ARM可执行映像文件各个段存放在寄存器中的位置关系。

    5. 组件大小

    6. 映像的真实大小

    六、.htm文件

    文件中做大的作用就是基本统计了所有被调用函数的栈stack使用的情况(不考虑中断嵌套)

    1. 栈的最大深度,调用路劲是main ⇒ LED_GPIO_Config ⇒ GPIO_Init

    2. 递归调用函数

    3. 函数指针

    4. 全局标号

      比如复位中断函数,使用的是Thumb指令,占用0字节栈空间,函数代码大小2字节

      比如系统时钟初始化函数SystemInit ,使用的是Thumb指令,函数代码大小68字节,占用栈空间8字节,代码深度28字节,函数调用路径是Call Chain = SetSysClock ⇒ SetSysClockTo72

    参考文献

    stm32的内存分布:https://blog.csdn.net/BooleanWater/article/details/119278723

    STM32单片机的内存分布详解(1):https://www.bilibili.com/read/cv13912565

    STM32内存知识你真的了解吗?:https://blog.csdn.net/qq_49864684/article/details/119887704

    MDK生成的map和htm文件分析:https://www.bilibili.com/video/BV1t3411C7Pu/?spm_id_from=autoNext

  • 相关阅读:
    RabbitMQ-02(docker安装、RabbitMQ的角色分类、AMQP、消息确认机制ACK)
    【Leetcode】面试题 01.09. 字符串轮转
    加密的手机号,如何模糊查询?
    ARM pwn 入门 (4)
    02-数据结构-线性表
    DIN模型和SIM模型原理与实践
    hot100-最大正方形
    二、MAVEN的安装和配置
    7.nodejs--egg框架简介、以及get、post请求
    如何让一套代码完美适配各种屏幕?
  • 原文地址:https://www.cnblogs.com/jzcn/p/16360728.html