帧率

1. 介绍

1.1 帧率定义

帧率指单位时间内屏幕显示的帧数,思澈方案采用2 秒内平均帧数作为统计标准,直接反映 UI 交互的流畅度(通常 60Hz 为理想流畅标准,对应每帧渲染耗时≤16.7ms)。

1.2 核心影响因素

帧率表现由两大环节共同决定,任一环节耗时过长均会导致卡顿:

  1. 页面渲染耗时:LVGL 图形库处理控件布局、图像绘制、数据计算的时间;

  2. LCD 送数时间:CPU/GPU 将渲染完成的帧数据传输至 LCD 显示屏的时间。

2. 刷新周期协同机制

LVGL 刷新周期与 LCD TE 周期的协同是保证帧率稳定的基础,二者需合理配置以避免画面撕裂或资源浪费。

2.1 LVGL 刷新周期

配置方式:在config中设置,例如目标帧率 60Hz 时,配置周期为 16ms(1000ms/60≈16.7ms);

作用:决定图形库多久触发一次渲染任务,是 UI 更新的 “调度时钟”。

2.2 LCD TE 周期

  • TE 信号定义:LCD 完成一帧显示后发出的同步脉冲,标志着下一帧可开始传输数据,是避免画面撕裂的核心信号;

  • 协同原则:

    • 优先配置:LVGL 刷新周期与 TE 周期保持一致(如均为 16ms),确保渲染与显示节奏匹配;

    • 兼容配置:若需统一驱动适配,可设置 LVGL 刷新周期小于 TE 周期(如 10ms),避免因渲染延迟错过 TE 同步点。

3. 帧率关键影响因素解析

3.1 控件与渲染效率

  • 控件开销:依赖软件处理的控件(arc、label、chart)会显著增加渲染耗时,尤其 chart 包含大量小尺寸子控件时,递归遍历成本极高;

  • 控件数量:LVGL 采用递归方式处理控件树,控件总数过多(如单页>50 个复杂控件)会导致递归耗时占比超 30%。

3.2 图像资源负载

  • 图片体积过大:高分辨率、带透明度的图片会占用更多 GPU 算力;

  • 旋转图片低效:针对图片旋转操作效率低下的问题,在 SRAM 资源充足的场景下,可配置专属的 SRAM 缓存区域(建议分配大小不小于60KB)用于旋转分块处理。渲染阶段检测到图片旋转指令时,会依据该专属 SRAM 的大小,将整张图片按行切分为若干数据块,逐块完成旋转计算与渲染输出,而非对整张图片一次性进行旋转处理,以此大幅提升图片旋转的渲染效率。

3.3 线程与数据操作

  • Flash 读写阻塞:UI 线程直接操作 Flash 会导致渲染任务被中断(单次读写耗时可达毫秒级);

4. 帧率优化实战方案

4.1 硬件加速与渲染优化

  • 启用硬件加速:升级至Solution最新版本(≥ V2.3),arc、label 控件支持硬件加速后,渲染效率提升约 50%;

  • 快照替换高耗控件:将复杂控件(如多子控件组合、动态图表)初始化后转为快照(snapshot),后续渲染直接使用图片,减少递归与计算开销。

4.2 页面与资源拆分

  • 长页面动态加载: 使用lvsf_multlist控件实现分页加载,仅渲染当前可视区域内容,避免一次性处理海量控件。参考示例:mainmenu_list.csetting_main_gui.c

  • 控件驻留机制优化: 使用lvsf_multlist控件时,启用LV_MULTLIST_FLAG_RESIDENCY标志位,使已创建的页面控件在划出屏幕可视区域后仍保持驻留状态,而非直接销毁。此举可避免页面回滚至该位置时重复执行控件创建与销毁操作,有效节省控件生命周期管理的时间开销。

4.3 控件布局刷新

  • LVGL V8 及后续版本中,控件创建时直接获取的宽高(lv_obj_get_width/height)和位置(lv_obj_get_x/y)信息为无效值,需调用布局刷新接口后才能获取真实数据。原生接口lv_obj_update_layout会刷新当前屏幕(screen)下所有控件的尺寸与位置,全量刷新耗时较高;建议替换为精细化接口 ——lv_obj_refr_size(仅刷新目标控件尺寸)和lv_obj_refr_pos(仅刷新目标控件位置),可大幅降低布局计算的时间开销。

5. LCD推屏能力和理论帧率计算

送数时长,以QSPI为例:

  • QSPI 是4个数据线, 在RGB565模式下 一个像素点(16bit)数据需要16/4个时钟周期

  • 送一帧全屏数据耗时(us) = 图形宽 * 图形高 * 每个像素需要的时钟周期 / QSPI速率

  • 如16bit的410x494的屏,在48M的速率下:t = 410 x 494 * 4 / 48 000 000 = 16.87(ms),在这种条件下,帧率就不能跑60hz了,会导致lcd自刷新时,数据还未送完出现撕裂

具体可参见 理论推屏帧率计算方法

6. 调试方法

6.1 抓点工具sysview

使用方法参见文档Systemview工具抓点

6.2 调试指令

以下的finsh指令不带传参的都是发一次开启,再发一次关闭。

  1. 查看每个刷新周期耗时:task_time

    • 发送一次仅打印每个周期task handle的耗时

    ../../_images/task_time_1s.png
    • 发送两次则在一的基础上打印indev 以及refr 的耗时

    ../../_images/task_time_2s.png
    • 发送四次则关闭打印

  2. 查看字体的log:ft_log

  3. 查看每个页面的msg handle(start/resume/pause/stop)耗时:app_sche_print_perf_tick 1

  4. 查看一级页面msg handle(start/resume/pause/stop)耗时:tileview_fwk

  5. 调试打印时间的接口

  • 毫秒级的可以直接使用rt_tick_get

  • 微秒级使用cpu_get_hw_us

统计某个行为的耗时,直接使用上面的接口计算该行为前后的时间差即可