Why LVGL for Embedded Displays?
Modern embedded consumer hardware demands responsive graphical interfaces comparable to smartphone screens. When developing the Breezi Air Purifier, an old-school 16x2 character LCD was insufficient to display PM2.5, PM10, temperature, humidity, and filter life simultaneously.
LVGL (Light and Versatile Graphics Library) provides an open-source, memory-efficient C graphics engine that brings anti-aliased fonts, animations, and touch controls to microcontrollers with modest RAM.
Optimizing SPI Throughput with DMA
A frequent bottleneck when running 320x240 RGB565 screens on microcontrollers is frame tearing and sluggish redraws. Each full frame transfer requires:
320 × 240 × 2 bytes = 153,600 bytes
To achieve a silky smooth 30+ FPS refresh without starving the CPU:
- SPI Frequency: Configured the ESP32 VSPI peripheral clock to 40MHz.
- Double DMA Buffering: Allocated two partial-frame buffers in internal SRAM (
320 x 20lines each). While DMA asynchronously pushes Buffer A to the ILI9341 display controller, LVGL renders the next dirty rectangle into Buffer B. - SPI Transaction Overhead: Minimized CS toggling by bursting contiguous scanlines.
FreeRTOS Multitasking Architecture
Running GUI rendering on the same thread as sensor reading will cause erratic display stutters whenever a sensor blocks on I2C/UART.
We structured the firmware into decoupled tasks:
- Core 0 - Sensor & Communication Task: Polls PMS5003 and SHT31, filters raw ADC data, and posts state updates to a FreeRTOS Queue.
- Core 1 - LVGL Render Task: Dedicated solely to handling
lv_timer_handler()and touch input polling every 10ms.
This isolation guarantees that the UI remains fluid even during intensive network activity or sensor calibration routines.