LVGL显示移植:lv_port_disp.c 详情介绍

lv_port_disp.c 详情介绍

lv_port_disp.c 是 LVGL 显示移植核心,负责把 LVGL 渲染结果从内存缓冲刷到物理屏幕。

可以把它理解为 3 个阶段:初始化显示对象、接收刷新区域、完成硬件提交。

1. 文件职责与执行时序

  1. LVGL 初始化时调用 lv_port_disp_init() 完成显示驱动注册。
  2. LVGL 产生重绘后触发 flush_cb(即 disp_flush(...))。
  3. disp_flushcolor_p/px_map 指向的像素数据送入 LCD 控制器或 SPI 驱动。
  4. 提交完成后通知 LVGL 刷新结束(LVGL8 常见为 lv_disp_flush_ready)。

如果第 4 步缺失,现象通常是界面只刷一帧后卡死。

2. ra6m3-hmi-board 实现要点

对应文件:bsp/renesas/ra6m3-hmi-board/board/lvgl/lv_port_disp.c

  1. 显示接口

    • 使用 GLCDC;
    • 通过 R_GLCDC_BufferChange(...) 在刷新时切换到新帧缓冲。
  2. Vsync 同步

    • 通过 DisplayVsyncCallback(...) + _SemaphoreVsync 做同步;
    • 目的:在正确时机切缓冲,减少 tearing(撕裂)。
  3. LVGL8/LVGL9 双分支

    • LVGL8 分支使用 lv_disp_drv_t / lv_disp_draw_buf_t
    • LVGL9 分支使用 lv_display_createlv_display_set_flush_cblv_display_set_buffers
  4. 缓冲模式

    • 使用 fb_background 双缓冲(direct render 路径);
    • 分辨率变化后要同步检查缓冲大小与底层显存定义。

3. ra6m3-ek 实现要点

对应文件:bsp/renesas/ra6m3-ek/board/lvgl/lv_port_disp.c

  1. 两条输出路径

    • PKG_USING_ILI9341:SPI 屏路径,调用 lcd_fill_array_spi(...)
    • 非 SPI 分支:直接写 framebuffer(RGB/LCD 设备)。
  2. 软件拷贝路径

    • 非 GPU 分支下,逐像素把 LVGL 颜色写入 framebuffer;
    • 包含区域裁剪逻辑(防止越界写显存)。
  3. 显存缓冲

    • buf_1 通过链接段放在固定地址;
    • COLOR_BUFFER = LV_HOR_RES_MAX * LV_VER_RES_MAX / 4,表示四分之一屏缓冲。
  4. 刷新完成通知

    • disp_flush 末尾调用 lv_disp_flush_ready(disp_drv)
    • 该调用必须在所有输出分支都可达。

4. 常改参数与接口

  1. 分辨率联动

    • disp_drv.hor_res/ver_res(LVGL8)或 lv_display_create 参数(LVGL9);
    • 必须与 lv_conf.hLV_HOR_RES_MAX/LV_VER_RES_MAX 一致。
  2. flush 提交函数

    • RGB 屏:替换为平台 LCD 控制器切缓冲/拷贝接口;
    • SPI 屏:替换为 SPI 批量刷图函数。
  3. 缓冲策略

    • 单缓冲:省内存,可能更卡;
    • 双缓冲:更流畅,RAM 占用更高;
    • 部分缓冲(如 1/4 屏):折中方案,常用于 RAM 紧张场景。
  4. 同步机制

    • 使用 Vsync/中断/信号量时,要避免死等和时序反转;
    • flush 偶发卡住,优先排查信号量初值与回调触发条件。

5. 常见故障与定位

  • 黑屏:确认 lv_port_disp_init() 是否被调用、LCD 设备是否打开成功、分辨率是否一致。
  • 只刷一次:确认每次 flush 都有 flush_ready 通知。
  • 花屏/偏色:核对 LV_COLOR_DEPTH、字节序、像素格式转换逻辑。
  • 撕裂明显:检查是否在 Vsync 同步点提交缓冲。
  • 拖动卡顿:检查 COLOR_BUFFER 大小、是否走了低效逐像素路径、SPI 时钟是否过低。

6. 推荐调试步骤(显示链路)

  1. 先用纯色填充测试确认"能亮屏";
  2. 再跑简单控件(label/button)确认 flush 连续触发;
  3. 再引入动画或列表滚动观察帧率与撕裂;
  4. 最后再开 GPU/2D 加速做性能优化。

7. lv_port_disp_init() 详解

lv_port_disp_init() 是显示移植的入口函数,它决定了 LVGL 后续能否正常刷新。

核心任务可以概括为 5 步:准备硬件资源 -> 准备绘图缓冲 -> 创建显示驱动对象 -> 绑定刷新回调 -> 注册到 LVGL

7.1 通用流程(抽象视角)

  1. 准备底层显示资源

    • 打开 LCD/SPI 设备,或准备 GLCDC 控制器;
    • 可选:创建同步资源(如 Vsync 信号量)。
  2. 初始化 draw buffer

    • 告诉 LVGL 使用哪块 RAM 作为绘图缓冲;
    • 决定单缓冲、双缓冲或部分缓冲策略。
  3. 创建并配置 display driver/display 对象

    • LVGL8:lv_disp_drv_init(...)
    • LVGL9:lv_display_create(...)
  4. 绑定刷新回调

    • disp_flush(...) 绑定给 LVGL;
    • 可选绑定等待回调(如 Vsync wait)。
  5. 完成注册

    • LVGL8:lv_disp_drv_register(...)
    • LVGL9:对象创建并设置完成后即生效。

7.2 ra6m3-hmi-board 中的初始化细节

对应:bsp/renesas/ra6m3-hmi-board/board/lvgl/lv_port_disp.c

  1. 先创建 _SemaphoreVsync,用于刷新同步;
  2. LVGL8 路径:
    • lv_disp_draw_buf_init(&disp_buf, &fb_background[0][0], &fb_background[1][0], sizeof(fb_background[0]))
    • 双缓冲直接对接 fb_background
    • 设置 disp_drv.hor_res/ver_resflush_cb
    • lv_disp_drv_register(&disp_drv) 完成注册。
  3. LVGL9 路径:
    • lv_display_create(LV_HOR_RES_MAX, LV_VER_RES_MAX)
    • 设置 lv_display_set_flush_cb(...)lv_display_set_flush_wait_cb(...)
    • lv_display_set_buffers(..., LV_DISPLAY_RENDER_MODE_DIRECT) 配置 direct 模式双缓冲。
  4. 最后置 lvgl_init_flag = 1,使 Vsync 回调开始真正参与同步。

7.3 ra6m3-ek 中的初始化细节

对应:bsp/renesas/ra6m3-ek/board/lvgl/lv_port_disp.c

  1. 先走硬件分支初始化:
    • SPI 屏分支:spi_lcd_init(20)
    • RGB/LCD 分支:查找并打开 lcd 设备,读取 rt_device_graphic_info
  2. 初始化绘图缓冲:
    • lv_disp_draw_buf_init(&disp_buf, buf_1, NULL, COLOR_BUFFER)
    • 当前是单缓冲(第二缓冲传 NULL)。
  3. 初始化显示驱动:
    • lv_disp_drv_init(&disp_drv)
    • 设置 hor_res/ver_resdraw_bufflush_cb
  4. 可选 GPU 路径:
    • 若使能 DLG_LVGL_USE_GPU_RA6M3,会额外 lv_port_gpu_init()
  5. lv_disp_drv_register(&disp_drv) 完成注册。

7.4 这个函数最容易出错的点

  • 分辨率不一致lv_conf.hlv_port_disp_init() 设置不一致,会导致越界、显示异常或黑屏。
  • 缓冲大小不够 :改分辨率后没同步改 COLOR_BUFFER 或底层 framebuffer。
  • 回调未绑定flush_cb 未设置或被条件编译排除,界面不刷新。
  • 设备未打开rt_device_find/open 失败,后续写 framebuffer 会异常。
  • 同步对象时序问题 :Vsync 信号量初值、等待时机不当,可能导致 flush 卡死。

7.5 建议的最小验证法(只测 init)

  1. 断点或日志确认 lv_port_disp_init() 执行到末尾;
  2. 输出当前分辨率、缓冲地址和缓冲长度;
  3. 统计 disp_flush 调用次数(确认刷新链路已建立);
  4. 屏幕显示单个静态 label(先不加动画)验证基本通路。
相关推荐
xzl043 天前
LVGL输入移植:lv_port_indev.c 详情介绍
lvgl·ra6m3-hmi-board
10Eugene3 天前
LVGL V9增加实体按键 驱动层和应用层的处理
c·lvgl
xzl046 天前
PL111 RGB LCD时序配置详解
rt-thread·pl111
xzl047 天前
LVGL 启动流程全解析:RT-Thread 下的界面渲染链路
rt-thread·lvgl
xzl047 天前
LVGL Coffee UI 接入实战:问题解决全记录
ui·rt-thread·lvgl
xzl0412 天前
GLCDC参数全解析:从配置到亮屏的完整指南
renesas·ra6m3-hmi-board
神一样的老师13 天前
【兆易创新GD32VW553开发板试用】 BSP 从 GitHub 下载与编译指南
单片机·github·rt-thread
xzl0413 天前
【Menuconfig】RT-Thread配置菜单第一级
rt-thread
xzl0414 天前
瑞萨 FSP 和 STM32 HAL 库的启动流程核心差异
stm32·单片机·嵌入式硬件·rt-thread