stm32-- 存储-flash和ram

server/2024/12/18 19:13:33/

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

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 BRW Data:            128 BZI 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 Datavoid 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_SizeAREA    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.ppmy.cn/server/151250.html

相关文章

【网络】传输层协议UDP/TCP网络层IP数据链路层MACNAT详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;计算机网络原理_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.传输层协议 UDP 1.1 传输层 1.2 端口号 1.3 UDP 协议 1.3.1 UDP 协议端格式 1.3.2 UDP 的特点 1.3.3 面向数据报 1…

【鸿蒙实战开发教程】HarmonyOS next开发网络请求封装(Api11Release)

前言 根据研究机构Counterpoint Research发布的最新数据&#xff0c;2024年第一季度&#xff0c;鸿蒙OS份额由去年一季度的8%上涨至17%&#xff0c;iOS份额则从20%下降至16%。 这意味着&#xff0c;华为鸿蒙OS在中国市场的份额超越苹果iOS&#xff0c;已成中国第二大操作系统。…

2024年华为OD机试真题-找终点-C++-OD统一考试(E卷)

最新华为OD机试考点合集:华为OD机试2024年真题题库(E卷+D卷+C卷)_华为od机试题库-CSDN博客 每一题都含有详细的解题思路和代码注释,精编c++、JAVA、Python三种语言解法。帮助每一位考生轻松、高效刷题。订阅后永久可看,持续跟新。 题目描述 给定一个正整数数组,设为…

Liquibase结合SpringBoot使用实现数据库管理

Liquibase概述 Liquibase 是一个开源的数据库变更管理工具&#xff0c;用于跟踪、版本化、和管理数据库结构&#xff08;如表、字段、索引等&#xff09;的变更。它的目的是使数据库变更的过程更加透明、可控制、自动化&#xff0c;避免开发团队在多个环境中手动执行相同的数据…

陕西科技大学《2024年807自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《陕西科技大学807自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

设计模式之 桥接模式 C# 范例

桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它将抽象与实现解耦&#xff0c;使得两者可以独立变化。桥接模式通常适用于以下情形&#xff1a; 类的功能可以在不同的维度上变化&#xff0c;例如在图形界面开发中&#xff0c;可能有不同的…

WPF Combox使用 Text无法选择正确获取CHange后的Text

使用固定ComboxItem 无法通过 selectitem as object 来进行回去到 Content内的对香数据。那我只能这个样干&#xff1a; private void CBPaiweiLeixingSelect_Change(object sender, SelectionChangedEventArgs e){ ComboBox ThisBox sender as ComboBox;List<EDaxiaosuixi…

RabbitMQ如何构建集群?

大家好&#xff0c;我是锋哥。今天分享关于【RabbitMQ如何构建集群&#xff1f;】面试题。希望对大家有帮助&#xff1b; RabbitMQ如何构建集群&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在RabbitMQ中&#xff0c;集群&#xff08;Cluster&#x…