23. HPM68系列:多图层仪表盘方案(Multi-Layer Dashboard)

23.1. 依赖 SDK 1.11.0

23.2. 概述

本方案基于先楫半导体 HPM6800EVK 平台,结合 LVGLFreeRTOSLCDCPDMA,实现了一套数字仪表场景的多图层 Dashboard 演示工程。

与传统“整屏重绘”的界面方案不同,本项目将仪表盘拆分为 1 个全屏基础层 + 7 个局部硬件叠加层,总计 八图层。其中大面积静态背景与低频元素保留在主层,高频变化元素(如双指针、左右能量条、顶部转向灯、底部告警条、中央档位区)分别独立成层,从而降低无效刷新面积,减轻内存带宽压力,并提升动画流畅度。

本方案重点展示以下内容:

  • 八图层仪表盘架构:基于 LCDC 8 层能力拆分 UI 区域

  • 分层渲染策略:静态内容与高频动态内容分离绘制

  • 缓冲区优化设计:主层与子层采用不同的 buffer 策略

  • PDMA 刷新链路:利用 PDMA 完成脏区域搬运,降低 CPU 参与度

23.3. 核心特性

23.3.1. 多图层显示特性

  • 八硬件层并行合成:主背景层配合 7 个 ARGB8888 透明叠加层

  • 图层独立刷新:不同区域按各自更新频率独立输出到 LCDC

  • 透明混合显示:各层使用 src_over 混合模式叠加到最终画面

  • 局部区域专层承载:将高频动画控件限制在较小矩形区域内

23.3.2. 动态 UI 特性

  • 双仪表指针动画:速度表与转速表分别独立刷新

  • 左右能量条动画:左右侧竖向条形区域独立更新

  • 告警/指示灯动画:转向、雾灯、远近光、安全带、手刹等图标独立控制

  • 档位滚轮与数值显示:中心区域单独分层,避免影响整屏

  • 时间/温度/续航信息展示:作为主层静态或低频控件统一显示

23.3.3. 渲染与性能特性

  • 主层 Direct Render:全屏主显示采用 LV_DISPLAY_RENDER_MODE_DIRECT

  • 子层 Full Render:局部层采用 LV_DISPLAY_RENDER_MODE_FULL

  • 双缓冲切换:主层和 7 个子层均使用双缓冲

  • PDMA 脏区搬运:主层 flush 时仅搬运脏区域到 LCDC 扫描缓冲

  • D-Cache 协同:CPU/DMA 共享内存前后执行 writeback / invalidate

23.4. 硬件要求

23.4.1. 主控板要求

  • MCU: HPM6800EVK

  • 显示输出: 1920 x 720 仪表盘界面

  • 显示控制器: 使用片上 LCDC

  • DMA 加速: 使用片上 PDMA

  • 运行内存: 需要较大 SDRAM 以容纳多组 ARGB8888 帧缓冲

23.5. 设备连接

23.5.1. 硬件连接示意图

connect

23.6. 创建工程

generate_project

23.6.1. 软件组件

  • LVGL

  • FreeRTOS

  • hpm_panel

  • LCDC 驱动

  • PDMA 驱动

23.7. 软件架构

23.7.1. 系统框架

┌──────────────────────────────────────────────┐
│              Dashboard UI Layer              │
│   (背景、指针、档位、告警、能量条等多区域)      │
└────────────────┬─────────────────────────────┘
				 │
┌────────────────┴─────────────────────────────┐
│           LVGL Multi-Display Layer           │
│  (主层 Direct Render + 子层 Full Render)     │
└────────────────┬─────────────────────────────┘
				 │
┌────────────────┴─────────────────────────────┐
│         PDMA + D-Cache Coherency             │
│   (脏区搬运 + cache writeback/invalidate)     │
└────────────────┬─────────────────────────────┘
				 │
┌────────────────┴─────────────────────────────┐
│            LCDC Hardware Compose             │
│   (8层硬件叠加 + Alpha Blend 输出)            │
└──────────────────────────────────────────────┘

23.7.2. 任务结构

  • LVGL 刷新任务:循环调用 lv_timer_handler(),驱动界面与动画

  • 显示输出链路:LCDC 负责主层与 7 个局部层的最终混合输出

  • VSYNC 同步链路:LCDC 中断用于刷新同步和显示切换确认

23.8. 八图层设计

23.8.1. 图层设计示意

背景层示意图background

图层拆分示意图layer

效果动图layer

23.8.2. 图层划分说明

本项目总共使用 8 个硬件显示层,其中 1 个为全屏主层,7 个为局部叠加层。代码中主层在 user_lvgl_port.c 中初始化,局部层在 ui/screens/home_gen.c 中分别初始化。

硬件层

软件对象

区域尺寸

主要内容

设计目的

Layer 0

主显示 disp

1920 x 720

背景图、发动机图标、油量/水温图标、日期、时间、温度、续航等

承载全屏背景与低频刷新元素

Layer 1

home_layer2

352 x 350

左侧速度表指针

将高频旋转指针从主层剥离

Layer 2

home_layer3

352 x 350

右侧转速表指针

独立刷新另一支高频指针

Layer 3

home_layer4

64 x 512

左侧竖向能量条

只刷新左侧条形区域

Layer 4

home_layer5

64 x 512

右侧竖向能量条

只刷新右侧条形区域

Layer 5

home_layer6

1408 x 100

底部告警/灯光图标带

集中管理底部状态图标

Layer 6

home_layer7

640 x 64

顶部左右转向灯图标

单独实现闪烁动画

Layer 7

home_layer8

256 x 345

中央速度值、档位滚轮、档位标签

中央交互区独立刷新

23.8.3. 图层拆分原则

  • 高频动画单独成层:速度指针、转速指针、左右能量条均是高频更新对象

  • 相同语义对象集中成层:底部状态灯集中到一条横向层,顶部转向灯集中到单独层

  • 保持主层稳定:背景、时间、温度等低频元素放在主层,减少全屏重绘次数

  • 区域最小化:每个子层尽量采用包围盒尺寸,降低每层双缓冲成本

23.9. 渲染策略

23.9.1. 1. 主层采用 Direct Render

software/inc/lv_app_conf.h 中启用了:

  • LV_USE_HPM_MODE_DIRECT = 1

  • LV_USE_HPM_PDMA_FLUSH = 1

主层在 software/src/user_lvgl_port.c 中通过:

  • lv_display_set_buffers(disp, user_lvgl_fb0, user_lvgl_fb1, USER_LVGL_FB_SIZE, LV_DISPLAY_RENDER_MODE_DIRECT)

配置为 Direct Render 双缓冲模式

这样做的意义是:

  • LVGL 直接在全屏 draw buffer 上组织最终主层画面

  • 当存在多个脏区域时,不必为每个小区域单独维护复杂的局部缓冲布局

  • 在仪表盘这种大背景 + 少量动态更新的场景下,便于配合 PDMA 做脏区搬运

23.9.2. 2. 主层 Flush 使用 PDMA 搬运脏区

主层 flush 回调 user_lvgl_display_flush_cb() 的策略为:

  • 先累计 LVGL 上报的脏区域

  • 对脏区域对应的 draw buffer 数据执行 D-Cache writeback

  • 使用 PDMA 将脏区域从 px_map 拷贝到 user_lvgl_lcdc_fb

  • 搬运结束后调用 lv_display_flush_ready()

这种方式的好处是:

  • 减少 CPU 逐像素搬运开销

  • 减少全屏 buffer 直接切换导致的显示抖动风险

  • 兼顾 Direct Render 的开发便利性与 LCDC 扫描缓冲的稳定性

23.9.3. 3. 子层采用 Full Render

7 个局部层都通过如下方式创建:

  • lv_display_create(width, height)

  • lv_display_set_color_format(..., LV_COLOR_FORMAT_ARGB8888)

  • lv_display_set_buffers(..., buf0, buf1, sizeof(buf0), LV_DISPLAY_RENDER_MODE_FULL)

选择 FULL 模式的原因是:

  • 子层尺寸远小于全屏,整层重绘成本可控

  • 逻辑更简单,flush 时直接切换该层下一帧 buffer 即可

  • 局部层通常对应单一功能区域,天然适合“整层更新”

23.9.4. 4. LCDC 负责最终多层合成

每个子层都配置为:

  • 像素格式:ARGB8888

  • 背景透明:lv_obj_set_style_bg_opa(..., LV_OPA_TRANSP, 0)

  • 混合模式:display_alphablend_mode_src_over

因此,LCDC 最终会把主层与各局部层按照硬件层次关系实时混合输出,避免软件端反复做整屏合成。

23.10. 缓冲区选择与设计

23.10.1. 主层缓冲区

主层使用 3 组全屏缓冲:

  • user_lvgl_fb0:LVGL 绘制缓冲 0

  • user_lvgl_fb1:LVGL 绘制缓冲 1

  • user_lvgl_lcdc_fb:LCDC 实际扫描输出缓冲

其中:

  • user_lvgl_fb0 / user_lvgl_fb1 组成 双缓冲绘制面

  • user_lvgl_lcdc_fb 作为 稳定前台显示面

  • PDMA 将脏区域从当前绘制缓冲搬运到扫描缓冲,兼顾性能与显示稳定性

23.10.2. 子层缓冲区

每个子层都各自分配两块独立缓冲,例如:

  • layer2_buf0 / layer2_buf1

  • layer3_buf0 / layer3_buf1

  • layer8_buf0 / layer8_buf1

这些缓冲统一具备以下特征:

  • 放置在 .framebuffer

  • HPM_L1C_CACHELINE_SIZE 对齐

  • 像素格式统一为 ARGB8888

  • flush 时通过 lcdc_layer_set_next_buffer() 切换到下一帧

23.10.3. 为什么这样选 buffer

  • 主层面积大:更适合 Direct Render + PDMA 脏区拷贝

  • 子层面积小:更适合 Full Render + 独立双缓冲翻转

  • 全层独立:任一局部动画不会迫使其它层重绘

  • 带宽更可控:高频动画仅占用对应子层带宽

  • 结构更清晰:显示问题更容易按层定位和调试

23.11. 运行现象

23.11.1. 系统启动后

启动后可看到一个典型数字仪表盘界面,包含:

  • 中央速度数字显示

  • 左右双圆形仪表指针动画

  • 左右竖向能量条动态变化

  • 顶部左右转向灯闪烁

  • 底部灯光/安全带/手刹等图标状态显示

  • 中央档位滚轮和档位标签切换

23.11.2. 动画表现

  • 速度与转速指针可平滑转动

  • 顶部转向灯按定时器节奏闪烁

  • 底部告警图标可按组显示/隐藏

  • 左右能量条独立执行高度和颜色动画

23.12. 方案价值

23.12.1. 适用场景

  • 车载仪表盘

  • 智能座舱 HMI

  • 需要硬件层叠加优化的显示系统

23.12.2. 设计收益

  • 减少整屏刷新压力

  • 提升动画流畅度

  • 降低无效像素搬运

  • 更适合大分辨率 ARGB8888 显示场景

  • 便于后续扩展更多局部动态控件