ESP32+ESP-IDF+LVGL+ST7789:从零构建GUI显示框架的实战指南
1. 环境准备与工程创建第一次接触ESP32和LVGL的朋友可能会觉得配置环境很复杂但其实只要跟着步骤走30分钟就能搞定。我最近用ESP32-S3和ST7789屏做了个小项目实测这套组合性价比超高240x240分辨率完全够用。先安装ESP-IDF开发环境推荐用VSCode插件一键安装。打开VSCode后搜索安装Espressif IDF插件按F1输入ESP-IDF: Configure ESP-IDF extension选择Express安装方式勾选需要的版本建议v5.0创建新工程时有个坑要注意不要用默认的hello_world模板我推荐用官方spi_lcd_touch示例作为基础cp -r $IDF_PATH/examples/peripherals/lcd/spi_lcd_touch my_lvgl_project cd my_lvgl_project2. ST7789驱动移植官方示例默认支持ILI9341我们需要手动添加ST7789支持。在项目根目录执行mkdir -p components/esp_lcd wget https://github.com/nopnop2002/esp-idf-st7789/archive/refs/heads/master.zip unzip master.zip -d components/esp_lcd关键修改点打开components/esp_lcd/esp-idf-st7789-main/st7789.c修改第35行#define TAG st7789为你的项目名检查148行的SPI模式配置ST7789通常需要mode3在menuconfig中配置idf.py menuconfig依次选择Component config - LCD SPI Driver - Controller model - ST7789设置正确的引脚号根据你的硬件连接SPI时钟建议设为40MHz实测超过60MHz会花屏3. LVGL集成与配置LVGL官方已经提供了ESP32的移植方案直接添加子模块git submodule add https://github.com/lvgl/lvgl.git components/lvgl git submodule add https://github.com/lvgl/lvgl_esp32_drivers.git components/lvgl_esp32_drivers在main.c中添加初始化代码// LVGL初始化 lv_init(); lv_disp_draw_buf_init(draw_buf, buf1, buf2, DISP_BUF_SIZE); // 显示驱动配置 lv_disp_drv_init(disp_drv); disp_drv.flush_cb disp_driver_flush; disp_drv.draw_buf draw_buf; lv_disp_drv_register(disp_drv); // 启用触摸输入如果有 lv_indev_drv_init(indev_drv); indev_drv.type LV_INDEV_TYPE_POINTER; indev_drv.read_cb touch_driver_read; lv_indev_drv_register(indev_drv);4. 性能优化技巧双缓冲配置 在menuconfig中设置LVGL的双缓冲大小建议为屏幕大小的1/10#define DISP_BUF_SIZE (240 * 240 / 10 * sizeof(lv_color_t))DMA传输优化 修改components/lvgl_esp32_drivers/lvgl_tft/disp_spi.cspi_bus_config_t buscfg { .miso_io_num -1, // 不需要MISO .mosi_io_num GPIO_NUM_11, .sclk_io_num GPIO_NUM_12, .quadwp_io_num -1, .quadhd_io_num -1, .max_transfer_sz DISP_BUF_SIZE * 2, .flags SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_DMA };刷新率监控 添加FPS显示功能static void fps_task(void *arg) { while(1) { lv_label_set_text_fmt(fps_label, FPS:%.1f, lv_refr_get_fps_avg()); vTaskDelay(pdMS_TO_TICKS(500)); } } xTaskCreate(fps_task, fps_task, 4096, NULL, 5, NULL);5. 常见问题解决花屏问题检查SPI线长建议10cm降低SPI时钟频率尝试20MHz在st7789_init()中添加延时gpio_set_level(ST7789_RST, 0); vTaskDelay(100 / portTICK_PERIOD_MS); gpio_set_level(ST7789_RST, 1); vTaskDelay(120 / portTICK_PERIOD_MS);触摸失灵确认触摸IC型号常见CST816修改lvgl_esp32_drivers中的touch_driver.c#define TOUCH_I2C_SLAVE_ADDR 0x15 // CST816默认地址内存不足 优化LVGL配置// 在lv_conf.h中调整 #define LV_MEM_SIZE (48 * 1024) // ESP32-S3可设更大 #define LV_DISP_DEF_REFR_PERIOD 30 // 刷新周期ms #define LV_USE_GPU 0 // 除非需要硬件加速6. 实战案例音乐播放器UI用LVGL实现一个漂亮的音乐界面lv_obj_t * create_music_player(lv_obj_t * parent) { // 创建背景 lv_obj_t * bg lv_obj_create(parent); lv_obj_set_size(bg, 240, 240); // 专辑封面 lv_obj_t * cover lv_img_create(bg); lv_img_set_src(cover, A:cover.jpg); lv_obj_align(cover, LV_ALIGN_TOP_MID, 0, 20); // 进度条 lv_obj_t * slider lv_slider_create(bg); lv_obj_set_size(slider, 200, 10); lv_obj_align(slider, LV_ALIGN_CENTER, 0, 40); // 控制按钮 lv_obj_t * btn_play lv_btn_create(bg); lv_obj_align(btn_play, LV_ALIGN_BOTTOM_MID, 0, -30); lv_obj_t * label lv_label_create(btn_play); lv_label_set_text(label, LV_SYMBOL_PLAY); return bg; }最后烧录时建议用release模式idf.py build -DCMAKE_BUILD_TYPERelease idf.py -p /dev/ttyUSB0 flash monitor