This project demonstrates how to run LVGL on a Raspberry Pi Pico (RP2040 or RP2040-compatible microcontroller) to drive an attached HUB75 RGB LED matrix panel.
This project currently demonstrates three animated demos:
- πΎ Bouncing Balls β includes circular horizontal scrolling text (15 sec)
- π₯ Fire Effect β Animated flame using procedural effect (15 sec)
- πΌοΈ Image Animation β Rotating static image for 360 degrees, then idle for 15 sec
β¨ Transitions (fade or slide) are applied between demos.
π‘ At reddit.com in the raspberrypipico group a video shows the listed demo effects. The video is titled "LVGL on Raspberry Pi Pico driving HUB75 RGB LED Matrix".
The LED matrix driver used in this project is an evolution of Pimoroni's HUB75 driver which leans on Raspberry Pi's pico-examples/pio/hub75. It is an optimised driver which boosts performance through self-paced, interlinked DMA and PIO processes. The LED Matrix driver implementation is described in detail in Hub75. In this referenced project the examples utilise Pimoronis Pico Graphics library to show the capabilities of the LED matrix driver. Pimoronis Pico Graphics library is a tiny graphics library ...
which supports drawing text, primitive and individual pixels and includes basic types such as Rect and Point brimming with methods to help you develop games and applications.
The goal of this project is to substitute Pimoronis Pico Graphics library with the Light and Versatile Graphics Library (LVGL), which claims ...
to be the most popular free and open-source embedded graphics library to create beautiful UIs for any MCU, MPU and display type.
- Controller: Raspberry Pi Pico (RP2040 or RP2040-compatible)
- Display: 64Γ64 HUB75 RGB LED matrix panel
β οΈ Other panel sizes can be supported with small adjustments - Power: External 5V supply for the LED matrix is required
+----------------------+ +----------------------+
| Core 0 | | Core 1 |
| | | |
| - LVGL | | - HUB75 Driver |
| - Demo Effects | | |
+----------------------+ +----------------------+
The HUB75 driver runs on core 1, utilizing PIO and DMA, freeing up core 0 for LVGL rendering and animation logic.
Currently LVGL version 9.4.0 is integrated in the project. If you want to switch to another version follow the steps described here:
-
Download the latest version of LVGL
-
Extract the zip and rename the folder to
lvgl -
Copy it into your project's top-level directory
-
Configure LVGL
- Copy
lv_conf_template.hto your top-level directory - Rename it to
lv_conf.h - Modify it to match your needs (use this project as reference)
Your directory structure should look similar to this
lvgl/ lv_conf.h other files and folders in your project, e.g. CMakeLists.txt - Copy
-
Add this snippet to your
CMakeLists.txt# LVGL configuration message(NOTICE "===>>> LVGL configuration start ===") set(LVGL_DIR_NAME lvgl) set(LVGL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(LV_CONF_PATH ${CMAKE_CURRENT_LIST_DIR}/lv_conf.h) set(LV_CONF_INCLUDE_SIMPLE ${CMAKE_CURRENT_LIST_DIR}/lv_conf.h) message(NOTICE "LVGL folder name: ${LVGL_DIR_NAME}") message(NOTICE "Path to LVGL folder: ${LVGL_DIR}") message(NOTICE "Path to config file: ${LV_CONF_PATH}") message(NOTICE "Include path: ${LV_CONF_INCLUDE_SIMPLE}") add_subdirectory(${LVGL_DIR_NAME}) message(NOTICE "=== LVGL configuration end <<<===")
See the projects
CMakeLists.txtfor details. -
Follow the LVGL Integration Guide
The steps below describe how LVGL is connected to the HUB75 driver in this project. This can be the basis for your modifications.
uint32_t get_milliseconds_since_boot()
{
critical_section_enter_blocking(&crit_sec);
uint32_t ms = to_ms_since_boot(get_absolute_time());
critical_section_exit(&crit_sec);
return ms;
}Connects LVGL's draw buffer to the HUB75 display. The parameter *area is not used as LGVL is directed to always pass the complete buffer of the display (see Choose LV_DISPLAY_RENDER_MODE_FULL).
void flush_cb(lv_display_t *display, const lv_area_t *area, uint8_t *px_map)
{
update(px_map); // Transfer buffer to HUB75 driver
lv_display_flush_ready(display); // Notify LVGL that flush is complete
}
update()is provided by the optimisedhub75driver.
With LV_DISPLAY_RENDER_MODE_DIRECT the buffer size must match the size of the display. LVGL will render into the correct location of the buffer. Using this method the buffer always contain the whole display image.
lv_init();
lv_tick_set_cb(get_milliseconds_since_boot);
display1 = lv_display_create(MATRIX_PANEL_WIDTH, MATRIX_PANEL_HEIGHT);
if (display1 == NULL)
{
printf("lv_display_create failed\n");
return -1;
}
lv_display_set_buffers_with_stride(display1, buf1, NULL, sizeof(buf1), MATRIX_PANEL_WIDTH * 3, LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_flush_cb(display1, flush_cb);In your main loop, call lv_timer_handler()
while (true)
{
if (load_anim)
{
load_anim = false;
setup_demo(frame_index, bouncingBalls, fireEffect, imageAnimation, timer);
}
update_demo(frame_index, bouncingBalls, fireEffect, imageAnimation, timer);
lv_timer_handler();
sleep_ms(frame_delay_ms);
}- LVGL
- hub75_lvgl (custom optimized driver)
- CMake build system (standard for Pico SDK projects)
You can easily use this project with VSCode, especially with the Raspberry Pi Pico plugin installed. Follow these steps:
-
Open VSCode and start a new window.
-
Clone the repository:
-
Project Import Prompt:
-
Configure Pico SDK Settings:
-
Wait for Setup Completion:
- VSCode will download required tools, the Pico SDK, and any plugins.
-
Connect the Hardware:
- Make sure the HUB75 LED matrix is properly connected to the Raspberry Pi Pico.
- Attach the Raspberry Pi Pico USB cable to your computer
-
Build and Upload:
π‘ If everything is set up correctly, your matrix should come to life with the updated HUB75 DMA driver.
- Add some more graphics examples to explore the capabilities of LVGL on Pico.
Any contribution to the project is appreciated!
For any question or problem, feel free to open an issue!





