7

STM32内存知识 - 浇筑菜鸟

 1 year ago
source link: https://www.cnblogs.com/jzcn/p/16360728.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

一、STM32命令规则

2406897-20220609152248981-241273497.png

二、MDK下生成.map文件

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

    2406897-20220609153715662-1470300397.png
  2. 打开.map文件

    2406897-20220609153858666-1164453810.png

三、MDK下文件基本概念

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

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

了解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内存知识你真的了解吗?”,感觉挺好的,所以我直接引用一下启动的图片,如下图所示:

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

注意:

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

STM32编程时需要注意的事项

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

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

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

    2406897-20220609165549693-1610527296.png

五、.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”选项开启,开启后可以大大优化程序代码量,打开方式是如下图所示:

    2406897-20220609173241553-418912003.png

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

    2406897-20220609173400362-143982045.png
  3. 局部标号和全局标号

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

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

      2406897-20220609173948742-1419813109.png

    注意:

    • Number 不站地址空间,大小为0。
    • DATA 只读数据
    • 文件中的标号再次用i.声明,说明在c文件中用static声明了的,如下图所示:
      2406897-20220609175255746-431344870.png
      2406897-20220609175344925-1515950117.png
  4. 映像文件
    映像文件可以分为加载域和运行域
    加载域反应了ARM可执行映像文件各个段存放在寄存器中的位置关系。

    2406897-20220609180418420-182491506.png
  5. 组件大小

    2406897-20220609180941603-404540502.png
  6. 映像的真实大小

    2406897-20220609181213538-51690389.png

六、.htm文件

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

  1. 栈的最大深度,调用路劲是main ⇒ LED_GPIO_Config ⇒ GPIO_Init
    2406897-20220609181608448-1356556175.png

  2. 递归调用函数

    2406897-20220609181826537-106273690.png
  3. 函数指针

    2406897-20220609181920161-1218579449.png
  4. 全局标号

    2406897-20220609181957299-2136621152.png

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

    2406897-20220609182109521-542799001.png

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

    2406897-20220609182617161-1345577882.png

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK