运行内存、FLASH(闪存)分配在远程升级开发过程中尤为重要,内存分配不严谨可能会导致代码出现各种异常(跑飞、无法运行、运行不受控的代码段)。
什么是SRAM(运行内存)、FLASH(闪存)?
(1)FLASH(闪存):Flash 存储器是一种非易失性存储器,用于存储程序代码和常量数据。在 STM32 微控制器中,Flash 存储器通常用于存储程序代码,它是在编译并烧录程序后被写入的。Flash 存储器的内容在断电后通常会保持不变,因此适用于存储固化的程序。
(2)SRAM(运行内存):运行内存通常指的是随机存储器(RAM),这种存储器用于临时存储程序执行过程中产生的数据、堆栈信息以及其他临时变量。在 STM32 微控制器中,RAM 存储器用于存储程序执行时需要修改或访问的数据,它的内容在断电后会丢失。
简单来说 FLASH(闪存)用来存代码的,掉电不会丢失。 SRAM(运行内存)是用来存放代码运行时所需的变量、中间结果和临时数据,掉电就丢失。
STM32的运行内存和我们电脑的运行内存功能大多是一致的,但还是有差别。在个人电脑(PC)中,代码通常是从硬盘(或其他非易失性存储设备,如SSD)加载到RAM(随机存取存储器)中执行的。这是因为RAM提供了比硬盘更快的数据访问速度,从而提高了程序运行的效率。STM32选择直接从Flash中读取代码执行,而将RAM用于存储运行时的变量和中间数据。
以STM32F407VET6为例内存参数如下:
- FLASH(闪存):512 KB。
- SRAM(运行内存):192 KB+4KB.
1、FLASH(闪存)
地址范围:0x08000000-0x0807FFFF
STM32F407VET6的FLASH(闪存)共分为8个扇区,每个扇区起始地址及空间大小分配情况如下,写操作前要先对flash进行擦除,擦除的最小单位为扇区。所以存储BOOT和APP的代码的FLASH空间要按扇区地址来分配。APP代码空间的起始地址要为扇区起始地址,flash的空间利用率最高且在boot更新APP代码时不容易出错。
我的BOOT代码大小为35K,且只用了一个APP,未做备份,FLASH闪存空间分配如下
扇区0-3作为BOOT代码空间 起始地址0x08000000 大小0x10000 即:64k
扇区4作为IAP标志存储空间 起始地址 0x08010000大小0x10000 即:64
扇区5-7作为APP代码空间 起始地址 0x08020000 大小0x60000 即:384k
(注:IAP标志一般存外部eeprom, 我此处是把内部FALSH当EEPROM使。根据自己实际情况配置!)
BOOT FLASH空间分配配置:
APP FLASH空间分配配置:
2、SRAM(运行内存)
- 系统SRAM:从地址
0x20000000
到0x20030000
,总共 192 KB(112KB+16KB+64KB)。 - 备份SRAM: 起始地址 0x40024000-0x40025000,总共4KB
2.1 STM32F4XX中文手册关于SRAM的描述如下:
通过上图 关于STM32F407VET6 SRAM得出以下总结:
(1)112 KB + 16 KB的SRAM:
这部分SRAM映射在地址0x2000 0000-0x2002 0000,总共128KB,可供所有的AHB(Advanced High-performance Bus)主控总线访问。AHB主总线支持多个主控设备(如CPU、DMA控制器等)的并发访问,因此这部分SRAM可以用于存储需要被多个设备共享或同时访问的数据。128K分成两段的,一段是112K,一段是16K,这样做的好处是两块RAM有各自的地址总线,可以同时被访问。举个例子,你用网口往16K段里面写数据,而同时CPU可以正常访问112K段,不受影响。
(2)64 KB的CCM(Core Coupled Memory):
这部分SRAM映射在地址0x1000 0000-0x1001 0000,只能供CPU通过数据总线访问。CCM是直接挂在D-bus(数据总线)上的,因此CPU能以最大的系统时钟和最小的等待时间从CCM中读取数据或代码。CCM通常用于存储那些需要快速访问的数据,如中断处理程序、堆栈和局部变量等。
(3)STM32F407VET6 SRAM由128K常规SRAM+64K的CCM组成。
2.2、四种类型的数据占用内存情况
如下图:
(来源:STM32深入系列01——内存简述(Flash和SRAM)_stm32内存-CSDN博客)
通过以上几点可知
通过查看map文件 得知我的BOOT和APP编译后各类型大小如下:
BOOT:
Total RO Size (Code + RO Data) 36124 ( 35.28kB)
Total RW Size (RW Data + ZI Data) 25320 ( 24.73kB)
Total ROM Size (Code + RO Data + RW Data) 36488 ( 35.63kB)
APP:
Total RO Size (Code + RO Data) 250740 ( 244.86kB)
Total RW Size (RW Data + ZI Data) 95688 ( 93.45kB)
Total ROM Size (Code + RO Data + RW Data) 251204 ( 245.32kB)
其中RW Size (RW Data + ZI Data)即为占用的SRAM空间(在不使用动态内存的情况下)。
已知BOOT 占用SRAM 25KB ,APP占用SRAM 94KB 可用SRAM128KB,
分配28K SRAM 给BOOT, 分配100K SRAM给APP。
BOOT FLASH空间及SRAM分配配置:
APP FLASH空间及SRAM分配配置:
3、64K的CCM也是SRAM,为什么不参与运行内存分配?
64K的CCM是不支持DMA的,只能供CPU通过数据总线访问。如果APP中使用了DMA,接收DMA的缓存地址又是编译器编译时决定的,编译器不会主动规避这个风险,若使用了DMA又使用了CCM的话,编译器有可能将DMA接收地址编译给CCM就会造成异常,若是要使用DMA又要使用CCM得将DMA接收缓存的地址指定到常规的128K的SRAM里面去。
总结起来就是:
IRAM2 :0x10000000 0x10000 CCM只能由CPU访问,除非你知道你在干嘛,否则不要使能它。(多方查找资料得到的结论,没空去验证,欢迎各方道友验证后留言评论给予指正)。
4、栈顶地址合法性校验
在固件更新完成校验通过后需要BOOT跳转到APP,在跳转前就需要进行栈顶地址合法性校验.确保栈地址在STM32的sram范围内。首先要知道的是栈顶地址以小端模式存放在固件的前4个字节,如下图是我APP的BIN文件打开后,即栈顶地址为0x2001B9E8。在地址 0x20000000
到 0x20030000之间,则说明该栈顶地址合法。
代码中最直接的检查栈顶地址是否合法方法是
if ((*(vu32*)APPLICATION_ADDRESS) <= 0x20030000&&
(*(vu32*)APPLICATION_ADDRESS)>=0x20000000
)
高端一点把人看迷糊的方法:
if (((*(vu32*)APPLICATION_ADDRESS) & 0x2FFC0000) == 0x20000000)
。