ESP32内存结构

ESP32内存结构

本篇文章参考《ESP32内存结构》

文章目录

ESP32内存结构

    1. 内存分段
    1. ESP32 内存模型
      2.1 片内RAM
      2.1.1 片内RAM空间布局
      2.1.2 IRAM组织结构
      2.1.3 DRAM
      2.1.4 启用蓝牙之后的DRAM
      2.2 片外RAM
      2.2.1 PSRAM (也叫SPIRAM)
    1. ESP32 内存的使用
      3.1 IRAM
      3.4 DRAM
      3.7 DMA
    1. 堆的分配与调试
      4.1 相关API
      4.1.1 分配特定用途的内存
      4.1.2 在运行时查询DRAM剩余空间
      4.1.3 释放空间
      4.2 启动时查看内存使用情况
      4.2.1 DRAM
      4.2.2 IRAM
    1. 片外RAM的配置和使用

ESP32 内存模型

2.1 片内RAM

2.1.1 片内RAM空间布局

在这里插入图片描述
上图是ESP32内部存储器的布局,SRAM分为3个存储块SRAM0,SRAM1和SRAM2。(还有RTC快速和慢速存储器)

按照功能,SRAM可以分为两种:

  • IRAM: 存储指令的RAM,存储的是代码段(text段)。IRAM可以使用SRAM0和SRAM1

  • DRAM: 存储数据的RAM,主要用来存储BSS段,数据段,堆区和栈区的数据。DRAM可以使用SRAM1和SRAM2

  • 在这里插入图片描述
    虽然理论上,SRAM1可以用于存储IRAM和DRAM,但是实际上,RAM的默认分配情况是

  • IRAM:192KB,占用SRAM0

  • DRAM:328KB 占用SRAM1和SRAM2

但是值得注意的是,IRAM和DRAM的地址范围方向是相反的。
在这里插入图片描述

2.1.2 IRAM组织结构

在这里插入图片描述
接下来单独看看IRAM里面包括什么

ESP32 中 192 KB 的可用 IRAM 用于代码执行,并且其中一部分作为高速缓存(Cache)用于访问 Flash(和 PSRAM )。

  • 前 32KB IRAM 用作 CPU0 的高速缓存,接下来的 32KB 用作 CPU1 高速缓存。这是在硬件中静态配置的,无法更改。
  • 在第一个 64KB 之后,链接脚本开始将 text 段放置在 IRAM 中。它首先放置所有中断向量,然后放置已编译应用程序中所有标记为放置在 IRAM 中的 text 段。在通常情况下,大多数应用程序代码从 flash (XiP)执行,但某些代码对执行时间有较高要求,或者本身需要操作 flash,需要将它们放置在 IRAM 中。这项操作通过对这些函数或代码文件添加特定属性标识实现,链接程序脚本将据属性标识将它们放置在 IRAM 中。链接脚本将 _iram_text_start 和 _iram_text_end 符号放置在 text 段的两个边界处。
  • text 段之后的 IRAM 保持未使用状态,并添加到堆中。
      当应用程序配置为单核模式时,CPU1 不工作并且不启用 CPU1 Cache。在这种情况下,CPU1 Cache 的空间(0x40078000–0x4007FFFF)将被添加到堆中。

IRAM 也可以用于放置数据,但有两个重要限制条件:

用于访问 IRAM 中数据的地址必须是 32 位对齐的;
访问的数据大小也必须是 32 位对齐的。

2.1.3 DRAM

在这里插入图片描述
上图显示了应用程序的典型(简化)DRAM 布局。由于 DRAM 地址从 SRAM2 的末尾开始,并向后增加,因此链接阶段段空间的分配从 SRAM2 的末尾开始。

  • 前 8KB(0x3FFA_E000–0x3FFA_FFFF)用作某些 ROM 内置函数的数据空间;链接器紧接着将已初始化的数据段放在第一个 8KB 存储器之后;
  • 接下来是未初始化的 BSS 段;

数据段和 BSS 段之后剩余的内存被配置为堆,典型的动态内存分配一般分配至该位置。

2.1.4 启用蓝牙之后的DRAM

在这里插入图片描述
启用蓝牙(BT)功能后,** BT 控制器(软件和硬件)需要使用专用的数据空间 **。该空间作为控制器的 Data/BSS 段,同时作为传输空间用于 BT 数据包在软件和硬件之间传输。因此,链接脚本在默认的 DRAM 空间中保留了 0x3FFB_0000–0x3FFB_DB5C 之间的 54KB 空间,在该区域之后才进行应用程序的数据段和 BSS 段分配。

当应用程序仅使用低功耗蓝牙(BLE)功能时,可以将 BT 控制器内存的一部分交还给堆。释放并添加到堆中的内存大小约为 19KB。

2.2 片外RAM

2.2.1 PSRAM (也叫SPIRAM)

ESP32 提供了在 QSPI 总线上外接伪静态 RAM (PSRAM 又名 SPIRAM)的能力,该总线同时用于访问 flash,二者同时工作时利用片选信号进行切换。该存储器同 flash 一样可直接寻址,访问过程通过 IRAM 中的 Cache 进行。ESP32 在其地址空间 0x3F80_0000 至 0x3FBF_FFFF 最多可映射 4MB SPIRAM (译者注:新版本 IDF 可使用 Himem API 访问最大为 8 MB 的 SPIRAM)。应用程序通过三种方式使用 SPIRAM :

  • 使用 SPIRAM 保存特定软件模块的 BSS 段;

  • 使用堆分配器从 SPIRAM 动态分配内存;

  • 通过直接内存映射,在应用程序中使用静态地址访问 SPIRAM。
      虽然这允许应用程序使用额外的内存,但对 SPIRAM 的使用有以下限制:

  • SPIRAM 不支持 DMA,在需要使用 DMA 向/从外设传输数据的情况下,不能使用它;
    由于 flash 和 SPIRAM 使用同一 QSPI 总线与 ESP32 通信,因此在执行禁用 XiP 模式的代码中不能使用 SPIRAM;

  • 由于 SPIRAM 访问比内部 SRAM 慢,因此建议对性能有要求的代码使用内部 SRAM 保存数据。
    此处详细介绍了使用 SPIRAM 的这些方式以及使用限制。

3. ESP32 内存的使用

3.1 IRAM

ESP-IDF 将内部 SRAM0 区域(在技术参考手册中有定义)的一部分分配为指令 RAM。除了开始的 64kB 用作 PRO CPU 和 APP CPU 的高速缓存外,** 剩余内存区域(从 0x40080000 至 0x400A0000 )被用来存储应用程序中部分需要在RAM中运行的代码 **。

些 ESP-IDF 的组件和 WiFi 协议栈的部分代码通过链接脚本文件被存放到了这块内存区域。

如果一些应用程序的代码需要放在 IRAM 中运行,可以使用 IRAM_ATTR 宏定义进行声明。

#include "esp_attr.h"

void IRAM_ATTR gpio_isr_handler(void* arg)
{
    // ...
}

3.4 DRAM

链接器将非常量静态数据和零初始化数据放入 0x3FFB0000 — 0x3FFF0000 这 256kB 的区域。注意,如果使用蓝牙堆栈,此区域会减少 64kB(通过将起始地址移至 0x3FFC0000 )。如果使用了内存跟踪的功能,该区域的长度还要减少 16kB 或者 32kB。放置静态数据后,留在此区域中的剩余空间都用作运行时堆。

常量数据也可以放在 DRAM 中,例如,用在 ISR 中的常量数据(参见上面 IRAM 部分的介绍),为此需要使用 DRAM_ATTR 宏来声明。

DRAM_ATTR const char[] format_string = "%p %x";
char buffer[64];
sprintf(buffer, format_string, ptr, val);

3.7 DMA

大多数的 DMA 控制器(比如 SPI,SDMMC 等)都要求发送/接收缓冲区放在 DRAM 中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量中而不是堆栈中。使用 DMA_ATTR 宏可以声明该全局/本地的静态变量具备 DMA 能力,例如:

DMA_ATTR uint8_t buffer[]="I want to send something";

void app_main()
{
    // 初始化代码...
    spi_transaction_t temp = {
        .tx_buffer = buffer,
        .length = 8*sizeof(buffer),
    };
    spi_device_transmit( spi, &temp );
    // 其他程序
}

或者

void app_main()
{
    DMA_ATTR static uint8_t buffer[]="I want to send something";
    // 初始化代码...
    spi_transaction_t temp = {
        .tx_buffer = buffer,
        .length = 8*sizeof(buffer),
    };
    spi_device_transmit( spi, &temp );
    // 其他程序
}

在堆栈中放置 DMA 缓冲区仍然是允许的,但是你必须记住:

如果堆栈在 pSRAM 中,切勿尝试这么做,因为堆栈在 pSRAM 中的话就要按照menuconfig 中使能 SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY ),所以请确保你的任务不在 pSRAM 中。在函数中使用 WORD_ALIGNED_ATTR

4. 堆的分配与调试

4.2 启动时查看内存使用情况

4.2.1 DRAM

在启动时,可以通过idf.size 命令查看静态分配的内存。一般DRAM最大静态内存是160KB,剩余的只能作为堆区

同时,运行时DRAM大小可能比启动时小,因为运行时加载FreeRTOS也占用空间

4.2.2 IRAM

在启动时,可以通过idf.size查看IRAM使用情况

5. 片外RAM的配置和使用

具体可以看开头的参考文章

### 静态SPIRAM的使用方法与配置 ESP32支持外部连接的PSRAM(通常称为SPIRAM),用于扩展其内存容量。这种外接存储器可以通过静态分配的方式供应用程序使用。以下是关于如何配置和使用ESP32上的静态SPIRAM的相关说明。 #### 1. 启用SPIRAM支持 为了启用SPIRAM的支持,在项目的`menuconfig`中需要进行如下设置: - 进入 `Component config -> ESP-IDF specific -> SPI RAM configuration`。 - 将选项 **"Enable PSRAM support"** 设置为开启状态[^4]。 此操作会激活硬件驱动程序以管理外部PSRAM模块,并允许将其作为系统的堆空间的一部分来使用。 #### 2. 使用静态分配模式 默认情况下,ESP-IDF框架中的SPIRAM是以动态方式进行管理的,即通过标准C库函数如`malloc()`自动处理内存请求。然而如果希望采用更精确控制的方法,则可以考虑手动指定某些变量或者数据结构存放在SPIRAM上: 对于全局或静态变量来说,只需简单地加上属性修饰符即可实现特定位置的数据放置。例如下面的例子展示了怎样定义一个位于SPIRAM内的数组: ```c #include "esp_heap_caps.h" uint8_t my_data_in_psram[1024 * 64] __attribute__((section(".psram_bss"))); void init_my_data() { memset(my_data_in_psram, 0xFF, sizeof(my_data_in_psram)); } ``` 上述代码片段利用GCC编译器提供的特殊功能——将变量放入名为`.psram_bss`的部分当中,从而确保这些数据被映射至物理地址范围对应于已安装好的PSRAM芯片之上[^5]。 另外一种常见做法就是调用专门API接口完成类似目的的任务;比如创建一块固定大小且仅限于来自SPIRAM区域的新缓冲区: ```c size_t buffer_size = 1024; void* psram_buffer = heap_caps_malloc(buffer_size , MALLOC_CAP_SPIRAM); if(psram_buffer != NULL){ printf("Allocated %d bytes of PSRAM at address:%p\n",buffer_size,psram_buffer ); }else{ printf("Failed to allocate memory from PSRAM.\n"); } ``` 这里运用到了`heap_caps_malloc()`函数而不是普通的`malloc()`版本,因为它能够接受额外参数用来表明所期望获取资源的具体特性需求。在这里我们传递了标志位`MALLOC_CAP_SPIRAM`表示只寻找那些标记成可用于SPIRAM分配的位置[^6]。 #### 3. 调整链接脚本 当项目涉及到大量预知尺寸的大块连续性读写操作时,可能还需要进一步修改应用层面上面涉及到底层细节部分的内容。这包括但不限于调整目标平台专属定制化后的LD Script文件路径下的相应章节描述信息等等[^7]。 --- ### 总结 综上所述,要成功部署并充分利用好附加型别的随机访问记忆体设备所提供的优势,就需要从软件层面做出相应的适配工作。主要包括正确初始化环境、合理规划布局以及灵活选用工具链所提供的一系列辅助手段等方面的努力成果体现出来。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值