1. 创建第一个LVGL项目

任务目标

在ESP32-S3开发板上搭建LVGL图形库开发环境,初始化显示屏驱动,最终在屏幕上居中显示“Hello World”文本。本案例使用ESP-IDF v5.x + LVGL v8.3作为软件栈,显示屏采用ILI9341(SPI接口,分辨率240x320)。

环境准备

  • 硬件:ESP32-S3-DevKitC-1开发板、ILI9341 SPI显示屏模块、杜邦线若干
  • 软件:ESP-IDF v5.1及以上版本(安装方法见官方文档)、Python 3.8+
  • 接线:ILI9341的SCLK→GPIO12、MOSI→GPIO11、CS→GPIO10、DC→GPIO9、RST→GPIO8、BL→GPIO7(背光控制,可接3.3V常亮)

工程创建与依赖配置

使用IDF组件管理器添加LVGL和显示屏驱动依赖。在项目根目录执行以下命令创建基础工程:

idf.py create-project hello_lvgl
cd hello_lvgl
idf.py add-dependency "lvgl/lvgl@^8.3.11"
idf.py add-dependency "esp_lcd_ili9341"

在main/CMakeLists.txt中确认自动生成的依赖引用已包含上述两个组件。若使用自定义板级配置,需在sdkconfig.defaults中设置SPI引脚和屏幕参数:

CONFIG_LV_COLOR_DEPTH_16=y
CONFIG_LV_MEM_CUSTOM=1
CONFIG_LV_MEM_SIZE=65536

关键完整代码

在main/main.c中写入以下完整实现。代码包含SPI总线初始化、ILI9341显示驱动注册、LVGL显示缓冲区配置以及“Hello World”标签绘制:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/spi_master.h"
#include "esp_log.h"
#include "lvgl.h"

#define LCD_H_RES 240
#define LCD_V_RES 320
#define LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000)
#define LCD_SPI_NUM SPI2_HOST
#define LCD_GPIO_SCLK 12
#define LCD_GPIO_MOSI 11
#define LCD_GPIO_CS   10
#define LCD_GPIO_DC   9
#define LCD_GPIO_RST  8

static const char *TAG = "hello_lvgl";

static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) {
    esp_lcd_panel_handle_t panel = (esp_lcd_panel_handle_t)drv->user_data;
    esp_lcd_panel_draw_bitmap(panel, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_p);
    lv_disp_flush_ready(drv);
}

void app_main(void) {
    // SPI总线配置
    spi_bus_config_t bus_cfg = {
        .sclk_io_num = LCD_GPIO_SCLK,
        .mosi_io_num = LCD_GPIO_MOSI,
        .miso_io_num = -1,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = LCD_H_RES * LCD_V_RES * 2
    };
    spi_bus_initialize(LCD_SPI_NUM, &bus_cfg, SPI_DMA_CH_AUTO);

    // LCD IO配置
    esp_lcd_panel_io_handle_t io_handle;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = LCD_GPIO_DC,
        .cs_gpio_num = LCD_GPIO_CS,
        .pclk_hz = LCD_PIXEL_CLOCK_HZ,
        .lcd_cmd_bits = 8,
        .lcd_param_bits = 8,
        .spi_mode = 0,
        .trans_queue_depth = 10
    };
    esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_NUM, &io_config, &io_handle);

    // 初始化ILI9341
    esp_lcd_panel_handle_t panel = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = LCD_GPIO_RST,
        .color_space = ESP_LCD_COLOR_SPACE_RGB,
        .bits_per_pixel = 16
    };
    esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel);
    esp_lcd_panel_reset(panel);
    esp_lcd_panel_init(panel);
    esp_lcd_panel_invert_color(panel, true);
    esp_lcd_panel_disp_on_off(panel, true);

    // LVGL初始化
    lv_init();
    static lv_disp_draw_buf_t draw_buf;
    static lv_color_t buf1[LCD_H_RES * 40];
    lv_disp_draw_buf_init(&draw_buf, buf1, NULL, LCD_H_RES * 40);

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LCD_H_RES;
    disp_drv.ver_res = LCD_V_RES;
    disp_drv.flush_cb = lvgl_flush_cb;
    disp_drv.draw_buf = &draw_buf;
    disp_drv.user_data = panel;
    lv_disp_drv_register(&disp_drv);

    // 创建并显示Hello World标签
    lv_obj_t *label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Hello World");
    lv_obj_center(label);

    // LVGL定时刷新任务
    while (1) {
        lv_timer_handler();
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

编译与烧录

idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor

将/dev/ttyUSB0替换为实际串口设备名。首次烧录后按复位键,屏幕应显示白色背景和黑色“Hello World”文本。

验证方法与通过条件

  • 编译通过:执行idf.py build无报错,生成hello_lvgl.bin固件文件。
  • 屏幕点亮:烧录后ILI9341背光亮起,屏幕呈现白色背景(若显示为蓝色,说明颜色反转设置不正确,需调整esp_lcd_panel_invert_color参数)。
  • 文本显示:屏幕中央出现清晰可辨的黑色“Hello World”字样,字体无乱码或花屏。
  • 系统稳定:持续运行5分钟无重启、无显示撕裂或闪烁现象。

常见问题排查

  • 白屏无内容:检查SPI接线是否松动,确认esp_lcd_panel_init返回ESP_OK。
  • 显示颜色异常:ILI9341模块存在兼容性差异,尝试将esp_lcd_panel_invert_color参数改为false。
  • 编译报错找不到lvgl头文件:在项目根目录执行idf.py reconfigure重新生成依赖。