目录

ESP32的IDF开发学习-移植lvgl并显示简单ui以gc9a01为例

ESP32的IDF开发学习-移植lvgl并显示简单ui(以gc9a01为例)

📖 前言

历经数日的调试与优化,终于成功攻克GC9A01显示屏的驱动开发!🎉 本文将聚焦 LVGL图形库的移植与实践 ,详细介绍如何在ESP32-S3平台上实现基础UI渲染,为后续复杂界面开发奠定基础。


🛠️ LVGL组件集成

1️⃣ 组件安装

通过 ESP-IDF组件管理器 快速集成LVGL:

  1. 打开组件注册器,搜索 lvgl

    https://i-blog.csdnimg.cn/img_convert/bc60e23ea1fadd12f6d273a94323edbe.png

  2. 选择 v8.x版本 (更稳定的长期支持版本)并安装

    https://i-blog.csdnimg.cn/img_convert/75bc10303848fdea8b5dd658130baad7.png

  3. 同时安装 lvgl_port 端口适配层

    https://i-blog.csdnimg.cn/img_convert/1e06473cf727c7d89c66341365797616.png

2️⃣ 关键配置

lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG(); 
lvgl_port_init(&port_cfg); 

📌 配置项解析

  • task_priority
    建议设为 5 (低于WiFi/BT等高优先级任务)
  • task_stack
    至少 4096 字(复杂UI需增大)
  • task_affinity
    绑定到 CPU1 可隔离渲染任务与其他逻辑

⚠️ 重要提示

  • 若显示屏出现撕裂,需调整 timer_period_ms 与刷新率同步
  • 多任务操作LVGL前必须 加锁

🖥️ 显示器注册与配置

1️⃣ 硬件定义

#define gc9a01_swap_xy  false // XY轴交换(适配屏幕方向)
#define gc9a01_mirror_x true  // X轴镜像(物理安装方向补偿)
#define gc9a01_mirror_y false // Y轴镜像

esp_lcd_panel_io_handle_t io_handle = NULL; // LCD总线句柄
esp_lcd_panel_handle_t panel_handle = NULL; // 面板控制句柄
lv_disp_t *disp_ = NULL; // LVGL显示设备对象

2️⃣ 显示端口注册

const lvgl_port_display_cfg_t display_cfg = {
    .io_handle = io_handle, 
    .panel_handle = panel_handle,
    .buffer_size = LCD_WIDTH * 80,   // 双缓冲每帧80行
    .double_buffer = true,           // ✅ 启用双缓冲防撕裂
    .hres = LCD_WIDTH,               // 水平分辨率
    .vres = LCD_HEIGHT,              // 垂直分辨率
    .rotation = {
        .swap_xy = gc9a01_swap_xy,   // 坐标系变换
        .mirror_x = gc9a01_mirror_x, 
        .mirror_y = gc9a01_mirror_y,
    },
    .flags = {
        .buff_dma = 1,               // 🚀 DMA加速传输
        .buff_spiram = 0,            // 禁用PSRAM(片上内存充足时)
        .sw_rotate = 0, // 不使用软件旋转
        .full_refresh = 0, // 不使用全屏刷新
        .direct_mode = 0, // 不使用直接模式
    },
};

disp_ = lvgl_port_add_disp(&display_cfg); // 注册显示器到LVGL

🔧 调试技巧

  • 若帧率不足,可尝试增大 buffer_size 至全屏尺寸(牺牲内存换取速度)
  • sw_rotate=1 可在不修改硬件排线的情况下软件旋转屏幕
  • PSRAM和DMA不能同时开启

🔍 核心代码剖析

lvgl_port_init() 函数详解

该函数实现了LVGL与FreeRTOS的深度整合,主要功能如下:

模块功能说明
心跳定时器通过 lvgl_port_tick_init() 注入系统时钟,维持LVGL动画/事件处理(默认5ms中断)
互斥量保护递归锁防止多线程竞争LVGL对象(如UI线程与传感器数据更新线程)
任务绑定可指定渲染任务运行的CPU核心,提升多核利用率

⚠️ 线程安全警告

所有LVGL对象操作必须包裹在锁内:

xSemaphoreTakeRecursive(lvgl_port_ctx.lvgl_mux, portMAX_DELAY); 
lv_label_set_text(label, "New Value"); 
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);

🎨 基础UI开发示例

// 获取默认屏幕对象
lv_obj_t *scr = lv_disp_get_scr_act(disp_);

// 设置背景样式(50%透明度,白色底色)
lv_obj_set_style_bg_opa(scr, LV_OPA_50, LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(scr, lv_color_white(), LV_PART_MAIN); 

// 创建文本标签并定位
lv_obj_t *label = lv_label_create(scr);
lv_label_set_text(label, "Hello ESP32");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); // 居中显示

ESP_LOGI(TAG, "UI初始化完成✅");

编译烧录后效果:

https://i-blog.csdnimg.cn/img_convert/aeb6a5fd81bfe9818ab8aa7da74e90bd.jpeg

大功告成!!!