ESP-IDFを使ってみる

PSRAM

ESP32のSoCにはSRAMが内蔵されていますが、520KBしかありません。
PSRAMがマウントされている開発ボードでは、PSRAMを疑似RAMとして使う事ができます。
PSRAMの詳細はこ ちらに公開されています。
PSRAMのサイズを調べるAPIにesp_psram_get_size()が有りますが、設定がちょっとトリッキーです。

まず、menuconfigを使ってSupport for external, SPI-connected RAMを有効にします。






これだけだと、PSRAMを持っていないESP32ではesp_psram_get_size()がHALTしてしまいます。
そこで、さらにIgnore PSRAM when not foundを有効にします。
この設定は、esp-idfのgithub issuesに書き込んでようやく分かりました。




これで、どのようなSoCでも、以下のコードでPSRAMの有無を判定できるようになります。
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_psram.h"

static const char *TAG = "MAIN";

void app_main()
{
    size_t psram_size = esp_psram_get_size();
    ESP_LOGI(TAG, "PSRAM size: %d bytes", psram_size);
    ESP_LOGI(TAG, "MALLOC_CAP_SPIRAM: %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
    ESP_LOGI(TAG, "Free heap size: %"PRIu32, esp_get_free_heap_size());
}

PSRAMを持っていないESP32ではesp_psram_get_size()から0が戻ります。
また使用できるHEAPのサイズも300Kぐらいです。
I (380) main_task: Started on CPU0
I (390) main_task: Calling app_main()
I (390) MAIN: PSRAM size: 0 bytes
I (390) MAIN: MALLOC_CAP_SPIRAM: 0 bytes
I (400) MAIN: Free heap size: 300964
I (400) main_task: Returned from app_main()

ESP-CAMなどのPSRAMを持っているESP32ではesp_psram_get_size()でPSRAMのサイズを知ることができま す。
使用できるHEAPのサイズも4400Kとメチャクチャ大きくなります。
I (1288) main_task: Started on CPU0
I (1298) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (1298) main_task: Calling app_main()
I (1308) MAIN: PSRAM size: 8388608 bytes
I (1308) MAIN: MALLOC_CAP_SPIRAM: 4191744 bytes
I (1318) MAIN: Free heap size: 4460248
I (1318) main_task: Returned from app_main()

PSRAMのサイズが8000K以上あるのに、HEAPサイズが4000Kしか増えない理由がこ ちらに書かれています。

On ESP32 only external SPI RAM under 4 MiB in size can be allocated this way.
To use the region above the 4 MiB limit, you can use the himem API.

ESP32 では、サイズが 4 MiB 未満の外部 SPI RAM のみをこの方法で割り当てることができます。
4 MiB 制限を超える領域を使用するには、himem APIを使用できます。

PSRAMの設定を何も行っていないsdkconfigと、PSRAMを有効にしたsdkconfigでは、これだけの差が有ります。
PSRAMを使う場合、これらの差分を全てsdkconfig.defaultに設定する必要が有ります。
$ diff sdkconfig sdkconfig.psram
3c3
< # Espressif IoT Development Framework (ESP-IDF)  Project Configuration
---
> # Espressif IoT Development Framework (ESP-IDF) 5.1.2 Project Configuration
665d664
< # CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set
666a666
> CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND=y
756c756,833
< # CONFIG_SPIRAM is not set
---
> CONFIG_SPIRAM=y
>
> #
> # SPI RAM config
> #
> CONFIG_SPIRAM_MODE_QUAD=y
> CONFIG_SPIRAM_TYPE_AUTO=y
> # CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set
> # CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
> # CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set
> CONFIG_SPIRAM_SPEED_40M=y
> CONFIG_SPIRAM_SPEED=40
> CONFIG_SPIRAM_BOOT_INIT=y
> CONFIG_SPIRAM_IGNORE_NOTFOUND=y
> # CONFIG_SPIRAM_USE_MEMMAP is not set
> # CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
> CONFIG_SPIRAM_USE_MALLOC=y
> CONFIG_SPIRAM_MEMTEST=y
> CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
> # CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set
> CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
> # CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
> # CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
> CONFIG_SPIRAM_CACHE_WORKAROUND=y
>
> #
> # SPIRAM cache workaround debugging
> #
> CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_MEMW=y
> # CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_DUPLDST is not set
> # CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_NOPS is not set
> # end of SPIRAM cache workaround debugging
>
> #
> # SPIRAM workaround libraries placement
> #
> CONFIG_SPIRAM_CACHE_LIBJMP_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBMATH_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBNUMPARSER_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBIO_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBTIME_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBCHAR_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBMEM_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBSTR_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBRAND_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBENV_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBFILE_IN_IRAM=y
> CONFIG_SPIRAM_CACHE_LIBMISC_IN_IRAM=y
> # end of SPIRAM workaround libraries placement
>
> CONFIG_SPIRAM_BANKSWITCH_ENABLE=y
> CONFIG_SPIRAM_BANKSWITCH_RESERVE=8
> # CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY is not set
>
> #
> # PSRAM clock and cs IO for ESP32-DOWD
> #
> CONFIG_D0WD_PSRAM_CLK_IO=17
> CONFIG_D0WD_PSRAM_CS_IO=16
> # end of PSRAM clock and cs IO for ESP32-DOWD
>
> #
> # PSRAM clock and cs IO for ESP32-D2WD
> #
> CONFIG_D2WD_PSRAM_CLK_IO=9
> CONFIG_D2WD_PSRAM_CS_IO=10
> # end of PSRAM clock and cs IO for ESP32-D2WD
>
> #
> # PSRAM clock and cs IO for ESP32-PICO-D4
> #
> CONFIG_PICO_PSRAM_CS_IO=10
> # end of PSRAM clock and cs IO for ESP32-PICO-D4
>
> # CONFIG_SPIRAM_CUSTOM_SPIWP_SD3_PIN is not set
> CONFIG_SPIRAM_SPIWP_SD3_PIN=7
> # CONFIG_SPIRAM_2T_MODE is not set
> # end of SPI RAM config
849a927
> CONFIG_ESP32_ECO3_CACHE_LOCK_FIX=y
884,887c962,965
< # CONFIG_ESP_WIFI_STATIC_TX_BUFFER is not set
< CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y
< CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
< CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32
---
> CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
> CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0
> CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=16
> CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32
896a975
> # CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
979a1059
> CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y
1247a1328
> # CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC is not set
1658d1738
< # CONFIG_ESP_SYSTEM_PD_FLASH is not set
1680,1681c1760,1762
< # CONFIG_SPIRAM_SUPPORT is not set
< # CONFIG_ESP32_SPIRAM_SUPPORT is not set
---
> CONFIG_SPIRAM_SUPPORT=y
> CONFIG_ESP32_SPIRAM_SUPPORT=y
> # CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST is not set
1738,1741c1819,1822
< # CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set
< CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y
< CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1
< CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32
---
> CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
> CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
> CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16
> CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32
1748a1830
> # CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set



PSRAMの設定はMenuconfigで行いますが、設定項目がESP32とESP32S3で違います。
こちらがESP32/ESP32S2の設定項目です。


こちらがESP32S3の設定項目です。


ESP32S3の一部のモデルは、SoC内にPSRAMを実装していて、Quad Mode PSRAM と Octal Mode PSRAM の2種類を使うことが出来るようになりました。
MenuconfigでPSRAMを有効にすると Quad Mode PSRAM がデフォルトとなりますが、一部のESP32S3は Octal Mode PSRAMを採用しているので、
これを変えないとPSRAMが有効になりません。
例えばESP32-S3-WROOM-1のN4R2/N8R2/N16R2はQuad Modeですが、N4R8/N8R8/N8R8/N16R8はOctal Modeです。
これはESP32-S3-WROOM-1のデータシートを見ないと分かりません。

Modeが有っていないと、以下の様にPSRAMのマウントに失敗し、PSRAMを認識しません。
I (171) cpu_start: Multicore app
E (172) quad_psram: PSRAM ID read error: 0x00ffffff, PSRAM chip not found or not supported, or wrong PSRAM line mode
E (175) esp_psram: PSRAM enabled but initialization failed. Bailing out.
I (183) cpu_start: Failed to init external RAM; continuing without it.


I (366) MAIN: PSRAM size: 0 bytes 0 Mbytes
I (366) MAIN: MALLOC_CAP_SPIRAM: 0 bytes
I (366) MAIN: Free heap size: 387728

Quad Mode/Octal Modeの変更は以下で行います。


Modeが正しいと、PARAMのマウントに成功します。
I (172) cpu_start: Multicore app

I (225) esp_psram: Found 8MB PSRAM device
I (230) esp_psram: Speed: 40MHz

I (1161) MAIN: PSRAM size: 8388608 bytes 8 Mbytes
I (1161) MAIN: MALLOC_CAP_SPIRAM: 8385720 bytes
I (1171) MAIN: Free heap size: 8739704

Modeを変更するとsdkconfigにはこれだけの差分が出ます。
$ diff sdkconfig_quad sdkconfig_octal
3c3
< # Espressif IoT Development Framework (ESP-IDF)  Project Configuration
---
> # Espressif IoT Development Framework (ESP-IDF) 5.1.2 Project Configuration
870,871c870,871
< CONFIG_SPIRAM_MODE_QUAD=y
< # CONFIG_SPIRAM_MODE_OCT is not set
---
> # CONFIG_SPIRAM_MODE_QUAD is not set
> CONFIG_SPIRAM_MODE_OCT=y
873,874d872
< # CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set
< # CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
881d878
< # CONFIG_SPIRAM_SPEED_120M is not set
894a892
> # CONFIG_SPIRAM_ECC_ENABLE is not set

ESP32CシリーズはPSRAMを使うことができません。


続く....