简介
在前几个esp32系列的帖子中, 我使用的是一块esp32s3-n4的开发板,它使用4MB的flash并没有psram. 为了这个帖子我又购买了一块esp32s3n8r8的模组来做psram的读写测试. 开发板如下图所示. 其他功能和之前的开发板一致.
PSRAM简介
PSRAM(Pseudo Static Random Access Memory)是一种伪静态随机存取存储器,结合了 DRAM 的高密度和 SRAM 的易用性。ESP32-S3 N8R8 内置了 8MB PSRAM,型号中的 "N8R8" 表示 8MB Flash 和 8MB PSRAM。支持高达 120MHz 的时钟频率,满足实时数据处理需求(官方推荐配置为80MHZ)。
本章节的主要内容为探讨如何配置和使用psram, 以及通过通用定时器,对psram进行读写测试. 要使用PSRAM首先我们需要在menuconfig中使能psram. 否则就算模组是n8r8, psram仍然处于不可用的状态.
首先使能PSRAM, 然后配置PSRAM的模式为8线PSRAM, 动态检测PSRAM大小, 以及默认的频率配置. 同时把所有的指令从flash移动到PSRAM中以及把所有的只读数据移动到psram中. 同时还可以选择PSRAM的访问方式. 我这里设置的是允许使用malloc函数来分配内存到psram里.
其他的数据保持默认,至此PSRAM的配置已经完成了. 接着将程序编译,然后烧录到开发板中.
我们可以通过监视的日志打印查看到PSRAM已经挂载和识别完毕. 大小为8000K, 即8MB(计量不同, 非1024 * 1024 * 8)
此时我们便可以使用malloc函数来分配内存从PSRAM里. 如下代码所示
size_t size_to_allocate = 1000 * 1000 * 8; void *external_ram_ptr = malloc(size_to_allocate); if (external_ram_ptr == NULL) { printf("Failed to allocate memory using malloc!\n"); return; }
但是只有内存的分配,我们并不清楚实际的PSRAM的读写速度. 我们可以借助ESP32的通用定时器来实现这个功能. 首先,我们引入通用定时器的头文件, 然后来初始化一个通用定时器的结构体.
gptimer_config_t timer_config = { .clk_src = GPTIMER_CLK_SRC_DEFAULT, .direction = GPTIMER_COUNT_UP, .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us }; ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
其中配置,使用默认的时钟源, 计时器为向上计数, 分辨率为 1MHZ 即每一个counter为1us. 这样的话我们只需要在分配完毕内存的时候,载向PSRAM读取之前,开启定时器, 然后写入之后读取一次定时器的值即为写入的耗时,然后在读取完毕之后再读取一次定时器的值. 使用第二次定时器的值减去第一次定时器的值即为读取的时间.
void psram_test() { ESP_ERROR_CHECK(gptimer_enable(gptimer)); ESP_ERROR_CHECK(gptimer_start(gptimer)); uint64_t start_count, end_count; // 1. 分配片外 RAM size_t size_to_allocate = 1000 * 1000 * 8; void *external_ram_ptr = malloc(size_to_allocate); if (external_ram_ptr == NULL) { printf("Failed to allocate memory using malloc!\n"); return; } printf("Successfully allocated %d bytes using malloc at address: %p\n", size_to_allocate, external_ram_ptr); // 2. 测量写入时间 ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &start_count)); int *data_array = (int *)external_ram_ptr; for (int i = 0; i < size_to_allocate / sizeof(int); i++) { data_array[i] = i; // 填充数据 } ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &end_count)); printf("Writing finished!\n"); printf("Time used for writing: %lld us\n", end_count - start_count); // 3. 测量读取时间 ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &start_count)); volatile int *volatile_data_array = (volatile int *)external_ram_ptr; for (int i = 0; i < size_to_allocate / sizeof(int); i++) { volatile int value = volatile_data_array[i]; // 读取数据 (void)value; // 防止编译器优化掉读取操作 } ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &end_count)); printf("Reading finished!\n"); printf("Time used for reading: %lld us\n", end_count - start_count); // 4. 释放内存 free(external_ram_ptr); printf("Memory freed.\n"); }
此时便可以准确的计算出PSRAM的操作时间, 如下图所示.
实际上可以看到读写速度还是超级快的, 8MB的PSRAM,写入的时间在237MS左右, 而读取的速度为300MS左右(不完全准确,但是已经可以看出来PSRAM的读写速度是非常快的了).
附件如下: