一、MAP文件浅析
二、STM32启动过程
三、总结
参考资料:STM32 MAP文件浅析.pdf、STM32 启动文件浅析.pdf、Cortex-M3权威指南(中文).pdf
路径:战舰 V4\资料\1,入门资料\STM32 MAP文件浅析.pdf、STM32 启动文件浅析.pdf
战舰 V4\资料\8,STM32参考资料\Cortex-M3权威指南(中文).pdf
MAP文件(Memory Map File)是编译器生成的连接地图文件,提供了有关程序在内存中的布局信息。MAP文件包含了代码、数据、堆栈等在内存中的地址分配情况,以及每个模块的大小等信息。在MDK(Keil Microcontroller Development Kit)编译过程中,MAP文件对于分析程序存储占用情况非常有用。
以下是MAP文件的一些关键信息:
模块列表:
内存段信息:
符号表:
存储占用情况:
调用图:
通过分析MAP文件,开发者可以了解程序的内存布局,查看各个模块的存储占用情况,优化代码,解决内存溢出等问题。
MAP文件(Memory Map File)是在编译过程中由链接器生成的一种文件,用于描述程序在内存中的布局和各个模块的分配情况。以下是MAP文件的主要概念和作用:
概念:
作用:
MAP文件是在调试和优化阶段非常有用的工具,通过分析这些信息,开发者可以更好地理解程序在内存中的分布,从而优化代码、解决内存相关问题。
程序段交叉引用关系:
删除映像未使用的程序段:
映像符号表:
映像内存分布图:
映像组件大小:
这些信息有助于开发者深入了解程序在内存中的布局和分布情况,通过MAP文件,可以更好地进行代码优化、查找冗余代码、解决内存相关的问题。
学会分析:哪个.c占用flash 和ram比较大,以便针对性的优化
设置好 MDK 以后,我全编译当前工程,当编译完成后(无错误),就会生成.map
文件。在 MDK 里面打开 .map
文件的方法如图所示:
启动模式,也被称为自举模式(bootstrapping mode),是指在单片机复位后执行的初始化和启动操作。对于STM32和许多其他微控制器,启动模式包括以下几个关键步骤:
取出堆栈指针 MSP 的初始值:
取出程序计数器指针 PC 的初始值:
地址映射:
在STM32中,这些初始值是由芯片上的启动加载器(Bootloader)提供的,该加载器通常存储在固定的引导区域中。通过在这个引导区域中设置不同的启动模式标志位,可以选择加载不同的程序或固件。
这个启动过程确保了在复位后,程序能够正确地开始执行,并且栈和程序计数器都被正确初始化。
Reset复位:
获取MSP值0X0800 0000:
获取PC值0X0800 0004:
Reset_Handler:
启动文件 startup_stm32xxx.s:
main函数:
这个启动过程确保了在复位后,程序能够正确地开始执行。内部FLASH启动是默认的启动模式,但STM32芯片通常支持其他启动模式,例如通过串口或外部存储器引导。在这种情况下,复位向量和启动文件可能会有所不同。
对STM32启动过程的这些关键步骤概述。以下是对每个步骤的一些进一步的解释:
初始化MSP:
初始化PC:
设置堆栈大小:
初始化中断向量表:
__Vectors
是一个包含这些指针的数组。调用初始化函数:
调用 __main:
__main
是标准 C 库函数,它执行一系列的设置,并最终调用 main
函数。在用户代码中,main
函数是程序的起始点。这些步骤确保了在复位后,处理器能够按照预期的方式执行程序。这个过程是由启动文件和链接脚本控制的,具体的细节可能会因设备型号和编译环境而有所不同。
这段汇编代码是 Cortex-M 处理器的启动代码,主要完成了以下几个步骤:
导出 Reset_Handler 函数: EXPORT
关键字用于标明 Reset_Handler
函数是一个全局可见的标识符,可以被其他模块引用。
声明 __main 和 SystemInit 函数: IMPORT
关键字用于声明 __main
和 SystemInit
函数,这两个函数通常是由编译器生成的。
加载 SystemInit 函数的地址到 R0 寄存器: 使用 LDR
指令将 SystemInit
函数的地址加载到 R0 寄存器。
通过 BLX 指令调用 SystemInit 函数: BLX
指令用于调用函数,这里将通过 R0 寄存器中的地址调用 SystemInit
函数。这个函数通常包含了系统初始化的相关操作。
加载 __main 函数的地址到 R0 寄存器: 使用 LDR
指令将 __main
函数的地址加载到 R0 寄存器。
通过 BX 指令跳转到 __main 函数: BX
指令用于跳转到指定地址的子程序,这里将跳转到 __main
函数,即 C 语言程序的入口点。
定义 Reset_Handler 过程结束: ENDP
关键字用于表示 Reset_Handler 过程的结束。
弱定义标记: WEAK
关键字表示这是一个弱定义,如果在其他地方有相同的函数定义,链接器可以选择其中一个。在这里,如果系统提供了自定义的 SystemInit
函数,它将被使用;否则,使用默认的 SystemInit
函数。
这段代码是启动文件中的一部分,它负责初始化系统,并跳转到主程序的入口点。
堆栈是在程序运行时动态分配和管理内存的两个主要区域。这两个区域分别是:
栈(Stack):
堆(Heap):
在嵌入式系统开发中,尤其是对于资源有限的系统,合理管理栈和堆的大小是很重要的。太小的栈可能导致栈溢出,而太小的堆可能导致动态内存分配失败。在确定栈和堆的大小时,需要考虑程序的复杂性、函数调用深度、局部变量和动态内存需求等因素。
在嵌入式系统中,Reset_Handler
是一个特殊的函数,它是系统复位后执行的第一个函数。该函数负责初始化系统,设置堆栈和跳转到主函数(通常是 main
函数)。下面是一个简单的 Reset_Handler
函数的例子:
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
在这个例子中:
EXPORT
表示将 Reset_Handler
函数标记为可被外部调用。IMPORT
表示引入外部定义的函数,这里是 __main
和 SystemInit
。LDR R0, =SystemInit
将 SystemInit
函数的地址加载到寄存器 R0
。BLX R0
调用 SystemInit
函数。__main
函数,通常在该函数中会进入到用户编写的 main
函数。关于 __initial_sp
,它是初始化堆栈指针的一个符号。在 ARM Cortex-M 处理器中,启动代码通常将 __initial_sp
设置为堆栈的起始地址。在链接脚本中,你可能会看到类似如下的定义:
__initial_sp = 0x20020000; /* 堆栈的起始地址,具体值根据实际情况而定 */
这个地址指向的是堆栈的起始位置,即堆栈的最高地址。在 Reset_Handler
函数中,会将这个地址加载到堆栈指针寄存器 MSP
(Main Stack Pointer)中,从而初始化堆栈。这个过程通常在 SystemInit
函数中完成。
总的来说,Reset_Handler
负责系统的初始化,包括设置堆栈和调用主函数,而 __initial_sp
是堆栈的初始地址。这两者通常在启动文件(startup 文件)中定义和使用。