Tips and tricks

IDF projects are optimized for image size by default. These options tune an ESP-IDF project for LVGL speed, PSRAM usage, logging, and file system access.

Edit on GitHub

Improving LVGL speed of execution

IDF projects are generally configured to optimize the final application image for size. For some LVGL applications this is not desirable and results in poor execution speed.

In this case it is worth setting some of the IDF project-wide options in sdkconfig.defaults, such as:

ini
CONFIG_COMPILER_OPTIMIZATION_PERF=y

This compiles the application with performance as the priority, using SIMD instructions where possible. An increase of up to 30% in overall execution speed can be observed.

It is also possible to speed up the execution of LVGL's critical code by telling the compiler to put those sections in the IRAM area of ESP32 chips with the following option:

ini
CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y

The CPU can also be set to always run at its maximum speed with the CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_ option. The frequency varies from chip to chip; for example, the P4 family supports 360 MHz:

ini
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_360

and the ESP32/ESP32-S3 support 240 MHz:

ini
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240

Some of these options need the IDF experimental options to be enabled:

ini
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

Configuring the PSRAM on ESP32 supported devices

Some of the high-end ESP32 chips have external memory in their module: Pseudo-Static Random Access Memory, or PSRAM. Values from 4 to 16 MB are typical, and LVGL can use a portion of this memory to:

  • Copy read-only objects from flash to PSRAM to increase speed.
  • Use direct mode plus dual buffers even on an ESP32 that does not have a built-in display controller.

In both scenarios the result is less time spent flushing data to the display, resulting in higher frame rates. To enable PSRAM usage, set:

ini
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_USE=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_RODATA=y

These options can reside in the IDF project's sdkconfig.defaults.

Application crashes when enabling PPA

An application can start crashing after the CONFIG_LV_USE_PPA option is enabled. The typical symptom is a message on the monitor console indicating an error when the ESP32 calls the esp_msync function.

This happens because the PPA only accepts chunks of data that are aligned to the L1 cache line size, that is, 64 bytes. Even though the PPA draw unit handles the alignment of the source buffer, the target draw buffer area has to be aligned as well, otherwise the transfer from the PPA to it may fail. To prevent this, make CONFIG_LV_DRAW_BUF_ALIGN a multiple of the L1 cache line size, i.e. set it to 64 instead of the default 4.

ini
CONFIG_LV_DRAW_BUF_ALIGN=64

ESP32-P4 monitor log reports buffer underrun and frame-rate decreases

When PSRAM is enabled and the PPA is used, it is common to see frame rate degradation followed by a log message reporting that the display buffer will underrun. This happens because, depending on the IDF version, PSRAM was not enabled with the maximum supported speed.

To fix it, add the following option to sdkconfig.defaults:

ini
CONFIG_SPIRAM_SPEED_200M=y

Additionally, the PPA burst length can be increased to raise the memory bandwidth of a particular channel and gain speed in the drawing operations:

ini
CONFIG_LV_PPA_BURST_LENGTH=128

There is a downside to increasing the burst length: if another piece of ESP-IDF code uses a DMA2D channel that is shared with the PPA, the increase may slow down that channel's consumer. Also mind that the supported burst length values are 128, 64, 32, 16, and 8 bytes; any other value results in a build error.

Enabling LVGL logs on IDF project

LVGL logs are not enabled by default. To enable them, add the following options to sdkconfig.defaults:

ini
CONFIG_LV_USE_LOG=y
CONFIG_LV_LOG_LEVEL_INFO=y
CONFIG_LV_LOG_PRINTF=y

The logging subsystem of LVGL relies on printf being present in ESP-IDF.

Using the File System under ESP-IDF

ESP-IDF uses the standard C file operation functions (fopen, fread) in all its storage related APIs. This allows seamless interoperability with LVGL when enabling the LV_USE_FS_STDIO configuration. The process is described in detail below, using SPIFFS as a demonstration.

Decide what storage system you want to use

ESP-IDF has many ready-to-use examples, like SPIFFS, SD Card and LittleFS.

Enable LVGL's STDIO file system

You can use menuconfig:

  • Component config → LVGL configuration → 3rd Party Libraries: enable File system on top of stdio API
  • Then select Set an upper cased letter on which the drive will accessible and set it to 65 (ASCII A)
  • You can also set Default driver letter to 65 to skip the prefix in file paths.

Modify the partition table

The exact configuration depends on your flash size and existing partitions, but the new final result should look something like this:

NameTypeSubTypeOffsetSize
nvsdatanvs0x90000x6000
phy_initdataphy0xf0000x1000
factoryappfactory0x100001400k
storagedataspiffs400k

If you are not using a custom partition.csv yet, it can be added via menuconfig (Partition Table → Partition Table → Custom partition table CSV).

Apply changes to the build system

Some ESP file systems provide automatic generation from a host folder using CMake. The proper line(s) must be copied to main/CMakeLists.txt.

LittleFS has extra dependencies that should be added to main/idf_component.yml.

Prepare the image files

LVGL's LVGLImage.py Python tool can be used to convert images to binary pixel map files. It supports various formats and compression.

Meanwhile 3rd party libraries (like LodePNG and Tiny JPEG) allow using image files without conversion.

After preparing the files, they should be moved to the target device:

  • If properly activated, a SPIFFS file system based on the spiffs_image folder should be automatically generated and later flashed to the target
  • A similar mechanism for LittleFS uses the flash_data folder, but it's only available for Linux hosts
  • For the SD Card, a traditional file browser can be used

Invoke proper API calls in the application code

The core functionality requires only a few lines. The following example draws the image as well.

 
#include "esp_spiffs.h"

void lv_example_image_from_esp_fs(void)
{
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = false
    };

    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    if(ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to register SPIFF filesystem");
        return;
    }

    lv_obj_t * obj = lv_image_create(lv_screen_active());
    lv_image_set_src(obj, "A:/spiffs/logo.bin");
    lv_obj_center(obj);
}

Build and flash

After calling idf.py build flash the picture should be displayed on the screen.

Changes made by menuconfig are not being tracked in the repository if the sdkconfig file is added to .gitignore, which is the default for many ESP-IDF projects. To make your configuration permanent, add the following lines to sdkconfig.defaults:

ini
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_LV_USE_FS_STDIO=y
CONFIG_LV_FS_STDIO_LETTER=65
CONFIG_LV_FS_DEFAULT_DRIVER_LETTER=65

Last updated on

On this page