Skip to content

Latest commit

 

History

History
808 lines (590 loc) · 25.6 KB

File metadata and controls

808 lines (590 loc) · 25.6 KB

组件服务

ESP LVGL Adapter

English | 中文

目录


组件说明

esp_lvgl_adapter 为 ESP-IDF 工程提供统一的 LVGL 适配层,涵盖显示注册、防撕裂策略、LVGL 线程安全访问,以及文件系统/图片解码/FreeType 字体的可选集成。兼容 LVGL v8 与 v9。

特性

  • 统一的显示注册接口:支持 MIPI DSI / RGB / QSPI / SPI / I2C / I80 等多种显示接口
  • 防撕裂策略:提供多种防撕裂模式
  • 线程安全:提供 lock/unlock API,保证多线程环境下的安全访问
  • 可选模块 (通过 Kconfig 开关控制):
    • 文件系统桥接 (esp_lv_fs)
    • 图片解码 (esp_lv_decoder)
    • FreeType 矢量字体
    • FPS 统计、Dummy Draw(不实际输出到显示器)
  • 多屏支持:可同时管理多个显示设备

添加到工程

通过组件服务添加

idf.py add-dependency "espressif/esp_lvgl_adapter"

idf_component.yml 中声明

dependencies:
  espressif/esp_lvgl_adapter: "*"

系统要求

  • ESP-IDF:>= 5.5
  • LVGL:8.x 或 9.x(自动适配版本)
  • 可选依赖:根据启用的功能需要满足相应的组件依赖

构建与运行

idf.py set-target esp32s3   # 设置目标芯片(或 esp32、esp32c3、esp32p4 等)
idf.py build                # 构建工程
idf.py flash monitor        # 烧录并查看日志

快速开始

以下是一个最小化的使用示例,展示如何初始化适配器并注册显示设备:

#include "esp_lv_adapter.h"  // 包含显示与输入适配接口

void app_main(void)
{
    // 步骤 0: 使用 esp_lcd API 创建面板和面板 IO(此处省略具体实现)
    esp_lcd_panel_handle_t panel = /* ... */;
    esp_lcd_panel_io_handle_t panel_io = /* ... 或 NULL */;

    // 步骤 1: 初始化适配器
    esp_lv_adapter_config_t cfg = ESP_LV_ADAPTER_DEFAULT_CONFIG();
    ESP_ERROR_CHECK(esp_lv_adapter_init(&cfg));

    // 步骤 2: 注册显示设备(根据接口类型选择相应的默认配置宏)
    esp_lv_adapter_display_config_t disp_cfg = ESP_LV_ADAPTER_DISPLAY_RGB_DEFAULT_CONFIG(
        panel,           // LCD 面板句柄
        panel_io,        // LCD 面板 IO 句柄(某些接口可为 NULL)
        800,             // 水平分辨率
        480,             // 垂直分辨率
        ESP_LV_ADAPTER_ROTATE_0 // 旋转角度
    );
    lv_display_t *disp = esp_lv_adapter_register_display(&disp_cfg);
    assert(disp != NULL);

    // 步骤 3: (可选) 注册输入设备
    // 使用 esp_lcd_touch API 创建触摸句柄(此处省略具体实现)
    // esp_lcd_touch_handle_t touch_handle = /* ... */;
    // esp_lv_adapter_touch_config_t touch_cfg = ESP_LV_ADAPTER_TOUCH_DEFAULT_CONFIG(disp, touch_handle);
    // lv_indev_t *touch = esp_lv_adapter_register_touch(&touch_cfg);
    // assert(touch != NULL);

    // 步骤 4: 启动适配器任务
    ESP_ERROR_CHECK(esp_lv_adapter_start());

    // 步骤 5: 使用 LVGL API 绘制界面(需要先加锁)
    if (esp_lv_adapter_lock(-1) == ESP_OK) {
        lv_obj_t *label = lv_label_create(lv_scr_act());
        lv_label_set_text(label, "Hello LVGL!");
        lv_obj_center(label);
        esp_lv_adapter_unlock();
    }
}

使用说明

本节将指导您在 ESP-IDF 项目中集成 LVGL 适配器。

推荐的 SDKCONFIG 配置

在项目的 sdkconfig.defaults 中添加以下配置以获得最佳 LVGL 性能:

核心 LVGL 配置

# FreeRTOS 时钟频率(提高定时器精度)
CONFIG_FREERTOS_HZ=1000

# LVGL 操作系统集成
CONFIG_LV_OS_FREERTOS=y

# 使用标准 C 库函数
CONFIG_LV_USE_CLIB_MALLOC=y
CONFIG_LV_USE_CLIB_STRING=y
CONFIG_LV_USE_CLIB_SPRINTF=y

# 显示刷新率(单位:毫秒,15ms ≈ 66 FPS)
CONFIG_LV_DEF_REFR_PERIOD=15

# 启用样式缓存以提高性能
CONFIG_LV_OBJ_STYLE_CACHE=y

多核优化(ESP32-S3/P4)

对于多核芯片,启用并行渲染:

# 使用 2 个绘制线程以提高性能
CONFIG_LV_DRAW_SW_DRAW_UNIT_CNT=2

PSRAM 优化(如果可用)

对于带有 PSRAM 的设备,优化缓存和 XIP 设置:

ESP32-S3:

CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y

ESP32-P4:

CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_CACHE_L2_CACHE_256KB=y
CONFIG_CACHE_L2_CACHE_LINE_128B=y

性能优化

# 启用编译器优化
CONFIG_COMPILER_OPTIMIZATION_PERF=y

注意:这些是推荐的基准配置。请根据您的具体需求和可用资源进行调整。

决策流程

在开始使用前,请按以下流程做出决策:

  1. 选择接口类型:确定使用的显示接口(MIPI DSI / RGB / QSPI / SPI / I2C / I80)
  2. 选择默认配置宏:根据接口类型选择对应的 ESP_LV_ADAPTER_DISPLAY_XXX_DEFAULT_CONFIG
  3. 选择合适的防撕裂模式(仅 RGB/MIPI DSI 接口支持):
    • 计算所需的帧缓冲数量 (num_fbs)
    • num_fbs 传入 esp_lcd 面板配置
  4. 调节缓冲大小:根据内存与吞吐需求调整 buffer_height

显示注册与防撕裂

接口类型与默认配置宏

根据您的显示接口选择对应的配置宏:

接口类型 默认配置宏 默认防撕裂模式
MIPI DSI ESP_LV_ADAPTER_DISPLAY_MIPI_DEFAULT_CONFIG(...) ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_MIPI_DSI
RGB ESP_LV_ADAPTER_DISPLAY_RGB_DEFAULT_CONFIG(...) ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_RGB
SPI/I2C/I80/QSPI (带 PSRAM) ESP_LV_ADAPTER_DISPLAY_SPI_WITH_PSRAM_DEFAULT_CONFIG(...) ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT (即 NONE)
SPI/I2C/I80/QSPI (无 PSRAM) ESP_LV_ADAPTER_DISPLAY_SPI_WITHOUT_PSRAM_DEFAULT_CONFIG(...) ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT (即 NONE)
MONO (单色显示) ESP_LV_ADAPTER_DISPLAY_SPI_MONO_DEFAULT_CONFIG(...) ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT (即 NONE)

注意

  • 仅 MIPI DSI 和 RGB 支持防撕裂模式
  • SPI/I2C/I80/QSPI 等接口在适配器中统称为 "OTHER" 接口,仅支持 NONE 模式
  • MONO(单色显示)接口支持 I1 水平平铺 (HTILED) 和垂直平铺 (VTILED) 两种布局,且支持旋转

计算帧缓冲数量

在 RGB/MIPI DSI 初始化前,需要计算所需的帧缓冲数量:

uint8_t num_fbs = esp_lv_adapter_get_required_frame_buffer_count(
    ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_RGB,  // 防撕裂模式
    ESP_LV_ADAPTER_ROTATE_0                // 旋转角度
);
// 将 num_fbs 传入 esp_lcd 面板配置

帧缓冲数量规则

  • 90°/270° 旋转 或 三缓冲模式:需要 3 个帧缓冲
  • 双缓冲模式:需要 2 个帧缓冲
  • 单缓冲模式:需要 1 个帧缓冲

防撕裂模式选择

根据您的应用场景选择合适的防撕裂模式:

防撕裂模式 适用场景 帧缓冲数 内存占用 支持接口
TRIPLE_PARTIAL 旋转(90°/270°)
高分辨率流畅 UI
3 RGB / MIPI DSI
TRIPLE_FULL 整屏/大区域刷新
内存充足
3 RGB / MIPI DSI
DOUBLE_FULL 大区域刷新
内存较紧
2 RGB / MIPI DSI
DOUBLE_DIRECT 小区域更新
控件/局部变化
2 RGB / MIPI DSI
TE_SYNC SPI/I2C/I80/QSPI 接口
面板提供 TE 信号,通过垂直消隐同步消除撕裂
1 SPI / I2C / I80 / QSPI
NONE 静态 UI
超低内存
1 所有接口

重要限制

  • RGB/MIPI DSI 在 TEAR_AVOID_MODE_NONE不支持旋转(任何非 0 旋转会被拒绝)
  • OTHER (SPI/I2C/I80/QSPI) 接口支持 NONETE_SYNC 模式;如需旋转,请在 LCD 初始化阶段配置面板方向(交换 XY/镜像),并相应调整触摸坐标映射
  • MONO (单色显示) 接口支持软件旋转(0°/90°/180°/270°),通过 LVGL 进行像素级旋转处理
  • TE_SYNC 模式要求面板提供 TE 输出信号,并将 TE 引脚连接到 ESP GPIO;使用 ESP_LV_ADAPTER_DISPLAY_SPI_WITH_PSRAM_TE_DEFAULT_CONFIG 宏进行配置。examples/display/gui/lvgl_common_demo 会自动检测并在可用时使用 TE 同步

内存估算

整帧显存大小 ≈ 水平分辨率 × 垂直分辨率 × 每像素字节数

示例(RGB565 格式,2 字节/像素):

  • 800×480 分辨率:≈ 750 KB
  • 三缓冲:≈ 2.2 MB
  • 结论:通常需要 PSRAM 支持

缓冲调优(默认与建议)

默认 buffer_height

不同接口的 buffer_height 默认值(详见 esp_lv_adapter_display.h):

接口类型 默认 buffer_height 说明
MIPI DSI 50 中等条带高度
RGB 50 中等条带高度
OTHER + PSRAM ver_res (整屏高度) 整屏高度分区,吞吐更高
OTHER 无 PSRAM 10 小条带以节省 RAM

调优建议

  • 提高 buffer_height

    • ✅ 提升吞吐量(减少 flush 次数、增大条带)
    • ❌ 增加 RAM 占用
  • 降低 buffer_height

    • ✅ 节省 RAM
    • ❌ 增加 flush 次数,可能降低性能

硬件初始化:设置 num_fbs

在 RGB/MIPI DSI 初始化阶段,需要提前计算并传入面板帧缓冲数量:

// 1. 选择防撕裂模式和旋转角度
esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode = ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_MIPI_DSI;
esp_lv_adapter_rotation_t rotation = ESP_LV_ADAPTER_ROTATE_0;

// 2. 计算所需帧缓冲数量
uint8_t num_fbs = esp_lv_adapter_get_required_frame_buffer_count(tear_avoid_mode, rotation);

// 3. 将 num_fbs 传入 esp_lcd 面板配置
#if SOC_MIPI_DSI_SUPPORTED
esp_lcd_dpi_panel_config_t dpi_cfg = {
    .num_fbs = num_fbs,
    // ... 其他 MIPI DSI DPI 配置
};
#endif

#if SOC_LCDCAM_RGB_LCD_SUPPORTED
esp_lcd_rgb_panel_config_t rgb_cfg = {
    .num_fbs = num_fbs,
    // ... 其他 RGB 配置
};
#endif

具体实现请参考示例工程和测试应用中的配置。

任务栈与 PSRAM

适配器 LVGL 任务栈

适配器的 LVGL 任务栈默认为 8KB,支持将其放入 PSRAM:

esp_lv_adapter_config_t cfg = ESP_LV_ADAPTER_DEFAULT_CONFIG();
cfg.stack_in_psram = true;   // 尝试将任务栈放入 PSRAM
ESP_ERROR_CHECK(esp_lv_adapter_init(&cfg));

说明

  • 默认栈大小:8KB(适合大多数应用)
  • 若 PSRAM 不可用会自动回退到片内 RAM
  • PSRAM 时延略高于片内 RAM,但对 UI 任务影响小

线程安全

LVGL API 不是线程安全的,多线程访问时必须使用适配器锁:

if (esp_lv_adapter_lock(-1) == ESP_OK) {
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn, 100, 50);
    esp_lv_adapter_unlock();
}

输入设备

触摸屏

适配器提供了触摸设备注册的默认配置宏 ESP_LV_ADAPTER_TOUCH_DEFAULT_CONFIG,简化配置过程:

// 使用默认配置宏注册触摸设备
esp_lv_adapter_touch_config_t touch_cfg = ESP_LV_ADAPTER_TOUCH_DEFAULT_CONFIG(disp, touch_handle);
lv_indev_t *touch = esp_lv_adapter_register_touch(&touch_cfg);
assert(touch != NULL);

参数说明

  • disp:关联的 LVGL 显示设备
  • touch_handle:通过 esp_lcd_touch API 创建的触摸句柄
  • 默认缩放比例:x = 1.0, y = 1.0

旋钮/编码器

需要启用 Kconfig 选项 ESP_LV_ADAPTER_ENABLE_KNOB。详见 esp_lv_adapter_input.h

按键导航

需要启用 Kconfig 选项 ESP_LV_ADAPTER_ENABLE_BUTTON。详见 esp_lv_adapter_input.h

文件系统桥接(可选)

启用 ESP_LV_ADAPTER_ENABLE_FS 后,可将 esp_mmap_assets 生成的资源分区挂载为 LVGL 文件系统:

#include "esp_lv_fs.h"
#include "esp_mmap_assets.h"

// 1. 创建 mmap 资源句柄
mmap_assets_handle_t assets;
const mmap_assets_config_t mmap_cfg = {
    .partition_label = "assets_A",
    .max_files = MMAP_DRIVE_A_FILES,
    .checksum  = MMAP_DRIVE_A_CHECKSUM,
    .flags = { .mmap_enable = true },
};
ESP_ERROR_CHECK(mmap_assets_new(&mmap_cfg, &assets));

// 2. 挂载为 LVGL 文件系统
esp_lv_fs_handle_t fs_handle;
const fs_cfg_t fs_cfg = {
    .fs_letter = 'A',           // 盘符(A 到 Z)
    .fs_assets = assets,
    .fs_nums = MMAP_DRIVE_A_FILES,
};
ESP_ERROR_CHECK(esp_lv_adapter_fs_mount(&fs_cfg, &fs_handle));

// 3. 在 LVGL 中使用文件路径,如 "A:image.png"
lv_obj_t *img = lv_img_create(lv_scr_act());
lv_img_set_src(img, "A:my_image.png");

图片解码(可选)

启用 ESP_LV_ADAPTER_ENABLE_DECODER 后,适配器会集成 esp_lv_decoder,支持直接使用 LVGL 图片控件加载各种格式的图片:

// 使用 LVGL 图片控件加载图片
lv_obj_t *img = lv_img_create(lv_scr_act());
lv_img_set_src(img, "I:red_png.png");  // I: 为图片解码器的盘符

支持的格式

  • 标准格式:JPG、PNG、QOI
  • 分片格式:SJPG、SPNG、SQOI(针对嵌入式优化)
  • 硬件加速:JPEG、PJPG(如果芯片支持)

详细说明:请参考 components/display/tools/esp_lv_decoder/README.md

FreeType 字体(可选)

启用 ESP_LV_ADAPTER_ENABLE_FREETYPE 并在 LVGL 中开启 FreeType 后,可以从文件或内存加载矢量字体:

#include "esp_lv_adapter.h"

// 从文件加载字体
esp_lv_adapter_ft_font_handle_t fh;
esp_lv_adapter_ft_font_config_t cfg = {
    .name = "F:my_font.ttf",         // 字体文件路径(注意:mmap 资源格式,冒号后不带斜杠)
    .size = 30,                      // 字号
    .style = ESP_LV_ADAPTER_FT_FONT_STYLE_NORMAL,
};
ESP_ERROR_CHECK(esp_lv_adapter_ft_font_init(&cfg, &fh));
const lv_font_t *font30 = esp_lv_adapter_ft_font_get(fh);

// 使用字体
lv_obj_set_style_text_font(label, font30, 0);

堆栈配置

LVGL 版本 必要配置 说明
v8 CONFIG_ESP_MAIN_TASK_STACK_SIZE=32768 字体初始化在调用线程执行
v9 CONFIG_LV_DRAW_THREAD_STACK_SIZE=32768 字体渲染在绘图线程执行

限制

  • LVGL v8:不支持 LVGL 虚拟文件系统(lv_fs),需使用直接文件路径或内存缓冲区
  • LVGL v9:支持虚拟文件系统路径(如 "F:font.ttf"

FPS 与 Dummy Draw

FPS 统计

需要启用 ESP_LV_ADAPTER_ENABLE_FPS_STATS

// 启用 FPS 统计
ESP_ERROR_CHECK(esp_lv_adapter_fps_stats_enable(disp, true));

// 获取当前 FPS
uint32_t fps;
if (esp_lv_adapter_get_fps(disp, &fps) == ESP_OK) {
    printf("Current FPS: %lu\n", fps);
}

Dummy Draw

Dummy Draw 模式绕过 LVGL 渲染,直接控制显示:

ESP_ERROR_CHECK(esp_lv_adapter_set_dummy_draw(disp, true));
ESP_ERROR_CHECK(esp_lv_adapter_dummy_draw_blit(disp, 0, 0, 800, 480, framebuffer, true));
ESP_ERROR_CHECK(esp_lv_adapter_set_dummy_draw(disp, false));

完整示例请参考 examples/display/gui/lvgl_dummy_draw

立即刷新

强制进行同步刷新:

esp_lv_adapter_refresh_now(disp);

区域对齐(Area Rounding)

某些显示面板要求绘制区域的宽度和高度必须是特定值的倍数(例如 8 的倍数)。适配器提供了区域对齐回调功能,可以在刷新到硬件之前对绘制区域进行舍入对齐。

使用场景:

  • 面板硬件要求绘制区域必须对齐到特定边界(如 8 像素对齐)
  • 某些 DMA 传输需要对齐的缓冲区大小
  • 优化硬件传输效率

示例代码:

// 定义对齐回调函数(将区域对齐到 8 的倍数)
void area_rounder_cb(lv_area_t *area, void *user_data)
{
    // 获取对齐值(例如 8)
    uint32_t align = *(uint32_t *)user_data;
    
    // 向下对齐 x1 和 y1
    area->x1 = (area->x1 / align) * align;
    area->y1 = (area->y1 / align) * align;
    
    // 向上对齐 x2 和 y2
    area->x2 = ((area->x2 + align - 1) / align) * align;
    area->y2 = ((area->y2 + align - 1) / align) * align;
}

// 注册对齐回调
uint32_t align_value = 8;  // 对齐到 8 像素
ESP_ERROR_CHECK(esp_lv_adapter_set_area_rounder_cb(disp, area_rounder_cb, &align_value));

// 禁用对齐回调(传入 NULL)
ESP_ERROR_CHECK(esp_lv_adapter_set_area_rounder_cb(disp, NULL, NULL));

注意事项:

  • 回调函数会在每次刷新区域之前被调用
  • 回调函数应该修改传入的 lv_area_t 结构体以调整区域边界
  • 传入 NULL 作为回调函数可以禁用区域对齐功能
  • user_data 参数可以用于传递对齐值或其他配置信息

睡眠管理

在保持 UI 状态的同时安全地关闭显示以降低功耗。适配器提供了与 ESP-IDF Light Sleep 配合使用的睡眠机制。

基本流程:

// 进入睡眠
esp_lv_adapter_sleep_prepare();      // 暂停 worker,等待刷新完成
esp_lcd_panel_del(panel);             // 删除硬件
esp_light_sleep_start();              // 进入 Light Sleep(CPU 暂停,外设保持状态)

// 从睡眠恢复(Light Sleep 唤醒后自动执行)
panel = /* 重新初始化 LCD 硬件 */;
esp_lv_adapter_sleep_recover(disp, panel, panel_io);  // 重新绑定面板,恢复 worker

配合 Light Sleep 使用:

  1. 进入睡眠前:调用 esp_lv_adapter_sleep_prepare() 暂停适配器任务并等待当前刷新完成
  2. 删除硬件:调用 esp_lcd_panel_del() 释放显示硬件资源
  3. 进入 Light Sleep:调用 esp_light_sleep_start(),系统将暂停 CPU 并保持外设状态
  4. 唤醒后恢复:重新初始化 LCD 硬件,然后调用 esp_lv_adapter_sleep_recover() 恢复适配器运行

主要特性:

  • UI 状态保留(无需重建控件)
  • 触摸设备保持注册(需要时单独关闭电源)
  • 多屏幕:为每个显示调用 sleep_recover()
  • 与 Light Sleep 无缝配合,实现低功耗待机

⚠️ 高级用法: 可使用 esp_lv_adapter_pause()/resume() 实现自定义流程,但请勿与 sleep_prepare() 混用


配置项 (Kconfig)

通过 idf.py menuconfig 可以配置以下选项:

配置项 说明
ESP_LV_ADAPTER_ENABLE_FS 启用文件系统桥接 (esp_lv_fs)
ESP_LV_ADAPTER_ENABLE_DECODER 启用图片解码 (esp_lv_decoder)
ESP_LV_ADAPTER_ENABLE_FREETYPE 启用 FreeType 矢量字体支持
ESP_LV_ADAPTER_ENABLE_FPS_STATS 启用 FPS 统计功能
ESP_LV_ADAPTER_ENABLE_BUTTON 启用按键导航输入
ESP_LV_ADAPTER_ENABLE_KNOB 启用旋钮/编码器输入

默认任务栈大小

  • LVGL 适配器任务栈:8KB

限制与注意事项

接口与防撕裂支持

接口类型 支持的防撕裂模式
RGB / MIPI DSI NONE, DOUBLE_FULL, TRIPLE_FULL, DOUBLE_DIRECT, TRIPLE_PARTIAL
OTHER (SPI/I2C/I80/QSPI) NONETE_SYNC

旋转限制

  • RGB/MIPI DSI

    • TEAR_AVOID_MODE_NONE不支持旋转(任何非 0 旋转会被拒绝)
  • OTHER (SPI/I2C/I80/QSPI)

    • 适配器不对 90°/270° 进行旋转处理
    • 如需旋转,请在 LCD 初始化阶段配置面板方向(交换 XY/镜像)
    • 同时需要相应调整触摸坐标映射
  • MONO (单色显示)

    • 完全支持旋转(0°/90°/180°/270°)
    • 旋转由 LVGL 在软件层面处理
    • 支持 I1 水平平铺 (HTILED) 和垂直平铺 (VTILED) 两种布局
    • 无需额外配置,直接通过 rotation 参数设置

缓冲与渲染模式

  • 模式映射

    • DOUBLE_FULL / TRIPLE_FULL → FULL 渲染模式
    • DOUBLE_DIRECT → DIRECT 渲染模式
    • 其余(含 TRIPLE_PARTIAL)→ PARTIAL 渲染模式
  • LVGL 绘制缓冲数量

    • TRIPLE_PARTIAL:1 块
    • TRIPLE_FULL / DOUBLE_*:2 块
    • FULL/DIRECT 模式默认:2 块
    • 其他:1 块
  • OTHER + NONE

    • 不申请面板帧缓冲
    • 仅使用 LVGL 绘制缓冲的条带方式发送数据

PPA/DMA2D 加速

  • 启用 PPA 加速时,建议设置 LV_DRAW_SW_DRAW_UNIT_CNT == 1
  • 旋转操作优先使用 PPA;否则使用 CPU 拷贝路径(带缓存局部性的块拷贝)
  • 效果与预期
    • PPA 可显著降低 CPU 占用
    • 通常不会带来明显的 FPS 提升

反初始化

关闭 UI 时的推荐资源释放顺序:

// 1. 注销输入设备
esp_lv_adapter_unregister_touch(touch);
// esp_lv_adapter_unregister_encoder(encoder);
// esp_lv_adapter_unregister_navigation_buttons(buttons);

// 2. 注销显示设备
esp_lv_adapter_unregister_display(disp);

// 3. 卸载文件系统(如果使用)
esp_lv_adapter_fs_unmount(fs_handle);
// 释放 mmap 资源
mmap_assets_del(assets);

// 4. 反初始化适配器
// 注意:如果启用了 FreeType,字体资源会自动清理
esp_lv_adapter_deinit();

兼容性

  • ESP-IDF:>= 5.5
  • LVGL:>= 8, < 10(支持 v8 和 v9)

ESP-IDF 补丁说明

PPA 卡死问题修复(ESP32-P4)

如果您在 ESP32-P4 上使用 ESP_LV_ADAPTER_TEAR_AVOID_MODE_TRIPLE_PARTIAL 模式并启用屏幕旋转时遇到画面卡死问题,需要应用以下补丁:

适用版本:ESP-IDF release/v5.5 (commit 62beeae461bd3692c2028f96a93c84f11291e155)

补丁文件0001-bugfix-lcd-Fixed-PPA-freeze.patch

应用方法

在 ESP-IDF 仓库根目录下执行:

cd $IDF_PATH
git apply /path/to/esp_lvgl_adapter/0001-bugfix-lcd-Fixed-PPA-freeze.patch

问题说明

  • 该补丁修复了 ESP32-P4 在 ESP_LV_ADAPTER_TEAR_AVOID_MODE_TRIPLE_PARTIAL 模式下使能旋转时,PPA 硬件加速导致画面卡死的问题
  • 仅影响同时满足以下条件的场景:
    • 使用 ESP32-P4 芯片
    • 使用 TRIPLE_PARTIAL 防撕裂模式
    • 启用了屏幕旋转(90°/270°)
  • 其他配置组合或芯片不需要此补丁

验证补丁是否已应用

检查 components/esp_driver_ppa/src/ppa_srm.c 文件中是否包含以下代码:

PPA.sr_byte_order.sr_macro_bk_ro_bypass = 1;

示例

示例 路径 说明
通用演示 examples/display/gui/lvgl_common_demo 统一的 LVGL 演示,支持多种接口
多屏示例 examples/display/gui/lvgl_multi_screen 多显示器管理示例
解码演示 examples/display/gui/lvgl_decode_image 图片解码功能演示
字体演示 examples/display/gui/lvgl_freetype_font FreeType 字体加载与使用
Dummy Draw examples/display/gui/lvgl_dummy_draw Dummy Draw 切换

常见问题

FreeType 崩溃或显示异常

解决方法

  • 确认 LVGL 配置中已启用 FreeType(CONFIG_LV_USE_FREETYPE=y
  • LVGL v8:设置 CONFIG_ESP_MAIN_TASK_STACK_SIZE=32768
  • LVGL v9:设置 CONFIG_LV_DRAW_THREAD_STACK_SIZE=32768

屏幕撕裂或闪烁

可能原因

  • 防撕裂模式选择不当
  • 帧缓冲数量不正确
  • 旋转配置不当

解决方法

  1. 根据应用场景选择合适的防撕裂模式
  2. 使用 esp_lv_adapter_get_required_frame_buffer_count() 计算正确的帧缓冲数

图片解码或文件系统不可用

可能原因

  • Kconfig 选项未启用
  • 组件依赖缺失
  • 资源分区未正确烧录

解决方法

  1. 检查相应的 Kconfig 开关
  2. 确认 esp_mmap_assets 等依赖组件已添加
  3. 验证资源分区是否正确打包并烧录到 Flash

LVGL API 调用崩溃

可能原因

  • 在多线程环境中未加锁访问 LVGL API

解决方法

// 所有 LVGL API 调用都必须加锁保护
if (esp_lv_adapter_lock(-1) == ESP_OK) {
    // LVGL API 调用
    esp_lv_adapter_unlock();
}

术语表

术语 说明
面板帧缓冲 由显示面板驱动持有的硬件帧缓冲区
LVGL 绘制缓冲 LVGL 用于渲染条带或整帧后再刷新到面板的 RAM 区域
分区刷新 仅更新屏幕的部分区域(脏区域)而非整屏刷新
直写(Direct) LVGL 直接渲染到面板帧缓冲,减少内存拷贝;适合小区域刷新
PPA Pixel Processing Accelerator(像素处理加速器),用于硬件加速旋转/缩放/内存拷贝等操作
防撕裂 通过多缓冲等技术避免屏幕更新时出现撕裂现象

参考


许可证

Apache-2.0


相关链接