ESP32-S3 TR2S LVGL Code Guide
Document Navigation — This page is the overview; see each subsystem for details:
- LCD Driver — SPI/QSPI pins, frame protocol, APIs, performance
- LVGL Porting — display/touch ports, double buffering, data flow, task synchronization
- CTP Driver — chip selection, I2C/SPI communication, callbacks
Download the code
1. Hardware Platform
| Component | Specification |
|---|---|
| MCU | ESP32-S3 (N16R8), dual-core Xtensa LX7, 240 MHz |
| Display | TR2S QSPI LCD, 800×480, RGB565 |
| Touchscreen | Capacitive, GT911 by default (I2C), optional TR230S (shares the LCD SPI/QSPI bus) |
| LVGL version | v8.3.11, full-screen double buffering (PSRAM ~1.46 MB) |
| ESP-IDF version | v5.1.2 |
The TR2S hardware supports SPI / QSPI / 8080-8B / 8080-16B; only the SPI and QSPI drivers are currently implemented.
2. Hardware Resource Allocation
| Signal | GPIO | QSPI Mode | SPI Mode |
|---|---|---|---|
| D0 / MOSI | GPIO 4 | QSPI data line 0 | SPI MOSI |
| D1 / MISO | GPIO 5 | QSPI data line 1 | SPI MISO |
| D2 | GPIO 6 | QSPI data line 2 + WP | — |
| D3 | GPIO 7 | QSPI data line 3 + HD | — |
| CLK | GPIO 16 | Clock line | Clock line |
| CS | GPIO 15 | Chip select | Chip select |
| DC | GPIO 19 | — (no DC needed) | Command/data select |
| RST | GPIO 18 | Reset (active low) | Reset (active low) |
| WAIT | GPIO 46 | Busy wait (low = busy) | Busy wait (low = busy) |
| CTP SDA | GPIO 39 | Touch I2C data | Touch I2C data |
| CTP SCL | GPIO 38 | Touch I2C clock | Touch I2C clock |
| CTP INT | GPIO 45 | Touch interrupt | Touch interrupt |
| CTP RST | GPIO 48 | Touch reset | Touch reset |
Bus Configuration:
| Bus | Controller | Mode | Frequency | Purpose |
|---|---|---|---|---|
| QSPI/SPI | SPI3_HOST | Quad / Standard, half-duplex | 80 MHz | LCD display + optional touch communication |
| I2C | I2C_NUM_1 | Master | 400 kHz | Touchscreen (GT911, etc., default) |
When
TR230S_ENis enabled, the TP driver reuses the LCD's SPI bus vialcd_ctp_write_cmd()/lcd_ctp_read_cmd()exported byqspilcd.c, and does not initialize I2C.
3. Code Structure
esp32_lvgl/
├── CMakeLists.txt # project root CMake (sets EXTRA_COMPONENT_DIRS)
├── main/
│ ├── CMakeLists.txt # main component registration
│ ├── main.c # ★ entry point app_main()
│ ├── APP/
│ │ ├── lvgl_demo.c # ★ LVGL porting core (display/touch/refresh/tasks) → lvgl.md
│ │ ├── lvgl_demo.h # public interface declarations
│ │ ├── lv_mainstart.c/.h # file browser demo
│ │ └── demo.c # additional demo pages
│ └── DEMO1/ # NXP GUI Guider dashboard demo
└── components/
├── BSP/
│ ├── SPILCD/ # ★ SPI/QSPI LCD driver core → lcd.md
│ │ ├── qspi.c # low-level SPI/QSPI transfer functions
│ │ ├── qspi.h # pin macros + function declarations
│ │ ├── qspilcd.c # LCD init + refresh callback + CTP sharing interface
│ │ └── qspilcd.h # configuration macros / color macros / structs
│ ├── TDO_CTP/ # ★ touchscreen driver (multi-chip support)
│ │ ├── drv/esp32/
│ │ │ ├── esp32_ctp.c # hardware abstraction layer (I2C/GPIO/read-write interface registration)
│ │ │ ├── esp32_ctp.h # touch pin definitions
│ │ │ └── tdo_ctp_conf.h # chip selection macro
│ │ ├── src/
│ │ │ ├── tdo_ctp.c # generic CTP driver framework
│ │ │ └── ctp/ # 11 touchscreen chip drivers
│ │ └── inc/
│ │ ├── tdo_ctp.h # CTP API interface
│ │ └── tdo_def.h # common types/event definitions
│ ├── LED/ # LED driver (led.c/h)
│ ├── SDIO/ # SD card driver (spi_sdcard.c/h)
│ └── SPI/ # generic SPI driver (spi.c/h)
└── LVGL/ # LVGL v8.3.11 full source code4. Initialization Sequence
4.1 Overall Flow
app_main() (Core 0)
│
├─① initialize_nvs() NVS Flash initialization
├─② led_init() LED pin configuration
│
└─③ lvgl_demo() ───────────────────── ★ main initialization entry
│
├─④ lv_init() LVGL core initialization
├─⑤ lv_port_disp_init() ★ display subsystem
│ ├─ lcd_init() │ GPIO + SPI3 bus + device + hardware reset + LCD init sequence
│ ├─ heap_caps_malloc() × 2 │ allocate two full-screen buffers in PSRAM (768 KB each)
│ ├─ lv_disp_draw_buf_init() │ register double buffering with LVGL
│ └─ lv_disp_drv_register() │ register display driver (flush_cb + full_refresh=1)
│
├─⑥ lv_port_indev_init() ★ touch subsystem
│ ├─ esp32_ctp_init() │ I2C or SPI initialization + chip probing
│ └─ lv_indev_drv_register() │ register input driver (read_cb)
│
├─⑦ xSemaphoreCreateBinary() semaphore (flush_cb → flush task)
├─⑧ xSemaphoreCreateMutex() mutex (protects g_area / g_color)
├─⑨ esp_timer_create/start() 1ms Tick timer → lv_tick_inc(1)
│
├─⑩ xTaskCreate(lv_demo_task) create UI task (Core 1, priority 1, 5 KB)
└─⑪ xTaskCreate(lv_flush_task) create flush task (Core 1, priority 2, 5 KB)
↓ after threads start ↓
lv_demo_task lv_flush_task
├─ lv_demo_widgets() while(1):
└─ while(1): xSemaphoreTake() blocking
lv_timer_handler() lcd_lv_cb() → QSPI DMA
vTaskDelay(10) xSemaphoreGive(xMutex)Applicable platform: ESP32-S3 + IDF 5.1.2 + TR2S + LVGL v8.3.11