在瞭解STM32記憶體之前需要了解 MCU 的型號和MDK 中的.map 檔案,很多剛學習 stm32 時都不會過多的去了解 MCU 的選型,是在太枯燥了。這裡在從新瞭解一下,久了就熟悉了。
在MDK中勾選.map檔案的生成,確認後編譯一下工程即可生成,map檔案。
開啟.map檔案
在.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記憶體知識你真的瞭解嗎?」,感覺挺好的,所以我直接參照一下啟動的圖片,如下圖所示:
注意:
堆疊的大小在編譯器編譯之後是不知道的,只有在執行時才知道,所以容易造成堆疊溢位(發生hardfault錯誤),那怎麼知道自己的記憶體大小了,通過選型手冊就知道了,比如我使用的是STM32F103C8T6,ROM是64k,RAM是20k,如下圖所示:
程式中的常數,如果沒加const也會編譯到SRAM裡,加了const會被編譯到flash中。
棧向下生長,記憶體地址由高至低;堆向上,記憶體地址由低至高,堆疊之間沒有固定的界限,堆疊衝突時會導致系統崩潰,如下圖所示:
不同檔案中函數呼叫的關係
==============================================================================
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函數
被刪除的冗餘函數
==============================================================================
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位元組,如下圖所以:
區域性標號和全域性標號
區域性標號
主要是在檔案中用static宣告的全域性變數和函數。組合檔案中的標號地址(作用域限本檔案)
全域性標號
非static宣告的變數和函數,組合檔案中的標號地址(作用域全域性工程)
注意:
映像檔案
映像檔案可以分為載入域和執行域
載入域反應了ARM可執行映像檔案各個段存放在暫存器中的位置關係。
元件大小
映像的真實大小
檔案中做大的作用就是基本統計了所有被呼叫函數的棧stack使用的情況(不考慮中斷巢狀)
棧的最大深度,呼叫路勁是main ⇒ LED_GPIO_Config ⇒ GPIO_Init
遞迴呼叫函數
函數指標
全域性標號
比如復位中斷函數,使用的是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