STM32的Code/RO/RW/ZI区、Flash/Ram的占用情况、堆栈大小Stack_Size/Heap_Size的设置

以cortex-M3为例,例如STM32F103

 

这篇文章要讲2个问题:

1、编译出的程序(指令)、变量的存放位置、大小?
2、在代码和keil中,“堆、栈”两者的大小如何设置?

 

keil编译完成后,会有提示,形如:

Program Size: Code=1148 RO-data=424 RW-data=20 ZI-data=1636  

其中:
① Code为代码,本质上就是一大堆ARM指令;
② RO为只读的数据,例如,char *name = "TOM";//TOM三个字符就存放在ROM中作为RO-DATA;char cmd[] = "AT";AT两个字符会在RAM和FLASH中各有一份。有些常量会在RO区中,有些例如立即数,会直接被编译在code区中。
③ RW为非0初始化的全局和静态变量占用的RAM大小.注意:同时还要占用等量的ROM大小用于存放这些非0变量的初值;
④ ZI(zero initialize)为0初始化的内存区的大小(该区域3个用途:0初始化的全局和静态变量+堆区+栈区)。

下面是keil自动生成的.map文件中的信息:

Flash的占用量就是上图中ROM Size的大小,它包含了①+②+③的大小【ARM指令代码+只读数据+非0初始化变量的初值】
RAM的占用量包含上述③+④的大小,也即【非0初始化变量、0初始化RAM(又分为0初始化静态变量区+堆区+栈区)】

 

我们用jflash或者其他软件打开hex文件,看一下前四个字节:

0x2000678-0x2000000=0x678=1656,而观察一下前文编译出的信息,正好1656 = RW-data(1636) +  ZI-data(20)的大小,这就证明了RAM的大小确实=RW+ZI的大小。

注:hex文件的前4个字节为主堆栈指针MSP的初值。

 

 

在STM32的启动文件(.s汇编文件)中,一开始我们会看到 Stack_Size、Heap_Size 这两个汇编宏定义:

 

Stack指的是栈,栈是由堆栈指针MSP/PSP自动管理的,理论上初始化时给堆栈指针赋值为RAM的最高地址即可,但是用keil编译时,keil并不是按RAM的最高地址生成的HEX前4字节(也即MSP的初值),而是按照:全局和静态变量的容量+Heap_Size+Stack_Size三者之和,作为HEX文件的前4字节。

设置某块芯片的RAM的总大小,是在keil的这个地方设的:

其实讲道理,我觉得keil还是把这个值+RAM起始地址(0x2000000)作为MSP的初值更好,只不过keil不是这么做的,keil仅仅是把这个值用作编译检测:检查全局和局部静态变量(含0初始化和非0初始化两部分)所占的空间+堆区+栈区,它们所占空间之和是否超过了上图红框里的这个值,如果超了就编译报错。这就是上面红框里的值唯一的作用。

根据这段理论,我们也就知道了启动文件中Stack_Size该设为多少了,只要【全局变量+局部静态变量+Heap_Size+Stack_Size】的大小不超过芯片的RAM容量即可。
keil编译时,会把C库函数支持的malloc、free所需的内存指向heap内存区,并且当我们不断的malloc吃内存堆时,malloc函数会检测程序已吃掉的内存堆是否超过了Heap_Size,超了的话就会返回NULL。
keil是这样为【全局变量 + 局部静态变量 + Heap_Size + Stack_Size】分配内存的:

上面论述的是,使用C库函数提供的mallloc时,内存堆heap的使用情形,如果我们不打算使用C库提供的动态内存分配,而打算使用自己写的内存堆管理程序,甚至干脆不打算使用动态内配功能,这些情况下,请直接把Heap_Size设为0。

 

最后总结一下Stack_Size的设置方法:①在keil中正确设置芯片的RAM大小, ②确定堆区Heap_Size的大小,这个值可以通过调试自己估计出来,如果不使用C库的malloc的话,直接设为0,③把Stack_Size设的越大越好,只要编译不报错,就把Stack_Size往尽可能大了配,否则如上图所示的内存空闲区,在整个程序运行期间,将不被使用,纯属浪费,不如让其作为栈,以增强程序稳定性。

 

PS:自己写的动态内存管理程序,本质上就是申请一个大数组,自己管理这个数组而已,相当于自己写一套malloc/free函数,很多操作系统中有这样的例子,比如freeRtos、ucos等,这些操作系统都提供了好多种动态内存管理方法,最简单的一种,跟切火腿似的,malloc出来之后就释放不掉了,直到把这个大数组切完拉倒,看似很low的内存管理方法,实际上在很多小型项目上却用起来很方便。更好的内存管理程序,那自然是支持动态切,还要支持把动态释放的空间合并起来,以便把刚才合并起来的空间再次malloc,这些更好的内存管理程序,用起来更方便,但是同时,这种功能强大的内存管理程序自身也会占掉很多RAM / ROM / CPU资源,矛盾利弊共存。到底要使用简单的内存管理还是高级的内存管理,要看项目需求,一般嵌入式操作系统会提供4种以上的内存管理程序供我们选用。

 

 

<think>好的,用户问的是STM32F407ZGT6的Stack_SizeHeap_Size最大可以设置多少。首先,我需要确定这个芯片的内存结构。STM32F407ZGT6有192KB的RAM,分为几个不同的域。用户可能是在进行嵌入式开发,遇到了堆栈溢出的问题,或者想要优化内存分配。 接下来,要明确StackHeap在内存中的位置。通常,堆栈RAM的顶部向下增长,而堆从RAM的底部向上增长。两者的最大设置受限于可用RAM的总大小以及它们之间是否有其他数据段,比如全局变量或静态变量占用的空间。 用户可能没有考虑到其他变量占用的内存,所以需要提醒他们实际可用空间可能会更小。比如,如果程序中有大量的全局变量,那么StackHeap大小就需要相应减少,否则会发生内存重叠,导致程序崩溃。 另外,堆栈大小设置也取决于具体的应用需求。如果使用实时操作系统或者有大量的递归调用,可能需要更大的栈空间。而堆的大小则取决于动态内存分配的需求,比如频繁使用malloc或free的情况。 还需要注意编译器的默认设置,通常启动文件里会有默认的StackHeap值,用户可能在修改这些值的时候遇到问题。需要指导他们如何正确修改链接脚本或启动文件,以确保设置生效。 此外,用户可能对堆栈溢出的调试不太熟悉,可以建议他们使用调试工具来监控堆栈的使用情况,比如通过填充特定模式(如0xDEADBEEF)并在运行时检查是否被修改,从而判断是否有溢出发生。 最后,要提醒用户,虽然理论上最大可以设置到接近整个RAM大小,但实际项目中必须留出足够的空间给其他数据,否则会导致系统不稳定。建议他们根据实际应用需求,逐步调整并测试,找到合适的值。</think>以下是关于 STM32F407ZGT6 的 `Stack_Size` 和 `Heap_Size` 设置的详细说明: --- ### **1. 硬件基础** - **STM32F407ZGT6** 的主要内存参数: - **Flash**: 1MB - **RAM**: 192KB(分为 `112KB` 主 RAM + `64KB` CCM RAM + `16KB` 备份 RAM) - **主 RAM**: `0x20000000` ~ `0x2001BFFF`(112KB) - **CCM RAM**: `0x10000000` ~ `0x1000FFFF`(64KB,仅 CPU 可访问,不可用于 DMA) - **备份 RAM**: `0x40024000` ~ `0x400243FF`(4KB,需特殊配置) > **注意**: > - **堆(Heap)** 和 **栈(Stack)** 默认从主 RAM 分配。 > - CCM RAM 需手动指定(例如通过分散加载文件),备份 RAM 通常用于低功耗模式数据保留。 --- ### **2. Stack_SizeHeap_Size 的理论上限** #### **(1) 最大理论值** - **若仅使用主 RAM(112KB)**: - `Stack_Size` 和 `Heap_Size` 的总和 ≤ 112KB。 - 例如: $$Stack\_Size = 64KB,Heap\_Size = 48KB$$ - **若使用主 RAM + CCM RAM(112KB + 64KB = 176KB)**: - 需通过分散加载文件将堆/栈分配到 CCM RAM。 - 此时总和 ≤ 176KB。 #### **(2) 实际限制** - **全局变量和静态变量**: 代码中的全局变量和静态变量会占用 RAM,需从总 RAM 中扣除。 - 例如:若全局变量占用 20KB,则可用堆栈空间为: $$112KB - 20KB = 92KB$$ - **中断嵌套和递归调用**: 栈空间需满足最大中断嵌套深度和函数调用链需求,通常需预留至少 2KB~8KB。 --- ### **3. 设置方法** #### **(1) 在启动文件(startup_stm32f407xx.s)中修改** 默认配置通常为: ```assembly Stack_Size EQU 0x00001000 ; 4KB 栈 Heap_Size EQU 0x00000800 ; 2KB 堆 ``` 修改示例(以最大化为目标): ```assembly Stack_Size EQU 0x0000C000 ; 48KB 栈 Heap_Size EQU 0x00004000 ; 16KB 堆 ``` #### **(2) 分散加载文件(.sct)配置(高级)** 若需使用 CCM RAM 扩展堆/栈: ```scatterload LR_IROM1 0x08000000 0x00100000 { ; Flash ER_IROM1 0x08000000 0x00100000 { *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x0001C000 { ; 主 RAM(112KB) .ANY (+RW +ZI) *(.stack) ; 栈分配在主 RAM } RW_IRAM2 0x10000000 0x00010000 { ; CCM RAM(64KB) *(.heap) ; 堆分配到 CCM RAM } } ``` --- ### **4. 验证与调试** 1. **编译后查看 map 文件**: - 检查 `Stack` 和 `Heap` 的地址范围是否与其他段无重叠。 - 确认总 RAM 使用量不超过芯片容量。 2. **运行时监控**: - 使用调试工具(如 STM32CubeIDE)监测栈溢出(例如通过填充 `0xDEADBEEF` 模式)。 - 动态内存分配失败时,需增大 `Heap_Size`。 --- ### **5. 推荐值** - **通用场景**: $$Stack\_Size = 4KB \sim 8KB,Heap\_Size = 2KB \sim 4KB$$ - **复杂应用(RTOS、大量递归)**: $$Stack\_Size = 16KB \sim 32KB,Heap\_Size = 8KB \sim 16KB$$ --- ### **总结** - **理论最大值**:主 RAM堆栈总和 ≤ 112KB,结合 CCM RAM 可达 176KB。 - **实际值**需根据代码需求动态调整,避免全局变量和堆栈冲突。 - 调试阶段建议启用栈溢出检测功能。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值