OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 29|回复: 0

《ESP32-P4开发指南— V1.0》第十章 ESP32-P4存储器类型

[复制链接]

1212

主题

1226

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5221
金钱
5221
注册时间
2019-5-8
在线时间
1316 小时
发表于 昨天 09:18 | 显示全部楼层 |阅读模式
第十章 ESP32-P4存储器类型

1)实验平台:正点原子DNESP32P4开发板

2)章节摘自【正点原子】ESP32-P4开发指南— V1.0

3)购买链接:https://detail.tmall.com/item.htm?id=873309579825

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729


2.jpg

3.png

ESP32-P4芯片具备多种存储器类型及灵活的存储器映射特性。本章节将介绍ESP-IDF如何默认使用这些存储器功能,以优化应用程序性能和资源管理。
本章分为如下几个小节:
10.1 存储器类型概述
10.2 DMA 缓冲区的静态与堆栈内存分配


10.1 存储器类型概述
ESP-IDF将存储器分为指令总线和数据总线。指令总线包括IRAM(指令RAM)、IROM(从Flash中运行的代码)和RTC FAST内存,这些存储器是可执行的,并且只能通过4字节对齐的方式进行读取或写入。数据总线则由DRAM(数据RAM)和DROM(存储在Flash中的数据)组成,这些存储器不可执行,允许单独的字节操作。有关详细信息,请参考ESP32-P4技术参考手册中的“系统和存储器”部分。
1,DRAM(数据RAM)
DRAM用于存放非常量静态数据(.data段)和零初始化数据(.bss段)。链接器会将这些数据放置于内部SRAM中,且该区域的剩余空间可在程序运行时用作堆。使用EXT_RAM_BSS_ATTR宏可将零初始化数据(.bss段)放入外部RAM。要启用此功能,需确保配置了CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY选项,如下图所示。

第十章 ESP32557.png
图10.1.1 允许.bss段放置外部RAM

该配置选项在SDK配置编辑器→Component config→ESP PSRAM→PSRAM config→Allow .bss segment placed in external memory路径下定义。启用后,使用EXT_RAM_BSS_ATTR属性的变量将被放置在SPIRAM中。EXT_RAM_BSS_ATTR使用方法如下所示:
  1. #include "esp_attr.h"

  2. EXT_RAM_BSS_ATTR uint32_t external_bss_data = 0;

  3. /**
  4. * @brief       程序入口
  5. * [url=home.php?mod=space&uid=271674]@param[/url]       无
  6. * @retval      无
  7. */
  8. void app_main()
  9. {
  10.     /* 在此处可以使用external_bss_data */
  11. }
复制代码
这段代码声明了一个外部RAM变量external_bss_data,使用EXT_RAM_BSS_ATTR宏确保它在外部RAM中存储并初始化为零。
2,IRAM(指令 RAM)
IRAM是存放执行指令的内存区域。内部SRAM中未用于IRAM的部分将作为DRAM供静态数据和动态分配(堆)使用。
1)何时需要将代码放入IRAM
在特定情况下,将部分应用程序代码放入IRAM是有益的。首先,当注册中断处理程序时,如果使用了ESP_INTR_FLAG_IRAM,则该处理程序必须放置于IRAM中,以确保其快速响应。其次,对于时序关键的代码,放入IRAM可以显著减少从Flash加载代码所带来的延迟,从而提升函数性能,确保系统在执行关键任务时的高效性。
2)如何将代码放入IRAM
过链接器脚本,可以自动将特定代码放入IRAM。如果需要手动指定,可以使用IRAM_ATTR宏,如下代码所示:
  1. #include "esp_attr.h"

  2. void IRAM_ATTR gpio_isr_handler(void* arg)
  3. {
  4.     /* 及时相应代码 */
  5. }
复制代码
需要注意的是,将代码放入IRAM可能会引发安全中断处理程序的问题,因为字符串或常量可能不会自动放入RAM。在这种情况下,应使用DRAM_ATTR属性进行标记,以确保它们正确存储在RAM中,如下示例所示:
  1. void IRAM_ATTR gpio_isr_handler(void* arg)
  2. {
  3.    const static DRAM_ATTR uint8_t INDEX_DATA[] = { 45, 33, 12, 0 };
  4.    const static char *MSG = DRAM_STR("I am a string stored in RAM");
  5. }
复制代码
上述代码中,确定哪些数据需要标记为DRAM_ATTR可能比较困难。如果未标记,某些变量或表达式可能会被编译器识别为常量并放入Flash中。这是因为GCC优化会自动生成跳转表或switch/case查找表。为了解决此问题,可以在ESP-IDF编译时使用-fno-jump-tables -fno-tree-switch-conversion标志,以避免将这些表放入Flash。如果某个源文件不需要放置在IRAM中,可以为该文件重新启用跳转表优化,具体操作请参考乐鑫科技ESP-IDF在线编程指南的构建系统章节中的“组件 CMakeLists 文件”小节。
3,IROM(代码从 flash 中运行)
如果一个函数没有被显式地声明放在IRAM或RTC存储器中,则它将被放置在Flash中。由于IRAM空间有限,大部分应用程序的二进制代码需要放入IROM中。在启动过程中,从IRAM中运行的引导加载程序配置MMU以缓存Flash,将应用程序的指令代码区域映射到指令空间。通过MMU访问的Flash使用一些内部SRAM进行缓存,这使得访问缓存的Flash数据的速度与访问其他类型的内部存储器一样快。
4,DROM(数据存储在 flash 中)
默认情况下,链接器将常量数据放入一个映射到MMU Flash缓存的区域,这个区域与IROM相同,但用于只读数据而非可执行代码。唯一不默认放入DROM的常量数据是那些被编译器嵌入到应用程序代码中的字面常量,这些常量会被放置在周围函数的可执行指令中。
为了强制将常量从DROM放入DRAM(数据RAM)部分,可使用DRAM_ATTR属性,如上文所述。这有助于确保在需要时常量数据能够快速访问,避免因存放在Flash中而引起的延迟。
5,RTC FAST memory(RTC 快速存储器)
除非禁用CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP选项(见下图所示),否则剩余的RTC FAST内存会被添加到堆中。这部分内存可以与DRAM(数据RAM)互换使用,但访问速度略慢一些。这种灵活性允许开发者在内存资源有限的情况下优化应用程序的内存使用。


第十章 ESP322623.png
图10.1.2 启用RTC快速内存区域添加到系统堆

该配置选项允许将RTC快速内存区域添加到系统堆中,具备类似于DRAM区域的功能,但不支持DMA。在堆初始化过程中,早期启动服务和调度相关代码会优先使用这部分内存。从速度上看,RTC快速内存在APB时钟下运行,因此对性能影响不大。这种配置使得在内存紧张的情况下能够更有效地利用可用资源。该配置选项在SDK配置编辑器→Component config→ESP System Settings→Cache config→Enable RTC fast memory for dynamic allocations路径下定义。
6,紧密耦合内存(TCM)
TCM(紧耦合内存)是靠近CPU的内存,支持在CPU频率下直接访问,无需经过缓存。尽管在一般情况下,TCM的效率或速度相比缓存要低一些,但访问TCM的时间是可预测且始终一致的。这种稳定的访问速度对时间关键型例程尤为重要,因此,TCM在需要精准时序控制的任务中非常有用,可以确保执行的延迟和速度一致。

10.2 DMA 缓冲区的静态与堆栈内存分配
在上一节中我们详细讲解了ESP32-P4的存储器类型概述,介绍了不同存储器(如IRAM、DRAM、TCM等)的特点和用途。在本节中,我们将深入探讨与DMA传输密切相关的缓冲区内存管理问题。具体来说,我们将讨论如何在ESP32-P4上合理分配和使用DMA缓冲区,包括在静态内存和堆栈中放置DMA缓冲区的方式,以及相关的最佳实践和注意事项。通过合理的内存布局,可以有效提高DMA传输的性能和可靠性。
在ESP32开发中,许多DMA控制器(如SPI、SDMMC等)要求发送/接收缓冲区必须放置在DRAM中,并且按字对齐。为了确保DMA传输的稳定性和性能,我们建议将DMA缓冲区放在静态变量中,而不是堆栈中。
1,静态 DMA 缓冲区
静态变量在内存中有固定位置,适合用于DMA传输。可以使用DMA_ATTR宏声明全局或本地静态变量具备DMA能力。如下代码所示;
  1. <font size="3">DMA_ATTR uint8_t buffer[] = "I want to send something";</font>

  2. <font size="3">void app_main()</font>
  3. <font size="3">{</font>
  4. <font size="3">    /* 初始化代码 */</font>
  5. <font size="3">    spi_transaction_t temp = {</font>
  6. <font size="3">        .tx_buffer = buffer,</font>
  7. <font size="3">        .length = 8 * sizeof(buffer),</font>
  8. <font size="3">    };</font>
  9. <font size="3">    spi_device_transmit(spi, &temp);</font>
  10. <font size="3">    /* 其它程序 */</font>
  11. <font size="3">}</font>
复制代码
此外,如果需要动态分配DMA缓冲区内存,可以使用MALLOC_CAP_DMA标志来确保所分配的内存具备DMA能力。
2,在堆栈中放置 DMA 缓冲区
虽然可以在堆栈中放置DMA缓冲区,但一般建议避免这样做。如果确实需要在堆栈中使用DMA缓冲区,必须注意以下几点:
①:如果堆栈位于PSRAM中,不建议将DRAM缓冲区放置在堆栈中。这种情况下,任务堆栈在PSRAM中时,必须按照片外RAM(请看ESP-IDF编程指南的“片外 RAM”章节)的相关步骤进行特殊处理,以确保DMA传输的正确性和稳定性。
②:在函数中,使用WORD_ALIGNED_ATTR宏修饰变量,确保变量的内存对齐,从而使其可以被DMA正确访问,如下代码所示:
  1. <font size="3">void app_main()</font>
  2. <font size="3">{</font>
  3. <font size="3">uint8_t stuff;</font>
  4. <font size="3">/* 否则 buffer 会被存储在 stuff 变量后面 */</font>
  5. <font size="3">    WORD_ALIGNED_ATTR uint8_t buffer[] = "I want to send something";  </font>
  6. <font size="3">    /* 初始化代码</font>
  7. <font size="3">    spi_transaction_t temp = {</font>
  8. <font size="3">        .tx_buffer = buffer,</font>
  9. <font size="3">        .length = 8 * sizeof(buffer),</font>
  10. <font size="3">    };</font>
  11. <font size="3">    spi_device_transmit(spi, &temp);</font>
  12. <font size="3">    /* 其它程序 */</font>
  13. <font size="3">}</font>
复制代码
至此,ESP32-P4基于ESP-IDF的基础开发部分讲解到这里。在接下来的章节中,我们将深入探讨ESP32-P4各个外设的API及其使用方法,帮助读者更全面地掌握芯片的各项功能,进行高效开发。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-12-13 16:16

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表