ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32F746G-DISCO移植LVGL 9.0:性能基准测试与优化实战

2026/8/31 11:00:35 拓冰建站 浏览量
STM32F746G-DISCO移植LVGL 9.0:性能基准测试与优化实战 拿到STM32F746G-DISCO这块板子后我做的第一件事不是点灯而是把LVGL 9.0移植上去跑一个官方Demo。画面亮起来的一瞬间理智跟着回来了能显示和能流畅显示是两码事。很多移植教程止步于“显示成功”但真正决定项目能否交付的是复杂界面下的帧率、动画顺滑度、内存是否溢出。于是我把这次移植的重点放在了“性能基准测试”上。折腾一圈后结论很清晰性能测试不是跑分表演它的价值是帮你定位瓶颈出在CPU、内存、渲染路径还是驱动刷新方式上然后再决定怎么改。1. 为什么选择STM32F746G-DISCO来测LVGL 9.01.1 硬件条件适合做LVGL性能基线STM32F746G-DISCO是一块自带4.3英寸RGB接口LCD的开发板主控是Cortex-M7内核主频最高216MHz片内Flash和RAM不算大但板载了SDRAM这对LVGL这种需要帧缓冲的GUI来说非常关键。和很多没有RGB接口屏幕的板子相比这块板子直接把“屏幕点亮”这个门槛降低了可以把精力放在引擎和渲染优化上。同时STM32F746内置DMA2D图形加速器可以处理颜色格式转换、图像混合、填充等常见图形操作。对LVGL 9.0来说这意味着有些渲染操作可以从CPU卸载到DMA2D上但前提是驱动写对、缓冲策略匹配。测性能时DMA2D是否启用结果会差很多。1.2 LVGL 9.0的架构变化值得重新测一遍LVGL 9.0和8.x相比内部改动并不小。最明显的是对象模型、样式系统和渲染代码都有重构并且推荐使用新的驱动接口。很多基于8.x的软件设计习惯到了9.0需要调整。比如显示设备从lv_disp_drv_register变成了lv_display_create输入设备也从lv_indev_drv_register变成了lv_indev_create。架构重构带来的直接后果是8.x和9.0的性能表现不能简单类比。不能凭老经验说“我8.3跑得很流畅9.0也一定没问题”必须重新测试。LVGL 9.0对渲染硬件加速的接口更友好但这也意味着如果移植时没有正确启用相关功能性能可能反而不如8.x优化充分。所以移植后的基准测试不是一个可选项而是一个必要步骤。1.3 性能基线是项目选型和优化前提在实际产品开发中选哪款主控、要不要加外置RAM、要不要上RTOS、UI复杂度怎么设计都需要一个“基线数据”作为参考。比如一套界面里包含多个圆角卡片、实时曲线、键盘弹窗如果知道在这块MCU上渲染一帧UI大概需要多少毫秒就能推算最大刷新率也能判断是否需要切成局部刷新或者换用更简单的视觉风格。性能基线还负责回答一个问题当前硬件能不能承载这个UI框架的全部特性。很多项目在开发中期才发现帧率不够只能被迫删特效、降分辨率那时改动成本极高。所以先跑一遍基准测试把上限定出来后面所有UI设计都围绕这个上限来整体进度反而更快。2. 移植过程先把LVGL 9.0跑起来再谈测试2.1 获取源码并建立最小工程LVGL 9.0源码可以从GitHub获取也可以直接使用官方发布的Release包。移植时我建议不要从零开始创建工程而是先基于一个能点亮的裸机工程把LVGL的源码目录加入构建系统。最小工程需要包含LVGL源码目录src/下全部或按需裁剪。lv_conf.h配置文件如果工程里没有可以从lv_conf_template.h复制一份。显示驱动文件实现lv_display的flush回调。触摸驱动文件如果要用触摸实现输入设备回调。主循环周期性调用lv_timer_handler()。VSCode加上Embedded IDE插件、STM32CubeIDE、或Keil都可以。关键是保证编译器和C标准兼容。LVGL 9.0对C11的编译器要求更好老旧编译器很可能编译报错。2.2 关键配置项和常见错误在lv_conf.h里有几个配置项直接影响能不能跑起来和能不能测出真实性能。LV_COLOR_DEPTH常见选16位或32位。STM32F746G-DISCO的LCD配置为RGB888时驱动可能希望输出32位颜色但LVGL内部可以按16位计算在flush回调里做格式转换。颜色深度选得越高帧缓冲占用越大渲染时像素填充的开销也会增加。LV_MEM_SIZELVGL默认自带一个内存分配器。SDRAM不足或者堆太小时初始化或创建控件容易失败。我习惯把LV_MEM_SIZE设到足够大然后再逐步缩小找到真正需要的大小。LV_USE_GPU如果使用DMA2D需要开启相关宏并实现对应接口。LVGL 9.0里的GPU接口不是简单的“一键加速”它需要你实现指定的draw函数DMA2D才能介入。LV_USE_PERF_MONITOR和LV_USE_MEM_MONITOR这两个宏在调试性能时很有用会在屏幕上显示帧率和内存占用。最常见的错误是把lv_conf.h放在不正确的位置或者宏定义没有被编译器读到。另一个常见问题是LVGL 9.0中某些API改动网上很多8.x的代码直接拷过来会报错比如lv_disp_drv_t已经改名了需要按9.0的驱动例程写。2.3 显示驱动与触摸驱动的接入LVGL 9.0的显示驱动流程大致是lv_display_t *disp lv_display_create(LCD_WIDTH, LCD_HEIGHT); lv_display_set_buffers(disp, buf1, NULL, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL); lv_display_set_flush_cb(disp, my_disp_flush);在my_disp_flush回调里把LVGL给出的绘制缓冲区送到屏幕static void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) { uint32_t w lv_area_get_width(area); uint32_t h lv_area_get_height(area); // 调用底层LCD接口把px_map中的像素数据写到区域对应的显存 lcd_draw_rgb(area-x1, area-y1, w, h, px_map); lv_display_flush_ready(disp); }触摸驱动的接入类似先创建输入设备再注册读取回调。如果只做性能基准测试可以先用lv_indev_set_read_cb注册一个返回固定坐标的模拟回调保证测试过程中触摸数据不干扰渲染结果。2.4 用官方Demo验证基础渲染移植完成后先跑一个最简单的Label或者官方Benchmark Demo。如果画面能正常显示说明显示链路是通的。此时不要急着测帧率先确认颜色格式和刷新方向有没有问题。如果颜色偏色、画面颠倒后面所有测试数据都会失真。我会分别跑lv_demo_widgets和lv_demo_benchmark。前者看整体视觉后者直接提供性能数据。如果官方Demo都没跑通不要责怪性能差大概率是驱动或者配置有问题。3. 性能基准测试的方法与指标3.1 性能指标拆解帧率、渲染时间、CPU占比、内存占用LVGL性能不能只盯一个帧率。在实际项目里我建议关注四类指标帧率FPS在连续刷新场景下屏幕每秒更新多少次。帧率受渲染速度和LCD刷新速度共同影响。渲染时间Rendering TimeLVGL生成一帧UI数据所花费的时间。它主要反映CPU和图形引擎的开销。渲染时间越短帧率上限越高。CPU占用率主循环跑LVGL的时间占比。如果在跑UI时还要执行通信、采集、控制算法这个指标直接决定系统能不能并行工作。内存占用LVGL运行过程中的RAM消耗。内存不足会导致UI初始化失败或者运行一段时间后卡死。某些开发板的LCD接口带宽也可能成为瓶颈比如RGB565数据从MCU传到LCD控制器的时间。这个一般包含在“每帧刷新时间”里可以在flush回调里用计时器测量。3.2 基于官方Benchmark Demo的测试流程LVGL官方提供了一个lv_demo_benchmark它的做法是按预定义场景渲染一组控件并计算每帧的渲染耗时和实时帧率。这个Demo很适合做第一轮基线评估。跑官方Benchmark时我一般会这样操作在lv_conf.h中打开LV_USE_DEMO_BENCHMARK。在主函数初始化后调用lv_demo_benchmark()。观察屏幕上的渲染时间、FPS和内存数据。记录不同场景下的数值比如矩形、圆角、文字、图像混合、阴影等。官方Demo的好处是场景标准化横向对比可信。缺点是它不一定代表你真实项目的界面复杂度。所以只能作为“基础分”。3.3 自定义场景模拟实际UI交互官方Demo跑完还要建一套自己的页面来测。我会用LVGL的样式系统和控件复制实际项目的布局包括带圆角的卡片、动态滚动列表、一个带有缓动函数的动画、一个用于数据刷新的图表。自定义测试的关键是“单变量控制”。比如测圆角渲染时先不开阴影再把阴影打开对比渲染时间的增量。测文字渲染时使用不同字号的字体看中文和数字混排的耗时。测图片显示时用格式相同的PNG或RGB565图片缩放和不缩放分别测一次。这样得出的数据才能指导优化到底是控件的数量拖慢了性能还是某个样式属性特别昂贵。3.4 数据记录与对比方法性能测试最怕“印象流”。我建议用表格记录每次修改后重新测一遍。场景渲染时间msFPSCPU占用备注官方Benchmark-矩形基线官方Benchmark-圆角开启圆角自定义页面-静态无动画自定义页面-滚动列表滚动自定义页面-动画动画执行需要注意LV_USE_PERF_MONITOR显示的数据是“目标渲染帧率”和“实际渲染帧率”它们并不总是一致。我更喜欢直接在lv_timer_handler()前后打点计时配合逻辑分析仪或串口输出得到更准确的单次渲染耗时。注意不要一边看屏幕上的帧率显示器一边凭肉眼判断卡顿。帧率是平均值一次卡顿在几十毫秒内很难被眼睛捕捉但计时器能发现。4. 影响LVGL 9.0性能的底层因素4.1 帧缓冲策略对渲染路径的影响LVGL 9.0支持三种渲染模式全缓冲、部分缓冲和直接模式。在lv_display_set_buffers里参数对应的是缓冲区大小和渲染模式。全缓冲FULL缓冲区大小等于整个屏幕LVGL渲染完一帧后统一刷新画面不会撕裂但内存开销巨大。STM32F746G-DISCO如果使用RGB565800x480的屏幕一帧需要768KBSDRAM充足时可以接受。部分缓冲PARTIAL只提供一块小的缓冲区LVGL按区块渲染内存占用很小但刷新次数增多可能导致屏闪或渲染路径变长。直接模式DIRECT让LVGL直接绘制到LCD的显存区适合支持RGB888的屏幕可以省去一次拷贝但对驱动要求高处理不当容易产生花屏。实际测试中我会先开启全缓冲或较大缓冲区测出理论上限再改小缓冲区看性能掉多少。这样才能判断你是否真的需要用“优化渲染区域”来换内存。4.2 内存配置与动态分配瓶颈LVGL 9.0的控件、样式和动画对象都需要内存。如果LV_MEM_SIZE过小动态分配失败后控件会显示不完整甚至直接卡在创建阶段。性能测试时如果内存频繁耗尽渲染时间会突然拉高因为LVGL可能需要反复释放和重新分配内存。我建议先让内存富余测出一个“无内存压力”的基线再把内存缩小到实际可用范围观察性能曲线。这个过程能帮你找到“当前UI能稳定运行的最低内存”这个值对产品选型非常有意义。4.3 控件复杂度与重绘区域LVGL 9.0性能瓶颈很大程度来自“重绘面积”。比如一个全屏背景的颜色变化会导致整块区域重绘而一个按钮的点击只重绘按钮所在区域。如果界面里大量控件都有不透明背景每次交互重绘区域可能会叠加导致性能下降。减少重绘区域的方法很简单尽量让控件透明避免大范围lv_obj_set_style_bg_opa设置为完全不透明。另一个方法是减少不必要的动画和刷新例如在滑块滚动时只更新数值文本而不是让整个页面重新布局。4.4 DMA2D等硬件加速带来的收益和限制STM32F746的DMA2D可以加速填充、拷贝和颜色转换但它不是万能药。LVGL 9.0的GPU接口需要实现draw callbackDMA2D才能接管特定操作。比如lv_draw_rect和lv_draw_img中有些操作可以交给DMA2D但圆角裁剪、复杂渐变、文字绘制通常还是需要CPU完成。启用DMA2D后可能会发现小面积绘制的性能反而下降因为DMA2D的配置和中断开销太高。这在基准测试里很常见大图形加速明显小图形或者细碎控件多时DMA2D可能不划算。所以硬件加速也要分层验证分别测小面积、大面积、纯色填充、图片拷贝再决定哪些路径真正适合用DMA2D。5. 性能测试结果的解读与优化决策5.1 从数据中判断瓶颈在CPU、内存还是总线拿到测试数据后第一步不是调参而是判断瓶颈归属如果渲染时间随控件数量线性增长但CPU主频提高后没有明显改善可能是样式系统或布局计算算法的问题。如果内存充足时性能好内存调小后性能骤降说明是动态分配和内存碎片问题。如果LVGL渲染耗时很短但FPS依然上不去瓶颈可能在LCD驱动刷新上比如RGB接口时钟配置太低或者flush回调里用了阻塞式传输。如果启用DMA2D后性能反而下降说明等待DMA2D完成的时间超过了节省的CPU时间。我常用的做法是在flush_cb和lv_timer_handler前后各打一个GPIO翻转用示波器看“LVGL渲染”和“LCD刷新”两个段时间占用。谁占的时间长谁就是优化重点。5.2 针对瓶颈做分层优化瓶颈不同优化策略也不同。如果是渲染路径太慢先降低视觉复杂度去掉阴影、减少圆角、关闭抗锯齿或者改用更简单的字体。这些不一定影响产品质感但能释放大量CPU。如果是内存不足优先减少LV_MEM_SIZE的浪费再检查有没有控件没有删除。可以使用lv_mem_monitor查看碎片和已用内存。如果是LCD驱动刷屏时间长可以尝试双缓冲和DMA传输让MCU在LCD刷屏的同时准备下一帧。前提是硬件支持并确保缓存一致性。如果CPU占用过高可以把LVGL放到RTOS的一个低优先级任务里或者使用lv_timer_handler的周期控制降低刷新频率来节省CPU。注意性能优化要有收益排序。先改成本低、收益高的地方比如调整缓冲策略或关闭阴影不要一上来就换主控。5.3 实际项目中的性能取舍基准测试最终要服务于产品。实际项目中我通常会把“最低保证”和“最优体验”分开设定。最低保证界面在慢速操作、静态显示、贴图切换时不出现明显闪烁帧率不低于某个阈值。最优体验动画、滑动、图表刷新时帧率尽量稳定而不是一开始快后来卡。如果无法两全宁可降低动画复杂度也要保证最低帧率稳定。因为GUI最忌讳的是“时快时慢”。用户对稳定卡顿的容忍度远高于偶发掉帧这会影响设备操作的可靠性。另外性能测试应该贯穿开发周期而不是只在移植完成时做一次。每增加一个界面、一个动效都重新跑一遍关键场景Benchmark防止性能悄悄退化。6. 常见坑点与排查路径6.1 版本差异导致的编译问题LVGL 9.0 API调整较多网上很多老教程会误导。最典型的是lv_disp_drv_t、lv_disp_buf_t这些类型在9.0里已经不存在取而代之的是lv_display_t。如果你把8.x的代码粘贴过来编译会报一连串错误。遇到编译问题先看官方文档针对9.0的移植说明再看工程里是否包含了lvgl.h。如果确认配置无误可以打开LV_USE_STDLIB_MALLOC等宏让LVGL使用C标准库的内存函数减少内存相关的兼容问题。6.2 触摸输入对性能测试的干扰触摸事件会触发LVGL重绘如果你在Benchmark测试时手碰到了屏幕数据必然波动。所以在自动测试时要么不注册触摸回调要么注册一个模拟函数返回固定点。也可以把触摸驱动放在测试完成后再接入确保数据反映的是纯UI渲染能力。6.3 刷新撕裂与缓冲配置撕裂现象多出现在单缓冲和部分缓冲模式下。LVGL渲染一帧的同时LCD控制器正在读显存两者竞争就会撕裂。解决方法是使用双缓冲一个缓冲用于渲染另一个缓冲用于输出然后交替切换。STM32F746G-DISCO自带SDRAM双缓冲内存不是问题。但在移植驱动时要确保两个缓冲区的地址是16字节对齐的否则某些DMA传输会出错。6.4 性能数据不稳定时的排查顺序如果跑出来的性能数据忽高忽低优先从下面几个方向排查检查lv_timer_handler的调用周期是否固定有没有被其他中断打断。检查LVGL是否频繁调用lv_timer导致渲染任务被拆散。检查LCD驱动里是否使用了轮询等待刷屏期间CPU被长时间占用。检查DMA2D使用完毕后有没有正确等待传输完成避免数据半更新。检查内存是否快要耗尽可以用lv_mem_monitor查看碎片率。最后再用示波器看GPIO翻转判断是否真的出现在渲染路径上。很多看似“LVGL卡顿”的问题最后都出在驱动层。所以在怀疑LVGL之前先把驱动刷屏时间单独测一遍。7. 最后想说的经验STM32F746G-DISCO搭配LVGL 9.0是一个很适合做GUI性能验证的组合。它的硬件性能和开发便利性都足够但能不能达到理想的流畅度取决于你对性能瓶颈的定位。我建议你拿到这块板子后严格按照“先跑通、再测试、后优化”的顺序做。先把官方Benchmark跑一遍得到基线数据再根据自己的UI复杂度和内存限制通过控制变量测试找出成本最高的控件和渲染路径最后针对瓶颈做分层优化。不要拿到板子直接堆功能等界面变得复杂了再回头解决性能问题那时候你已经很难定位是哪次改动拖慢了系统。性能基准测试的本质不是跑一个好看的数字而是给整个项目的UI开发定一个可量化的边界。只要边界清晰优化就有了方向选型也有了依据。希望这篇文章也能帮你少走一点弯路。