diff --git a/examples/peripherals/lcd/i80_controller/README.md b/examples/peripherals/lcd/i80_controller/README.md index 34148ba271b..ff71a081247 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 4ff1a82f4e2..dfb87f38da6 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 2fa16859b2a..709d9c50577 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 8e5ca3b0b82..6418a947387 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 a5fecbed352..53df8dd6c85 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 6487a8ef09a..3b8c9d79aba 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 3141d59a5b8..07e57195c5a 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 00000000000..8bd31ec7d83 --- /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 ce198ca2d3d..886760ce048 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 87cf2ae1b11..898af5c4f68 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 7052c89f6c7..d845bac990a 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 87cf2ae1b11..174a94c6c8c 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 b609ec124bc..91cbdfddc10 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