[应用相关] Stm32 Flash 及 Ram 内存管理

[复制链接]
1335|27
Zhiniaocun 发表于 2025-9-6 18:41 | 显示全部楼层 |阅读模式
该文章的背景是我使用keil工程写了一个bootloader,给另外用stm32cubeide的app使用,原bootloader工程是已经验证过的方案,但在使用的过程中发现bootloader跳转后的sp地址不对,在解决这个问题的路上学到诸多内容,特此分享。

1.RAM 内存管理
首先了解RAM是如何分配的,下图可以清晰的展示内存分配

5767268bbb00c30b1d.png


简而言之分四段,.data 区存储的是初始化的全局变量和静态变量,.bss区存放的未初始化的全局变量和局部变量, user_head_stack 则是 堆和栈的地址,并且堆的地址一般在.bss地址的下一个字节,而栈的地址则在堆之后。这个我后续会在特别分析

2.FLASH 内存管理
flash内容要复杂一些,我这边分两张图来分析,第一张图 ROM size 即使Flash内存储的内容,

里面包括了 Code ,Ro Data, Rw Data,Code指的是代码区,Ro Data是定义的常量数据,Rw Data是初始化的全局变量,事实上还会有 inc.data 和 ZI Data,inc.data指的常量的字符串, 如定义的字符串“Hello Word”,这样的内容,而ZI Data不会实际全部存储在Flash内部,而是存储的ZI Data的地址,因为实际上ZI data的内容都是清零,所以只需要记录地址即可。

9095368bbb00526ef0.png


在stm32cubeide中的 Memory Details中描述了Flash的内存管理, 第一段是中断向量表,第二段是代码区,第三段是常量区,.init_array 和 .fini_array是连接链接脚本中的一个字段,在执行main之前,会遍历执行.init_array中的函数,.fini_array则会在main结束后执行。.data段是初始化的全局变量,.bss 和 ._user_head_stack 这边表达的比较特殊,看起来这部分内容存放在flash内,实际上只是存放了地址信息。

525568bbaffebd8c3.png


3.keil 和 stm32cubeide 对于堆栈的处理的不同
先看stm32cubeide设置的栈的地址,在ld文件中可以看到设置了_estack的地址未RAM的地址+RAM的长度,也就是RAM的最顶部,在startup.s文件中,第一个自己是栈地址,也就是_estack地址,因此stm32cubeide 默认设置的栈地址是RAM 的顶端,而堆的地址则在.bss后面stm32cubeide 里面的对于堆和栈的大小的定义为 _Min_Size ,这也是stm32cubeide对堆和栈的管理设计。

总结,stm32cubeide 栈指针在ram顶端,堆在.bss的后一个字节,cubemx中设置的head_size 和 stack_size 是最小的大小,实际大小为除了全局变量和静态变量以外的全部内存。

8182068bbaff7a3e5a.png


9263068bbafefd196c.png


keil 对堆和栈的管理类似,但是不同,keil对堆栈分配内存的时候则是按照实际的heap_size 以及 stack_size 分配,见下图

4611568bbafe968a31.jpg


4.stm32cubeide bootloader的问题
我们在写bootloader时有一个关键步骤 ,则是_set_msp,也就是设置sp的地址,在使用stm32cubeide 进行调试时,cubeide会从首地址设置sp的地址,如果bootloader和app都是用的stm32cubeide来写的,那不会有问题,因为栈顶指针都在ram顶部,但如果使用stm32cubeide来写app,并且用keil写bootloader,使用stm32cubeide调试时,会设置sp地址为keil计算出来的地址,此时就是容易出现栈溢出的情况,如何解决这种情况呢?

在Reset_Handler 中添加一行 以下内容,事实上,在f4,以及L4的芯片的Startup.s文件中,就是有这行的,只有在f1芯片的startup.s中没有这个,估计st公司也发现了这个问题,只是没修改f1的startup中的内容,因此闹了个乌龙。

        ldr sp, =_estack

4718668bbafddd3d66.png


————————————————
版权声明:本文为CSDN博主「qq_45019496」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45019496/article/details/150858870

jtracy3 发表于 2025-9-8 09:11 | 显示全部楼层
高速RAM,适用于中断频繁访问的数据或实时性要求高的任务栈。
claretttt 发表于 2025-9-8 10:02 | 显示全部楼层
将大常量(如 lookup 表)放入 Flash(用 const 修饰),避免占用 RAM
albertaabbot 发表于 2025-9-8 11:26 | 显示全部楼层
允许运行时动态调整分区大小,灵活性高但管理复杂。
nomomy 发表于 2025-9-8 14:26 | 显示全部楼层
存储函数调用上下文和局部变量,后进先出,需合理设置大小以防溢出。
sdCAD 发表于 2025-9-8 15:36 | 显示全部楼层
根据函数调用深度和局部变量大小调整栈空间
earlmax 发表于 2025-9-9 16:07 | 显示全部楼层
按扇区(Sector)或页(Page)划分(不同系列差异大,如 F1 系列分扇区,F4 系列分 Bank+Sector)。
写入前需先擦除(擦除单位为扇区 / 页),擦写次数有限(通常 10 万次以上)。
读取速度快(可直接执行代码),但写入速度较慢(需等待擦除和编程时间)。
sheflynn 发表于 2025-9-9 18:13 | 显示全部楼层
通过修改链接脚本(.ld 文件),将 Flash 划分为不同区域,避免代码与用户数据冲突
lzmm 发表于 2025-9-9 19:22 | 显示全部楼层
Flash 写入失败,检查地址是否在用户可访问区域、是否已解锁、电压是否稳定。
alvpeg 发表于 2025-9-10 10:00 | 显示全部楼层
​​Flash​​:用于固化数据,避免频繁擦写,合理划分扇区;

​​RAM​​:用于动态数据,优化堆/栈分配,减少碎片和泄漏;
benjaminka 发表于 2025-9-10 14:25 | 显示全部楼层
于存储函数调用时的局部变量和函数调用信息。栈的大小也在启动文件中定义。栈采用后进先出(LIFO)策略。
pentruman 发表于 2025-9-10 15:07 | 显示全部楼层
优先使用静态分配(全局 / 静态变量),避免频繁动态分配。
若需动态内存,使用固定大小内存池而非 malloc()。
robertesth 发表于 2025-9-10 15:45 | 显示全部楼层
通过调试工具查看 RAM 使用率(如 .data/.bss 占用)和栈 / 堆峰值,避免过度分配
timfordlare 发表于 2025-9-10 17:40 | 显示全部楼层
未使用const修饰大数组,导致RAM耗尽
sheflynn 发表于 2025-9-10 19:06 | 显示全部楼层
未初始化的全局变量和静态变量在RAM中也会分配空间,并在程序启动时初始化为0。
pmp 发表于 2025-9-10 20:18 | 显示全部楼层
存储程序代码、常量数据、配置参数 ,掉电不丢失。
sesefadou 发表于 2025-9-13 10:37 | 显示全部楼层
动态内存分配区域,通过malloc()/free()管理,需注意碎片化问题。
modesty3jonah 发表于 2025-9-13 14:15 | 显示全部楼层
Flash 存储代码、常量、掉电不丢失数据
linfelix 发表于 2025-9-13 16:36 | 显示全部楼层
避免频繁写入同一区域,延长芯片寿命。
robertesth 发表于 2025-9-13 16:58 | 显示全部楼层
通过内存保护单元限制对特定Flash区域的访问权限
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

260

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部