一、LVGL 图形库简介

LVGL(Light and Versatile Graphics Library)是一款开源嵌入式图形库,可用于构建美观、响应迅速的图形用户界面(GUI),广泛适配智能手表、智能家居设备、工业控制终端等嵌入式场景,具备轻量级、跨平台、易扩展的特性。

二、ESP32S3 开发板部署 LVGL 9.4 关键步骤

1. 组件安装

基于 ESP-IDF 开发环境,通过终端指令完成 LVGL 核心库及适配组件的安装:

bash

运行

idf.py add-dependency "lvgl/lvgl^9.4.0"
idf.py add-dependency "espressif/esp_lvgl_port^2.6.3"

安装完成后,在自动生成的idf_component.yml文件中补充触摸屏驱动依赖:

yaml

dependencies:
  espressif/esp_lcd_touch_ft5x06: ^1.0.6

2. 配置与初始化

  • LCD.h头文件中添加 LVGL 相关头文件引入及初始化函数声明,确保硬件层与图形库的适配;
  • 打开 SDK 配置编辑器,搜索并勾选Benchmark your system选项以启用系统基准测试功能;
  • 按需添加字体配置,本次开发选择 16 号、24 号、26 号字体以满足界面显示需求;
  • 在主函数中引入 LVGL 示例头文件:#include "demos/lv_demos.h",为界面开发提供基础接口。

3. 布局方式说明

LVGL 支持两种核心布局方式,适配不同 UI 开发场景:

(1)绝对定位

通过lv_obj_set_x()/lv_obj_set_y()函数精准控制组件坐标,示例:

c

运行

lv_obj_set_x(btn_obj, 0); // 设置组件x轴坐标为0
(2)弹性布局(Flex Layout)
  • 排列方向配置:lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow),用于定义组件排列方向及换行规则,如垂直列布局(从上到下排列):

    c

    运行

    lv_obj_set_flex_flow(root, LV_FLEX_FLOW_COLUMN);
    
  • 对齐方式配置:lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_align, lv_flex_align_t cross_align, lv_flex_align_t track_align),用于控制组件对齐及间距,示例:

    c

    运行

    lv_obj_set_flex_align(root, 
                          LV_FLEX_ALIGN_CENTER,    // 子元素水平居中
                          LV_FLEX_ALIGN_SPACE_EVENLY, // 子元素垂直间距均匀
                          LV_FLEX_ALIGN_CENTER);   // 交叉轴居中
    

三、核心代码实现(主函数及 UI 逻辑)

c

运行

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "LCD/LCD.h"
#include "demos/lv_demos.h"

// LVGL刷新任务(必须)
static void lvgl_refresh_task(void *arg)
{
    while(1) {
        lv_timer_handler(); // 处理LVGL定时器/渲染任务
        vTaskDelay(pdMS_TO_TICKS(5)); // 5ms刷新一次,适配320x240屏幕
    }
}

// 事件回调函数(处理按钮/复选框交互)
static void event_handler(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    if(code == LV_EVENT_CLICKED) {
        ESP_LOGI("UI_EVENT", "Clicked event triggered");
    }
    else if(code == LV_EVENT_VALUE_CHANGED) {
        ESP_LOGI("UI_EVENT", "Toggled event triggered");
    }
}

// 登录界面创建函数
void lv_example_login(void)
{
    lv_obj_t * root = lv_scr_act(); // 获取屏幕根对象
    // 配置弹性布局:垂直列排列+居中对齐+均匀间距
    lv_obj_set_flex_flow(root, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(root, 
                          LV_FLEX_ALIGN_CENTER,    
                          LV_FLEX_ALIGN_SPACE_EVENLY, 
                          LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_all(root, 20, LV_PART_MAIN); // 根对象内边距

    // 文本标签(解决文本框单独置顶白屏问题)
    lv_obj_t *label_title = lv_label_create(root);
    lv_label_set_text(label_title, "Login"); // 标题文本(英文)
    lv_obj_set_style_text_font(label_title, &lv_font_montserrat_24, LV_PART_MAIN);

    // 账号输入文本框
    lv_obj_t *textarea_account = lv_textarea_create(root);
    lv_textarea_set_placeholder_text(textarea_account, "please input account");
    lv_textarea_set_one_line(textarea_account, true);  // 单行模式
    lv_obj_set_size(textarea_account, 280, 40); // 适配320x240屏幕

    // 密码输入文本框
    lv_obj_t *textarea_pwd = lv_textarea_create(root);
    lv_textarea_set_placeholder_text(textarea_pwd, "please input password");
    lv_textarea_set_one_line(textarea_pwd, true);
    lv_textarea_set_password_mode(textarea_pwd, true); // 密码隐藏模式
    lv_obj_set_size(textarea_pwd, 280, 40);

    // 同意条款复选框
    lv_obj_t * cb_agree = lv_checkbox_create(root);
    lv_checkbox_set_text(cb_agree, "I agree to the terms of service");
    lv_obj_add_event_cb(cb_agree, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_set_width(cb_agree, 280);

    // 登录按钮
    lv_obj_t * btn_login = lv_button_create(root);
    lv_obj_add_event_cb(btn_login, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_remove_flag(btn_login, LV_OBJ_FLAG_PRESS_LOCK);
    lv_obj_set_size(btn_login, 240, 30);

    // 按钮文本
    lv_obj_t *label_btn = lv_label_create(btn_login);
    lv_label_set_text(label_btn, "login");
    lv_obj_center(label_btn);
}

// 主函数入口
void app_main(void)
{
    // 硬件初始化
    bsp_i2c_init();        // I2C总线初始化
    pca9557_init();        // IO扩展芯片初始化
    bsp_lcd_init();        // LCD屏幕初始化(关键,避免白屏)

    // 启动LVGL
    bsp_lvgl_start();

    // 创建LVGL刷新任务(优先级5,避免界面卡顿)
    xTaskCreate(lvgl_refresh_task, "lvgl_refresh", 4096, NULL, 5, NULL);

    // 创建登录界面
    lv_example_login();

    // 主循环
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

四、登录界面开发问题与现象记录

基于上述配置开发登录界面时,选用文本框、复选框、按钮三类核心组件,计划按 “文本框→复选框→按钮” 的顺序垂直排列。开发过程中发现关键问题:单独将文本框置于界面最上方时,程序烧录至 ESP32S3 开发板后屏幕呈现白屏状态,无任何 UI 元素显示;仅在文本框上方额外添加一个文本标签(如 “Login”)后,整个界面才能正常渲染,各组件按预期顺序显示。

五、参考资源

LVGL 相关组件及开发文档可参考百问网开源资源:http://lvgl.100ask.net/9.2/

总结

本次实践完成了 LVGL 9.4 在 ESP32S3 开发板的移植与基础 UI 开发,验证了弹性布局在嵌入式界面开发中的实用性,但仍存在文本框单独置顶导致白屏的异常现象,后续将进一步排查硬件初始化顺序、LVGL 组件渲染机制等方面的原因,以解决该兼容性问题。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐