esp32idf 关于SPIRAM的使用

一、ESP32资源介绍

数据手册

从这里可以看到不同型号对应的FLASH、和SPIRAM的大小,我这里使用的是ESP32-S3-WROOM-1-N16R8 也就是FLASH 16M SPIRAM 8M 。

二、SPIRAM使用menuconfig进行配置

1、打开menuconfig

menuconfig
注意选择Mode (QUAD/OCT) of SPI RAM chip in use
这里面选的是 Octal Mode
在这里插入图片描述
这么选择以后保存配置,就自动初始化了,接下来我们做个例子查看一下对不对。

至于怎么调用spiram,看例子

三、代码示例

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_psram.h"
#include "esp_heap_caps.h"
#define TAG "AUDIO_QUEUE"
#define QUEUE_MAX_ITEMS 100

// 音频数据块结构体
typedef struct
{
    uint8_t data[1024];
    size_t len;
    uint32_t timestamp; // 添加时间戳用于处理
} audio_wav_chunk_t;

// 队列相关变量
QueueHandle_t audio_wav_queue = NULL;
StaticQueue_t queue_struct;
uint8_t *queue_buffer = NULL;
#include "esp_timer.h"

#include <math.h>
// 生成正弦波音频数据
void generate_sine_wave(uint8_t *buffer, size_t len, uint32_t counter)
{
    float frequency = 1.0f + (counter % 10) * 0.1f; // 频率变化
    float amplitude = 127.0f;
    float phase = counter * 0.1f;

    for (int i = 0; i < len; i++)
    {
        // 生成正弦波,幅度0-255,频率和相位变化
        float value = sinf(2.0f * M_PI * frequency * i / len + phase);
        buffer[i] = (uint8_t)(amplitude * value + amplitude + 0.5f);
    }
}
// 生产者任务:生成并发送音频数据到队列
void producer_task(void *pvParameters)
{
    audio_wav_chunk_t chunk;
    static uint32_t counter = 0;

    while (1)
    {
        // 生成模拟音频数据(这里用递增数字填充)
        generate_sine_wave(chunk.data, sizeof(chunk.data), counter);
        chunk.len = sizeof(chunk.data);
        chunk.timestamp = esp_timer_get_time(); // 获取当前时间戳

        // 发送数据到队列
        BaseType_t status = xQueueSendToBack(audio_wav_queue, &chunk, portMAX_DELAY);

        if (status == pdPASS)
        {
            ESP_LOGI(TAG, "Producer: Sent chunk #%ld, len=%d, time=%ldus",
                     counter, chunk.len, chunk.timestamp);
        }
        else
        {
            ESP_LOGE(TAG, "Producer: Failed to send data to queue");
        }

        counter++;
        vTaskDelay(pdMS_TO_TICKS(10)); // 每100ms生成一个数据块
    }
}

// 消费者任务:从队列读取并处理音频数据
void consumer_task(void *pvParameters)
{
    audio_wav_chunk_t received_chunk;
    uint32_t processed_count = 0;

    while (1)
    {
        // 从队列接收数据
        BaseType_t status = xQueueReceive(audio_wav_queue, &received_chunk, portMAX_DELAY);

        if (status == pdPASS)
        {
            // 处理音频数据(这里只是简单计算平均值作为示例)
            uint32_t sum = 0;
            for (int i = 0; i < received_chunk.len; i++)
            {
                sum += received_chunk.data[i];
            }
            float average = (float)sum / received_chunk.len;

            uint32_t current_time = esp_timer_get_time();
            uint32_t latency = current_time - received_chunk.timestamp;

            ESP_LOGI(TAG, "Consumer: Processed chunk #%ld, len=%d, avg=%.2f, latency=%ldus",
                     processed_count, received_chunk.len, average, latency);

            processed_count++;
        }
        else
        {
            ESP_LOGE(TAG, "Consumer: Failed to receive data from queue");
        }
    }
}

void app_main(void)
{
    // // 初始化PSRAM
    // if (esp_psram_init() != ESP_OK) {
    //     ESP_LOGE(TAG, "PSRAM initialization failed");
    //     return;
    // }

    // 打印内存信息
    ESP_LOGI(TAG, "Internal RAM free: %ld k bytes", esp_get_free_heap_size() / 1024);
    ESP_LOGI(TAG, "PSRAM free: %d k bytes", esp_psram_get_size() / 1024);

    // 在PSRAM中分配队列存储区
    queue_buffer = (uint8_t *)heap_caps_malloc(
        sizeof(audio_wav_chunk_t) * QUEUE_MAX_ITEMS,
        MALLOC_CAP_SPIRAM);

    if (queue_buffer == NULL)
    {
        ESP_LOGE(TAG, "Failed to allocate queue storage in PSRAM");
        return;
    }

    // 创建静态队列
    audio_wav_queue = xQueueCreateStatic(
        QUEUE_MAX_ITEMS,
        sizeof(audio_wav_chunk_t),
        queue_buffer,
        &queue_struct);

    if (audio_wav_queue == NULL)
    {
        ESP_LOGE(TAG, "Failed to create queue");
        heap_caps_free(queue_buffer);
        return;
    }

    ESP_LOGI(TAG, "Queue created successfully in PSRAM");

    // // 创建生产者和消费者任务
    // xTaskCreate(
    //     producer_task,
    //     "producer",
    //     4096,
    //     NULL,
    //     5,
    //     NULL);

    // xTaskCreate(
    //     consumer_task,
    //     "consumer",
    //     4096,
    //     NULL,
    //     5,
    //     NULL);

    // 主任务可以处理其他事务或进入低功耗模式
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

四、打印调试信息

I (27) boot: ESP-IDF v5.4.1-dirty 2nd stage bootloader
I (27) boot: compile time Jun 10 2025 09:42:51
I (27) boot: Multicore bootloader
I (28) boot: chip revision: v0.2
I (30) boot: efuse block revision: v1.3
I (34) boot.esp32s3: Boot SPI Speed : 80MHz
I (38) boot.esp32s3: SPI Mode       : DIO
I (42) boot.esp32s3: SPI Flash Size : 16MB
I (45) boot: Enabling RNG early entropy source...
I (50) boot: Partition Table:
I (52) boot: ## Label            Usage          Type ST Offset   Length
I (59) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (65) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (72) boot:  2 factory          factory app      00 00 00010000 00100000
I (78) boot: End of partition table
I (82) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=0a788h ( 42888) map
I (97) esp_image: segment 1: paddr=0001a7b0 vaddr=3fc92d00 size=02cdch ( 11484) load
I (99) esp_image: segment 2: paddr=0001d494 vaddr=40374000 size=02b84h ( 11140) load
I (106) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=18124h ( 98596) map
I (129) esp_image: segment 4: paddr=0003814c vaddr=40376b84 size=0c0c8h ( 49352) load
I (140) esp_image: segment 5: paddr=0004421c vaddr=600fe100 size=0001ch (    28) load
I (146) boot: Loaded app from partition at offset 0x10000
I (146) boot: Disabling RNG early entropy source...
I (158) octal_psram: vendor id    : 0x0d (AP)
I (158) octal_psram: dev id       : 0x02 (generation 3)
I (158) octal_psram: density      : 0x03 (64 Mbit)
I (160) octal_psram: good-die     : 0x01 (Pass)
I (164) octal_psram: Latency      : 0x01 (Fixed)
I (169) octal_psram: VCC          : 0x01 (3V)
I (173) octal_psram: SRF          : 0x01 (Fast Refresh)
I (178) octal_psram: BurstType    : 0x01 (Hybrid Wrap)
I (182) octal_psram: BurstLen     : 0x01 (32 Byte)
I (187) octal_psram: Readlatency  : 0x02 (10 cycles@Fixed)
I (192) octal_psram: DriveStrength: 0x00 (1/1)
I (196) esp_psram: Found 8MB PSRAM device
I (200) esp_psram: Speed: 40MHz
I (203) cpu_start: Multicore app
I (937) esp_psram: SPI SRAM memory test OK
I (946) cpu_start: Pro cpu start user code
I (946) cpu_start: cpu freq: 160000000 Hz
I (946) app_init: Application information:
I (946) app_init: Project name:     hello_world
I (950) app_init: App version:      1
I (954) app_init: Compile time:     Jun 10 2025 12:01:27
I (959) app_init: ELF file SHA256:  336c8e012...
I (963) app_init: ESP-IDF:          v5.4.1-dirty
I (967) efuse_init: Min chip rev:     v0.0
I (971) efuse_init: Max chip rev:     v0.99 
I (975) efuse_init: Chip rev:         v0.2
I (979) heap_init: Initializing. RAM available for dynamic allocation:
I (985) heap_init: At 3FC96338 len 000533D8 (332 KiB): RAM
I (991) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (996) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (1001) heap_init: At 600FE11C len 00001ECC (7 KiB): RTCRAM
I (1006) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator
I (1014) spi_flash: detected chip: gd
I (1016) spi_flash: flash io: dio
I (1019) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (1026) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (1033) main_task: Started on CPU0
I (1063) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (1063) main_task: Calling app_main()
I (1063) AUDIO_QUEUE: Internal RAM free: 8535 k bytes
I (1073) AUDIO_QUEUE: PSRAM free: 8192 k bytes
I (1073) AUDIO_QUEUE: Queue created successfully in PSRAM

五、总结

有了外部SPIRAM 我们可用的资源就很多了,在需要大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]。 --- ### 总结 综上所述,要成功部署并充分利用好附加型别的随机访问记忆体设备所提供的优势,就需要从软件层面做出相应的适配工作。主要包括正确初始化环境、合理规划布局以及灵活选用工具链所提供的一系列辅助手段等方面的努力成果体现出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值