Files
esp-idf/examples/peripherals/lcd/i80_controller/README.md
morris 150466f598 refactor(lcd): use LZ4-compressed binary images in i80_controller example
- Remove the EXAMPLE_LCD_IMAGE_SOURCE Kconfig option (embedded binary vs
  file system) and always load images from LittleFS.
- Convert the PNG images into LZ4-compressed LVGL binary images (.bin)
at
  build time using LVGLImage.py, and flash them to a LittleFS partition.
- Upgrade lvgl to 9.6.0 and add the espressif/lz4 component; link the
external LZ4 library into lvgl.
- Split the example: main.c focuses on the esp_lcd i80 API, while the
LVGL
  porting (lvgl_port.c) and the UI (lvgl_demo_ui.c) live in separate
files.
- Rework the demo animation to use the LVGL animation engine (lv_anim)
with
  easing paths instead of a manual software timer for smoother
rendering.
- Remove the embedded C image arrays (esp_logo.c, esp_text.c) and the
  obsolete sdkconfig.ci.image_in_{bin,fs} variants.
2026-09-18 13:50:16 +08:00

9.0 KiB

Supported Targets ESP32 ESP32-P4 ESP32-S2 ESP32-S3 ESP32-S31

i80 LCD LVGL porting example

LVGL is an open-source graphics library for creating modern GUIs. It has plenty of built-in graphical elements with low memory footprint, which is friendly for embedded GUI applications.

This example can be taken as a skeleton of porting the LVGL library onto the esp_lcd driver layer. Note that, this example only focuses on the display interface, regardless of the input device driver.

The source files are split by responsibility:

  • i80_controller_example_main.c: demonstrates the esp_lcd i80 (Intel 8080) driver APIs, such as initializing the bus, the LCD panel and the backlight.
  • lvgl_port.c: the LVGL porting layer (display, flush callback, tick timer, LVGL task and LittleFS mounting).
  • lvgl_demo_ui.c: the demo UI and its animation.

The UI will display two images (one Espressif logo and another Espressif text). The images are stored as LZ4-compressed LVGL binary images on a LittleFS file system. See Image Resource for more details.

This example is constructed by IDF component manager, 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, with the version specified in the manifest file.

This example uses the esp_timer to generate the ticks needed by LVGL and uses a dedicated task to run the lv_timer_handler(). Since the LVGL APIs are not thread-safe, this example uses a mutex which be invoked before the call of lv_timer_handler() and released after it. The same mutex needs to be used in other tasks and threads around every LVGL (lv_...) related function call and code.

How to use the example

Hardware Required

  • An ESP development board
  • An Intel 8080 interfaced (so called MCU interface or parallel interface) LCD (this example uses ST7789)
  • An USB cable for power supply and programming

Hardware Connection

The connection between ESP Board and the LCD is as follows:

   ESP Board                      LCD Screen
+-------------+              +----------------+
|             |              |                |
|         3V3 +------------->| VCC            |
|             |              |                |
|         GND +--------------+ GND            |
|             |              |                |
|  DATA[0..7] |<------------>| DATA[0..7]     |
|             |              |                |
|        PCLK +------------->| PCLK (WR)      |
|             |              |                |
|          CS +------------->| CS             |
|             |              |                |
|         D/C +------------->| D/C            |
|             |              |                |
|         RST +------------->| RST            |
|             |              |                |
|    BK_LIGHT +------------->| BCKL           |
|             |              |                |
|         3V3 +------------->| RD             |
|             |              |                |
+-------------+              +----------------+

Important

The 8080 interface also has an RD pin. The RD line must be held high when idle. (Connect to 3V3 or use pull-up resistors)

Especially, please pay attention to the binary signal level used to turn the LCD backlight on, some LCD modules need a low level to turn it on, while others require a high level. You can change the backlight level macro EXAMPLE_LCD_BK_LIGHT_ON_LEVEL in i80_controller_example_main.c.

Build and Flash

Run idf.py set-target <target-name> to select one supported target that can run this example. This step will also apply the default Kconfig configurations into the sdkconfig file.

Run idf.py menuconfig to open a terminal UI where you can tune specific configuration for this example in the Example Configuration menu.

  • Allocate color data from PSRAM: Select this option if you want to allocate the LVGL draw buffers from PSRAM.
  • Pixel clock frequency (Hz): Set the pixel clock frequency for the LCD controller.
  • LCD GPIO Configuration: Select the GPIO number used by this example

Run idf.py -p PORT build flash monitor to build, flash and monitor the project. A fancy animation will show up on the LCD as expected.

The first time you run idf.py for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from the ESP Component Registry into managed_components folder.

(To exit the serial monitor, type Ctrl-].)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

Example Output

I (0) cpu_start: Starting scheduler on APP CPU.
I (418) example: Turn off LCD backlight
I (418) gpio: GPIO[2]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (428) example: Initialize Intel 8080 bus
I (438) example: Install LCD driver of st7789
I (558) example: Turn on LCD backlight
I (558) example: Initialize LVGL library
I (558) example: Register display driver to LVGL
I (558) example: Install LVGL tick timer
I (558) example: Create LVGL task
I (558) example: Starting LVGL task
I (638) example: Display LVGL animation

Image Resource

This example loads its images from a LittleFS file system, and stores them as LZ4-compressed LVGL binary images to reduce the on-flash size. At runtime LVGL decodes them with its built-in binary image decoder, which supports LZ4-decompressed images out of the box.

During the build, the LVGLImage.py tool shipped with the LVGL component is invoked to convert the source PNG files in main/assets into .bin images (e.g. esp_logo.bin, esp_text.bin), which are then packed into a LittleFS partition image and flashed to the storage partition:

python3 <lvgl>/scripts/LVGLImage.py --ofmt BIN --cf RGB565A8 --compress LZ4 -o build/main/images/binary logo.png

The build system will:

  1. Use a custom partition table that includes a storage partition for LittleFS.
  2. Convert each PNG in main/assets into an LZ4-compressed LVGL binary image under the build directory (see main/CMakeLists.txt), so the repository stays clean.
  3. Generate a LittleFS image from those .bin files and flash it to the storage partition.

At runtime, the application mounts the LittleFS partition at /littlefs and LVGL loads the images via its POSIX FS interface, e.g.:

lv_image_set_src(img, "S:/littlefs/esp_logo.bin");

where S is the LVGL POSIX FS drive letter.

The following features are enabled in sdkconfig.defaults:

  • 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.
  • LV_USE_LZ4_INTERNAL=n: Disable LVGL's bundled LZ4 copy so the external espressif/lz4 library is used for decompression. The external library is pulled automatically via the conditional dependency declared in lvgl/idf_component.yml.
  • LV_BIN_DECODER_RAM_LOAD: Required for compressed binary images. LVGL's bin decoder cannot stream LZ4 data line-by-line; it must decompress the whole image into RAM. If this option is off, lv_image_set_src() fails and the logo/text widgets stay empty.

The CMake configure step installs pypng and lz4 into the IDF Python environment if they are missing. These packages are only needed to run LVGLImage.py for this example.

Troubleshooting

  • Garbled or random display, or different image on each boot: If your LCD has an RD pin with no internal pull-up, it must be held high. You can directly connect RD to the 3V3 or use a pull-up resistor.

  • Can't get a stable UI when EXAMPLE_LCD_I80_COLOR_IN_PSRAM is enabled.

    This is because of the limited PSRAM bandwidth, compared to the internal SRAM. You can either decrease the PCLK clock EXAMPLE_LCD_PIXEL_CLOCK_HZ from the menuconfig or increase the PSRAM working frequency SPIRAM_SPEED from the KConfig (e.g. Component-config -> ESP PSRAM -> PSRAM config -> Set PSRAM clock speed) or decrease the FPS in LVGL configuration. In addition, enabling SPIRAM_XIP_FROM_PSRAM can help increase the PCLK frequency when the Frame Buffer is allocated from the PSRAM.

For any technical queries, please open an issue on GitHub. We will get back to you soon.