From fb81acb2060bde29c40acf66885eb54b301732d1 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Wed, 4 Mar 2026 10:58:19 +0800 Subject: [PATCH] fix(i80_controller): fix example crash when using filesystem - Replace SPIFFS with LittleFS as the preferred NOR flash file system - Expand the Image Resource section in README with detailed file system documentation - Add pytest_i80_controller_lvgl.py to test both image_in_bin and image_in_fs configurations - Increase LVGL task stack size from 4KB to 8KB to prevent stack overflow when loading images from FS - Add CONFIG_LV_USE_CLIB_MALLOC to sdkconfig.defaults Made-with: Cursor --- .../peripherals/lcd/i80_controller/README.md | 27 ++++++++++-- .../lcd/i80_controller/main/CMakeLists.txt | 5 +-- .../lcd/i80_controller/main/Kconfig.projbuild | 2 +- .../main/i80_controller_example_main.c | 28 ++++++------- .../lcd/i80_controller/main/idf_component.yml | 1 + .../lcd/i80_controller/main/lvgl_demo_ui.c | 4 +- .../partitions_lvgl_example.csv | 2 +- .../pytest_i80_controller_lvgl.py | 26 ++++++++++++ .../lcd/i80_controller/sdkconfig.defaults | 1 + .../i80_controller/sdkconfig.defaults.esp32 | 42 +++++++++---------- .../i80_controller/sdkconfig.defaults.esp32p4 | 1 + .../i80_controller/sdkconfig.defaults.esp32s2 | 40 +++++++++--------- .../i80_controller/sdkconfig.defaults.esp32s3 | 2 +- 13 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 examples/peripherals/lcd/i80_controller/pytest_i80_controller_lvgl.py diff --git a/examples/peripherals/lcd/i80_controller/README.md b/examples/peripherals/lcd/i80_controller/README.md index 34148ba271..ff71a08124 100644 --- a/examples/peripherals/lcd/i80_controller/README.md +++ b/examples/peripherals/lcd/i80_controller/README.md @@ -9,7 +9,7 @@ This example can be taken as a skeleton of porting the LVGL library onto the `es The whole porting code is located in [i80_controller_example_main.c](main/i80_controller_example_main.c), and the UI demo code is located in [lvgl_demo_ui.c](main/lvgl_demo_ui.c). -The UI will display two images (one Espressif logo and another Espressif text), which have been converted into C arrays by the [online converting tool](https://lvgl.io/tools/imageconverter), and will be compiled directly into application binary. +The UI will display two images (one Espressif logo and another Espressif text). You can choose to load images from a [LittleFS file system](https://github.com/joltwallet/esp_littlefs) or from embedded binary data. See [Image Resource](#image-resource) for more details. This example is constructed by [IDF component manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html), all the external dependency will be handled by the CMake build system automatically. In this case, it will help download the lvgl from the [ESP Component Registry](https://components.espressif.com/component/lvgl/lvgl), with the version specified in the [manifest file](main/idf_component.yml). @@ -97,10 +97,29 @@ I (638) example: Display LVGL animation ## Image Resource -This example supports two ways of reading images +This example supports two ways of loading images, which can be selected via `LCD image source from` in the menuconfig: -* from the [SPIFFS file system](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/spiffs.html). This is the suggested way we use in the example. It may take a little bit longer to load the image because of the bottleneck of the SPI flash read speed, but it will save the binary size. -* from the embedded binary (i.e., pre-decode the image into an array and pack it together with the application firmware). By this way, you can get faster image loading speed at the cost of bloating your application binary. What's worse, if you enabled the [XIP from PSRAM](https://github.com/espressif/esp-idf/tree/master/examples/system/xip_from_psram) feature, it will increase the PSRAM usage as well. +### File System (recommended) + +Load images from a [LittleFS file system](https://github.com/joltwallet/esp_littlefs). This approach saves binary size, though it may have slightly slower image loading due to SPI flash read speed. + +When this option is selected, the build system will: + +1. Use a custom partition table [partitions_lvgl_example.csv](partitions_lvgl_example.csv) that includes a `storage` partition for LittleFS. +2. Automatically generate a LittleFS image from the PNG files in [main/images/filesystem](main/images/filesystem) and flash it to the `storage` partition. + +At runtime, the application mounts the LittleFS partition at `/littlefs` and LVGL accesses the images via its POSIX FS interface (e.g., `S:/littlefs/esp_logo.png`, where `S` is the drive letter). + +The following LVGL features are enabled for file system support (see [sdkconfig.ci.image_in_fs](sdkconfig.ci.image_in_fs)): + +* `LV_USE_LODEPNG`: Decode PNG images at runtime. +* `LV_USE_FS_POSIX`: Enable POSIX file system interface in LVGL. +* `LV_FS_POSIX_LETTER`: Set to `83` (ASCII for `S`) as the drive letter. +* `PARTITION_TABLE_CUSTOM` : Use the custom partition table. + +### Embedded Binary (default) + +Pre-decode images into C arrays (via the [online converting tool](https://lvgl.io/tools/imageconverter)) and pack them together with the application firmware. This gives faster image loading speed at the cost of a larger application binary. If you have enabled the [XIP from PSRAM](https://github.com/espressif/esp-idf/tree/master/examples/system/xip_from_psram) feature, it will also increase the PSRAM usage. ## Troubleshooting diff --git a/examples/peripherals/lcd/i80_controller/main/CMakeLists.txt b/examples/peripherals/lcd/i80_controller/main/CMakeLists.txt index 4ff1a82f4e..dfb87f38da 100644 --- a/examples/peripherals/lcd/i80_controller/main/CMakeLists.txt +++ b/examples/peripherals/lcd/i80_controller/main/CMakeLists.txt @@ -4,10 +4,9 @@ if(CONFIG_EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY) endif() idf_component_register(SRCS "i80_controller_example_main.c" "lvgl_demo_ui.c" ${embedded_images} - PRIV_REQUIRES esp_lcd spiffs esp_psram + PRIV_REQUIRES esp_lcd esp_psram INCLUDE_DIRS ".") if(CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM) - # Create a partition to store the image resources in the filesystem - spiffs_create_partition_image(storage ./images/filesystem FLASH_IN_PROJECT) + littlefs_create_partition_image(storage ./images/filesystem FLASH_IN_PROJECT) endif() diff --git a/examples/peripherals/lcd/i80_controller/main/Kconfig.projbuild b/examples/peripherals/lcd/i80_controller/main/Kconfig.projbuild index 2fa16859b2..709d9c5057 100644 --- a/examples/peripherals/lcd/i80_controller/main/Kconfig.projbuild +++ b/examples/peripherals/lcd/i80_controller/main/Kconfig.projbuild @@ -3,7 +3,7 @@ menu "Example Configuration" config EXAMPLE_LCD_I80_COLOR_IN_PSRAM bool "Allocate color data from PSRAM" depends on SOC_PSRAM_DMA_CAPABLE - default y + default n help Enable this option if you want to allocate the LVGL draw buffer from PSRAM. diff --git a/examples/peripherals/lcd/i80_controller/main/i80_controller_example_main.c b/examples/peripherals/lcd/i80_controller/main/i80_controller_example_main.c index 8e5ca3b0b8..6418a94738 100644 --- a/examples/peripherals/lcd/i80_controller/main/i80_controller_example_main.c +++ b/examples/peripherals/lcd/i80_controller/main/i80_controller_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ @@ -17,7 +17,7 @@ #include "esp_lcd_panel_vendor.h" #include "esp_lcd_nt35510.h" #include "esp_lcd_panel_ops.h" -#include "esp_spiffs.h" +#include "esp_littlefs.h" #include "driver/gpio.h" #include "lvgl.h" @@ -72,7 +72,7 @@ static const char *TAG = "example"; #define EXAMPLE_LVGL_TICK_PERIOD_MS 2 #define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500 #define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1000 / CONFIG_FREERTOS_HZ -#define EXAMPLE_LVGL_TASK_STACK_SIZE (4 * 1024) +#define EXAMPLE_LVGL_TASK_STACK_SIZE (8 * 1024) #define EXAMPLE_LVGL_TASK_PRIORITY 2 #define EXAMPLE_LVGL_DRAW_BUF_LINES 100 @@ -130,33 +130,31 @@ static void example_lvgl_port_task(void *arg) void example_init_filesystem(void) { ESP_LOGI(TAG, "Initializing filesystem"); - esp_vfs_spiffs_conf_t conf = { - .base_path = "/spiffs", + esp_vfs_littlefs_conf_t conf = { + .base_path = "/littlefs", .partition_label = "storage", - .max_files = 5, - .format_if_mount_failed = true + .format_if_mount_failed = true, + .dont_mount = false, }; - // Use settings defined above to initialize and mount SPIFFS filesystem. - // Note: esp_vfs_spiffs_register is an all-in-one convenience function. - esp_err_t ret = esp_vfs_spiffs_register(&conf); + esp_err_t ret = esp_vfs_littlefs_register(&conf); if (ret != ESP_OK) { if (ret == ESP_FAIL) { ESP_LOGE(TAG, "Failed to mount or format filesystem"); } else if (ret == ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to find SPIFFS partition"); + ESP_LOGE(TAG, "Failed to find LittleFS partition"); } else { - ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + ESP_LOGE(TAG, "Failed to initialize LittleFS (%s)", esp_err_to_name(ret)); } return; } size_t total = 0, used = 0; - ret = esp_spiffs_info(conf.partition_label, &total, &used); + ret = esp_littlefs_info(conf.partition_label, &total, &used); if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret)); - esp_spiffs_format(conf.partition_label); + ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s). Formatting...", esp_err_to_name(ret)); + esp_littlefs_format(conf.partition_label); return; } else { ESP_LOGI(TAG, "Partition size: total: %zu, used: %zu", total, used); diff --git a/examples/peripherals/lcd/i80_controller/main/idf_component.yml b/examples/peripherals/lcd/i80_controller/main/idf_component.yml index a5fecbed35..53df8dd6c8 100644 --- a/examples/peripherals/lcd/i80_controller/main/idf_component.yml +++ b/examples/peripherals/lcd/i80_controller/main/idf_component.yml @@ -1,3 +1,4 @@ dependencies: lvgl/lvgl: "9.2.2" espressif/esp_lcd_nt35510: "^1.0.0" + joltwallet/littlefs: "~=1.20.0" diff --git a/examples/peripherals/lcd/i80_controller/main/lvgl_demo_ui.c b/examples/peripherals/lcd/i80_controller/main/lvgl_demo_ui.c index 6487a8ef09..3b8c9d79ab 100644 --- a/examples/peripherals/lcd/i80_controller/main/lvgl_demo_ui.c +++ b/examples/peripherals/lcd/i80_controller/main/lvgl_demo_ui.c @@ -59,7 +59,7 @@ static void anim_timer_cb(lv_timer_t *timer) // Create new image and make it transparent img_text = lv_image_create(scr); #if CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM - lv_image_set_src(img_text, "S:/spiffs/esp_text.png"); + lv_image_set_src(img_text, "S:/littlefs/esp_text.png"); #elif CONFIG_EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY lv_image_set_src(img_text, &esp_text); #endif @@ -123,7 +123,7 @@ void example_lvgl_demo_ui(lv_disp_t *disp) // Create image img_logo = lv_image_create(scr); #if CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM - lv_image_set_src(img_logo, "S:/spiffs/esp_logo.png"); + lv_image_set_src(img_logo, "S:/littlefs/esp_logo.png"); #elif CONFIG_EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY lv_image_set_src(img_logo, &esp_logo); #endif diff --git a/examples/peripherals/lcd/i80_controller/partitions_lvgl_example.csv b/examples/peripherals/lcd/i80_controller/partitions_lvgl_example.csv index 3141d59a5b..07e57195c5 100644 --- a/examples/peripherals/lcd/i80_controller/partitions_lvgl_example.csv +++ b/examples/peripherals/lcd/i80_controller/partitions_lvgl_example.csv @@ -3,4 +3,4 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 1M, -storage, data, spiffs, , 0xF0000, +storage, data, littlefs, , 0xF0000, diff --git a/examples/peripherals/lcd/i80_controller/pytest_i80_controller_lvgl.py b/examples/peripherals/lcd/i80_controller/pytest_i80_controller_lvgl.py new file mode 100644 index 0000000000..8bd31ec7d8 --- /dev/null +++ b/examples/peripherals/lcd/i80_controller/pytest_i80_controller_lvgl.py @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from pytest_embedded_idf.utils import soc_filtered_targets + + +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'image_in_bin', + 'image_in_fs', + ], + indirect=True, +) +@idf_parametrize('target', soc_filtered_targets('SOC_LCD_I80_SUPPORTED == 1'), indirect=['target']) +def test_i80_lcd_lvgl(dut: Dut) -> None: + dut.expect_exact('Calling app_main()') + dut.expect_exact('example: Initialize Intel 8080 bus') + dut.expect_exact('example: Initialize LVGL library') + dut.expect_exact('example: Install LVGL tick timer') + dut.expect_exact('example: Create LVGL task') + dut.expect_exact('example: Starting LVGL task') + dut.expect_exact('example: Display LVGL animation') diff --git a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults index ce198ca2d3..886760ce04 100644 --- a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults +++ b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults @@ -1,4 +1,5 @@ CONFIG_LV_CONF_SKIP=y +CONFIG_LV_USE_CLIB_MALLOC=y CONFIG_LV_USE_OBSERVER=y CONFIG_LV_USE_SYSMON=y CONFIG_LV_USE_PERF_MONITOR=y diff --git a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32 b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32 index 87cf2ae1b1..898af5c4f6 100644 --- a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32 +++ b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32 @@ -1,21 +1,21 @@ -CONFIG_EXAMPLE_PIN_NUM_PCLK=5 -CONFIG_EXAMPLE_PIN_NUM_CS=3 -CONFIG_EXAMPLE_PIN_NUM_DC=4 -CONFIG_EXAMPLE_PIN_NUM_RST=2 -CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=1 -CONFIG_EXAMPLE_PIN_NUM_DATA0=6 -CONFIG_EXAMPLE_PIN_NUM_DATA1=7 -CONFIG_EXAMPLE_PIN_NUM_DATA2=8 -CONFIG_EXAMPLE_PIN_NUM_DATA3=9 -CONFIG_EXAMPLE_PIN_NUM_DATA4=10 -CONFIG_EXAMPLE_PIN_NUM_DATA5=11 -CONFIG_EXAMPLE_PIN_NUM_DATA6=12 -CONFIG_EXAMPLE_PIN_NUM_DATA7=13 -CONFIG_EXAMPLE_PIN_NUM_DATA8=14 -CONFIG_EXAMPLE_PIN_NUM_DATA9=15 -CONFIG_EXAMPLE_PIN_NUM_DATA10=16 -CONFIG_EXAMPLE_PIN_NUM_DATA11=17 -CONFIG_EXAMPLE_PIN_NUM_DATA12=18 -CONFIG_EXAMPLE_PIN_NUM_DATA13=19 -CONFIG_EXAMPLE_PIN_NUM_DATA14=20 -CONFIG_EXAMPLE_PIN_NUM_DATA15=21 +CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=2 +CONFIG_EXAMPLE_PIN_NUM_RST=-1 +CONFIG_EXAMPLE_PIN_NUM_CS=4 +CONFIG_EXAMPLE_PIN_NUM_DC=5 +CONFIG_EXAMPLE_PIN_NUM_PCLK=18 +CONFIG_EXAMPLE_PIN_NUM_DATA0=19 +CONFIG_EXAMPLE_PIN_NUM_DATA1=21 +CONFIG_EXAMPLE_PIN_NUM_DATA2=0 +CONFIG_EXAMPLE_PIN_NUM_DATA3=22 +CONFIG_EXAMPLE_PIN_NUM_DATA4=23 +CONFIG_EXAMPLE_PIN_NUM_DATA5=33 +CONFIG_EXAMPLE_PIN_NUM_DATA6=32 +CONFIG_EXAMPLE_PIN_NUM_DATA7=27 +CONFIG_EXAMPLE_PIN_NUM_DATA8=12 +CONFIG_EXAMPLE_PIN_NUM_DATA9=13 +CONFIG_EXAMPLE_PIN_NUM_DATA10=14 +CONFIG_EXAMPLE_PIN_NUM_DATA11=15 +CONFIG_EXAMPLE_PIN_NUM_DATA12=26 +CONFIG_EXAMPLE_PIN_NUM_DATA13=25 +CONFIG_EXAMPLE_PIN_NUM_DATA14=16 +CONFIG_EXAMPLE_PIN_NUM_DATA15=17 diff --git a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32p4 b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32p4 index 7052c89f6c..d845bac990 100644 --- a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32p4 +++ b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32p4 @@ -2,6 +2,7 @@ CONFIG_SPIRAM=y CONFIG_SPIRAM_MODE_HEX=y CONFIG_SPIRAM_SPEED_200M=y CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM=y CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=48 CONFIG_EXAMPLE_PIN_NUM_RST=35 diff --git a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s2 b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s2 index 87cf2ae1b1..174a94c6c8 100644 --- a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s2 +++ b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s2 @@ -1,21 +1,21 @@ -CONFIG_EXAMPLE_PIN_NUM_PCLK=5 -CONFIG_EXAMPLE_PIN_NUM_CS=3 -CONFIG_EXAMPLE_PIN_NUM_DC=4 -CONFIG_EXAMPLE_PIN_NUM_RST=2 -CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=1 -CONFIG_EXAMPLE_PIN_NUM_DATA0=6 -CONFIG_EXAMPLE_PIN_NUM_DATA1=7 -CONFIG_EXAMPLE_PIN_NUM_DATA2=8 -CONFIG_EXAMPLE_PIN_NUM_DATA3=9 -CONFIG_EXAMPLE_PIN_NUM_DATA4=10 -CONFIG_EXAMPLE_PIN_NUM_DATA5=11 -CONFIG_EXAMPLE_PIN_NUM_DATA6=12 +CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=0 +CONFIG_EXAMPLE_PIN_NUM_RST=18 +CONFIG_EXAMPLE_PIN_NUM_CS=19 +CONFIG_EXAMPLE_PIN_NUM_DC=38 +CONFIG_EXAMPLE_PIN_NUM_PCLK=33 +CONFIG_EXAMPLE_PIN_NUM_DATA0=1 +CONFIG_EXAMPLE_PIN_NUM_DATA1=10 +CONFIG_EXAMPLE_PIN_NUM_DATA2=2 +CONFIG_EXAMPLE_PIN_NUM_DATA3=11 +CONFIG_EXAMPLE_PIN_NUM_DATA4=3 +CONFIG_EXAMPLE_PIN_NUM_DATA5=12 +CONFIG_EXAMPLE_PIN_NUM_DATA6=4 CONFIG_EXAMPLE_PIN_NUM_DATA7=13 -CONFIG_EXAMPLE_PIN_NUM_DATA8=14 -CONFIG_EXAMPLE_PIN_NUM_DATA9=15 -CONFIG_EXAMPLE_PIN_NUM_DATA10=16 -CONFIG_EXAMPLE_PIN_NUM_DATA11=17 -CONFIG_EXAMPLE_PIN_NUM_DATA12=18 -CONFIG_EXAMPLE_PIN_NUM_DATA13=19 -CONFIG_EXAMPLE_PIN_NUM_DATA14=20 -CONFIG_EXAMPLE_PIN_NUM_DATA15=21 +CONFIG_EXAMPLE_PIN_NUM_DATA8=5 +CONFIG_EXAMPLE_PIN_NUM_DATA9=14 +CONFIG_EXAMPLE_PIN_NUM_DATA10=6 +CONFIG_EXAMPLE_PIN_NUM_DATA11=15 +CONFIG_EXAMPLE_PIN_NUM_DATA12=7 +CONFIG_EXAMPLE_PIN_NUM_DATA13=16 +CONFIG_EXAMPLE_PIN_NUM_DATA14=8 +CONFIG_EXAMPLE_PIN_NUM_DATA15=17 diff --git a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s3 b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s3 index b609ec124b..91cbdfddc1 100644 --- a/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s3 +++ b/examples/peripherals/lcd/i80_controller/sdkconfig.defaults.esp32s3 @@ -4,7 +4,7 @@ CONFIG_SPIRAM_SPEED_80M=y # Enabling the following configurations can help increase the PCLK frequency in the case when # the Frame Buffer is allocated from the PSRAM and fetched by EDMA CONFIG_SPIRAM_XIP_FROM_PSRAM=y - +CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM=y CONFIG_EXAMPLE_PIN_NUM_PCLK=2 CONFIG_EXAMPLE_PIN_NUM_CS=0