当前位置: 首页 > article >正文

stm32-- 存储-flash和ram

本文中的程序都是伪代码,没经过验证使用。

keil编译完成,会得到一个.map文件,详细列出空间占用情况。

示例如下:

==============================================================================

    Total RO  Size (Code + RO Data)                39888 (  38.95kB)
    Total RW  Size (RW Data + ZI Data)              3456 (   3.38kB)
    Total ROM Size (Code + RO Data + RW Data)      40108 (  39.17kB)

==============================================================================
 

RW Data 和 ZI Data 是嵌入式系统中存储全局变量和静态变量的两种主要内存区域,它们分布在 RAM 中,但初始化方式和功能不同。


1. RW Data (Read-Write Data)

概念

RW Data 是 .data 段的另一种叫法,用于存储有初始值的全局变量和静态变量

存储位置
  • 初始值:存储在 Flash 中(程序的非易失性存储器)。
  • 运行时值:存储在 RAM 中。
特点
  • 程序启动时,初始值从 Flash 复制到 RAM。
  • RAM 中的值可以被读写和修改。
示例
int global_var = 10;        // 有初始值,存储在 RW Data
static int static_var = 20; // 有初始值,存储在 RW Data

2. ZI Data (Zero-Initialized Data)

概念

ZI Data 是 .bss 段的另一种叫法,用于存储未初始化或显式初始化为零的全局变量和静态变量

存储位置
  • 初始值:不存在于 Flash(因为它们是未初始化的)。
  • 运行时值:启动时,RAM 中这些变量的值被清零。
特点
  • 不占用 Flash 空间(没有初始值)。
  • 在程序启动时,启动代码负责将 RAM 中的区域清零。
示例
int global_var;             // 未初始化,存储在 ZI Data
static int static_var = 0;  // 显式初始化为零,存储在 ZI Data

3. RW Data + ZI Data

总和的意义

RW Data + ZI Data 表示整个程序运行时所需的 RAM 空间,用来存储全局变量和静态变量。这是嵌入式开发中一个非常重要的指标。

计算公式
RAM 使用量 = RW Data 大小 + ZI Data 大小 + 堆大小 + 栈大小

4. 如何查看 RW Data 和 ZI Data 大小

方法 1:查看编译输出

编译器通常会在编译日志或 Map 文件中显示 RW Data 和 ZI Data 的大小。例如,在 STM32CubeIDE 中:

Memory region         Used Size  Region Size
RAM                   1024 B     2048 B
  RW Data:            128 B
  ZI Data:            256 B
方法 2:通过 Map 文件

查看编译生成的 .map 文件:

.data          0x20000000    0x80  // RW Data,占用 128 字节
.bss           0x20000080    0x100 // ZI Data,占用 256 字节
方法 3:通过运行时计算

在代码中使用链接器定义的符号来计算:

extern uint32_t _sdata, _edata; // RW Data
extern uint32_t _sbss, _ebss;   // ZI Data

void check_memory_usage(void) {
    uint32_t rw_size = (uint32_t)&_edata - (uint32_t)&_sdata;
    uint32_t zi_size = (uint32_t)&_ebss - (uint32_t)&_sbss;

    printf("RW Data size: %lu bytes\n", rw_size);
    printf("ZI Data size: %lu bytes\n", zi_size);
}

5. 优化 RAM 使用

优化 RW Data
  • 减少初始化较大的全局变量。
  • 考虑是否可以动态分配内存,而非在编译时分配。
优化 ZI Data
  • 尽量避免声明未初始化的大数组。
  • 使用局部变量代替生命周期较短的全局或静态变量。

6. 总结

类型RW Data (.data)ZI Data (.bss)
存储位置Flash(初始值)+ RAM(运行时值)RAM
初始化从 Flash 复制初始值到 RAM启动时在 RAM 中清零
变量类型有初始值的全局和静态变量未初始化或初始化为零的全局变量

通过合理优化 RW Data 和 ZI Data,可以有效减少 RAM 的占用,提高嵌入式系统的运行效率。

堆栈使用的空间:

在 STM32 等嵌入式系统中,堆(Heap)和栈(Stack)都位于 RAM 中,但它们的分配方式和用途有所不同。以下是详细解释:


1. 堆和栈的定义

堆 (Heap)
  • 用途:用于动态内存分配,例如 malloc()free() 函数。
  • 特点
    • 堆空间在程序运行时动态分配和释放。
    • 堆的增长方向通常是从 低地址高地址 增长。
    • 如果动态分配不当,可能会导致 内存泄漏堆溢出
栈 (Stack)
  • 用途:存储函数调用的局部变量、函数返回地址、中断上下文等。
  • 特点
    • 栈是 LIFO(Last In, First Out)结构。
    • 栈的增长方向通常是从 高地址低地址 增长。
    • 如果栈过深,可能会导致 栈溢出

2. 堆和栈在内存中的位置

在 STM32 的 RAM 中,堆和栈的位置通常是通过 链接脚本(Linker Script)指定的。以下是一个常见的内存布局:

+--------------------+ <--- RAM 起始地址(低地址)
|     RW Data        | (全局和静态变量)
+--------------------+
|     ZI Data        | (未初始化的全局变量)
+--------------------+
|     Heap           | (动态分配内存)
+--------------------+
|     Free Space     | (未使用的 RAM)
+--------------------+
|     Stack          | (函数调用栈)
+--------------------+ <--- RAM 结束地址(高地址)
  • 堆从低地址向高地址增长
  • 栈从高地址向低地址增长
  • 如果堆和栈使用过多,可能会在 RAM 中相遇,导致 堆栈冲突

3. 如何设置堆和栈的大小

在链接脚本中设置

堆和栈的大小通常在链接脚本中指定。例如:

_estack = ORIGIN(RAM) + LENGTH(RAM);  /* 栈顶 */
_Heap_Limit = _estack - 0x800;        /* 栈大小 2KB */
_Stack_Limit = _Heap_Limit - 0x400;   /* 堆大小 1KB */
通过 CMSIS 配置

在 CMSIS 文件(如 startup_stm32.ssystem_stm32.c)中,常见的定义方式如下:

Stack_Size      EQU     0x00000400   ; 栈大小 (1KB)
Heap_Size       EQU     0x00000800   ; 堆大小 (2KB)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem        SPACE   Heap_Size

4. 查看堆栈的使用情况

栈的实际使用

栈的使用可以通过运行时检测。通常在启动代码中,栈区域会被填充为一个固定值(如 0xDEADBEEF),运行时检查剩余的栈空间即可:

extern uint32_t _estack;  // 栈顶地址
extern uint32_t _sstack;  // 栈底地址

void check_stack_usage(void) {
    uint32_t *stack_ptr = (uint32_t *)&_sstack;
    while (*stack_ptr == 0xDEADBEEF) {
        stack_ptr++;
    }
    uint32_t used_stack = (uint32_t)&_estack - (uint32_t)stack_ptr;
    printf("Used stack: %lu bytes\n", used_stack);
}
堆的实际使用

堆的使用可以通过记录 malloc()free() 的分配情况来估算:

#include <malloc.h>

void check_heap_usage(void) {
    struct mallinfo mi = mallinfo();
    printf("Heap used: %d bytes\n", mi.uordblks);  // 已分配的堆内存
    printf("Heap free: %d bytes\n", mi.fordblks);  // 剩余堆内存
}

5. 常见问题

堆栈冲突

当堆和栈在 RAM 中相遇时,可能会导致程序异常。这种情况称为 堆栈冲突。要避免冲突:

  • 减少堆和栈的使用。
  • 合理设置堆和栈的大小。
栈溢出

栈使用超过定义大小时,可能会覆盖其他 RAM 区域,引发不可预料的行为。解决方法:

  • 优化代码结构,减少函数嵌套深度。
  • 使用静态内存而非递归或大局部变量。

总结

  1. 堆和栈都位于 RAM 中

    • 堆:动态内存分配,增长方向从低地址向高地址。
    • 栈:函数调用栈,增长方向从高地址向低地址。
  2. 堆和栈大小由链接脚本或启动代码配置,需要根据应用需求合理分配。

  3. 监控堆栈使用可以帮助优化内存,避免冲突或溢出问题。

通过合理规划堆和栈,可以充分利用 RAM,确保嵌入式系统的稳定性和性能。

参考资料:

【Keil编译后查看ram和flash大小及占比】_keil如何看程序大小、-CSDN博客

【Keil5】Keil查看程序占用flash大小_keil怎么看程序大小-CSDN博客


http://www.kler.cn/a/440929.html

相关文章:

  • Hadoop概述
  • Linux实操篇-远程登录/Vim/开机重启
  • 【超详细实操内容】django的身份验证系统之User对象
  • 科技快讯 | 开源鸿蒙5.0版本即将亮相;英国测试AI摄像头抓酒驾;Kimi 发布视觉思考模型 k1
  • 单元测试知识总结
  • FFMPEG视频转图片
  • 【大模型微调学习6】-实战Hugging Face Transformers工具库
  • 太速科技-365-基于XC7Z045+AD9361的双收双发无线电射频板卡
  • ubuntu24.04、win11配置pysot
  • 代码随想录-算法训练营-番外(图论03:孤岛的总面积,沉没孤岛,水流问题,建造最大岛屿)
  • vue子组件在什么情况下会更新
  • 按键精灵苹果 iOS 脚本工具的基本编写方法
  • 【Prompt Engineering】5 文本转换
  • 3GPP协议解读_物理层系列(二)_RB SB SC什么关系?
  • 【代码随想录|动态规划02】
  • 【日期规则】EXCEl 自定义日期匹配规则,学习基础知识,自由匹配场景
  • vue+node+mysql8.0,详细步骤及报错解决方案
  • 【uni-app】微信小程序引入lime-echart并使用
  • 自动驾驶控制与规划——Project 2: 车辆横向控制
  • Python:跨文件实现字符串填充