一、常见存储器介绍
FLASH又称为闪存,不仅具备电子可擦除可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据,U盘和MP3里用的就是这种存储器。在以前的嵌入式芯片中,存储设备一直使用ROM(EPROM),随着技术的进步,现在嵌入式中基本都是FLASH,用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘)。
RAM随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据。当机器电源关闭时,存于其中的数据就会丢失。比如电脑的内存条。
RAM有两大类,一种称为静态RAM(Static RAM/SRAM),SRAM速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲。另一种称为动态RAM(Dynamic RAM/DRAM),DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的。
二、STM32 的存储器映射分析
STM32存储器映射表(选用的是STM32F103VE的,不同的型号Flash 和 SRAM 的地址空间不同,起始地址都是一样的):
STM32 的内存管理主要就是对0X0800 0000 开始的 Flash 部分 和0x2000 0000 开始的 SRAM 部分使用管理
三、C/C++ 程序编译后的存储数据段
.data
数据段,储存已初始化且不为0的全局变量和静态变量(全局静态变量和局部静态变量)。
static声明的变量放在data段。需要在 RAM里面运行,但是起初需要保存在 Flash里面,程序运行后复制到 RAM里面运行,需要占用Flash空间
.BSS
Block Started by Symbol。储存未初始化的,或初始化为0的全局变量和静态变量。
BSS段属于静态内存分配,所以放在RAM里。
.text(CodeSegment/Text Segment)
代码段,储存程序代码。也就是存放CPU执行的机器指令(machineinstructions)。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
放在Flash里。
.constdata
储存只读常量。const修饰的常量,不管是在局部还是全局放在Flash 里。
所以为了节省 RAM,把常量的字符串,数据等 用const声明
heap(堆)
堆是用于存放进程运行中被动态分配的内存段。他的大小并不固定,可动态扩张或者缩减,由程序员使用malloc()和free()函数进行分配和释放。当调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
放在RAM里
其可用大小定义在启动文件startup_stm32fxx.s中。
stack(栈)
栈又称堆栈,是用户存放程序临时创建的局部变量,由系统自动分配和释放。可存放局部变量、函数的参数和返回值(但不包括static声明的变量,static意味着 放在 data 数据段中)。
四、单片机启动过程
- 设置堆栈指针 SP = _initial_sp
- 设置PC指针 = Reset_Handler
- 配置系统时钟SystemInit
- 配置外部 SRAM 用于程序变量等数据存储(可选)
- 调用C库的 _main 函数,最终调用main函数
五、start_xxx.s启动文件讲解
5.1 开辟栈空间和堆空间
代码的开始,就是开辟栈空间,用于局部变量,函数调用,函数参数等。
接下来是开辟堆空间,主要用于动态内存分配,使用malloc,calloc等函数分配的变量空间是在堆上的。
5.2 中断向量表部分
5.3 Reset_Handler 系统启动
系统上电或者复位后首先执行的代码就是复位中断服务函数 Reset_Handler:
图中的 Reset_Handler 中断服务函数使用了WEAK,说明在外部可以自定义Reset_Handler 函数
PROC、ENDP这一对伪指令把程序分为若干个过程,是程序结构更加清晰
_main 标号表示 C/C++标准实时库函数里的一个初始化子程序 _main的入口地址。该程序的一个主要作用是初始化堆栈(跳转_user_initial_stackheap标号进行初始化堆栈),并初始化映像文件,最后跳转到C程序中的main函数。这也正解释了为什么所有的C程序必须有一个main函数作为程序的起点,因为这是由C/C++标准实时库所规定的。