LVGL (7) 显示驱动与缓冲区配置实战

张开发
2026/4/18 21:35:24 15 分钟阅读

分享文章

LVGL (7) 显示驱动与缓冲区配置实战
1. LVGL显示缓冲区配置实战第一次接触LVGL的显示缓冲区配置时我也被各种buffer方案搞得一头雾水。后来在STM32F407上折腾SPI屏时才发现缓冲区配置直接影响界面流畅度。LVGL提供了三种经典配置模式每种都有特定的适用场景。单缓冲模式就像独木桥所有车辆像素数据都得排队通过。我曾在128x64的OLED上测试过配置一个128x10的缓冲区实际刷新率能达到30fps。关键代码如下static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[128 * 10]; // 单缓冲区 lv_disp_draw_buf_init(draw_buf, buf, NULL, 128 * 10);双缓冲模式则是立交桥一个缓冲区渲染时另一个缓冲区传输数据。在240x320的TFT屏项目里使用两个240x20的缓冲区配合DMA传输刷新率提升到45fps。注意要启用DMA否则性能提升有限static lv_color_t buf1[240 * 20]; static lv_color_t buf2[240 * 20]; lv_disp_draw_buf_init(draw_buf, buf1, buf2, 240 * 20);全屏双缓冲适合有足够RAM的场合。我在ESP32SDRAM的方案中测试过直接分配两个240x320的缓冲区设置full_refresh1刷新率轻松突破60fps。这种模式下LVGL会直接交换缓冲区指针省去数据拷贝static lv_color_t buf1[240 * 320]; static lv_color_t buf2[240 * 320]; lv_disp_draw_buf_init(draw_buf, buf1, buf2, 240 * 320); disp_drv.full_refresh 1; // 关键配置缓冲区大小选择有讲究。实测发现当缓冲区能容纳1/10屏幕数据时再增大缓冲区对性能提升就不明显了。比如320x240的屏幕使用320x24的缓冲区性价比最高。内存紧张的设备可以适当减小但不要小于8行像素否则会出现明显卡顿。2. 显示驱动关键回调实现移植LVGL最关键的莫过于实现flush_cb回调这个函数负责把渲染好的像素数据送到显示屏。我在STM32ST7789的方案中最初用GPIO模拟SPI逐点写入刷新率只有5fps后来改用硬件SPIDMA才解决问题。基础版的flush_cb实现如下适合大多数SPI屏void disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) { LCD_SetWindow(area-x1, area-y1, area-x2, area-y2); SPI_WriteDMA((uint8_t *)color_p, (area-x2 - area-x1 1) * (area-y2 - area-y1 1) * 2); // 注意不能立即调用lv_disp_flush_ready() }DMA传输完成后需要在中断里调用lv_disp_flush_ready()。这个细节坑过我一次忘记调用会导致LVGL停止渲染void SPI_TxComplete_Callback() { lv_disp_flush_ready(disp_drv); }对于支持局部刷新的屏幕可以优化性能。我在ILI9341驱动中就实现了智能刷新当刷新区域小于1/4屏幕时改用非DMA模式传输减少上下文切换开销。rounder_cb回调也很有用。遇到一款奇葩的OLED只能按8像素高度刷新通过这个回调对齐区域坐标后显示才正常void disp_rounder(lv_disp_drv_t *drv, lv_area_t *area) { area-y1 (area-y1 / 8) * 8; area-y2 ((area-y2 7) / 8) * 8 - 1; }3. 硬件加速配置技巧现在的MCU基本都有绘图加速器LVGL通过draw_ctx机制支持硬件加速。我在STM32F769上测试DMA2D加速矩形填充速度提升20倍启用STM32的DMA2D加速需要三步在lv_conf.h中打开LV_USE_GPU_STM32_DMA2D实现颜色格式转换函数如果需要配置LTDC层对于RGB屏关键配置代码disp_drv.draw_ctx_init lv_draw_stm32_dma2d_ctx_init; disp_drv.draw_ctx_deinit lv_draw_stm32_dma2d_ctx_deinit; disp_drv.draw_ctx_size sizeof(lv_draw_stm32_dma2d_ctx_t);遇到过一个坑DMA2D不支持ARGB8888格式。后来在draw_ctx_init里注册了格式转换回调才解决void my_draw_ctx_init(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx) { lv_draw_stm32_dma2d_ctx_init(drv, draw_ctx); draw_ctx-draw_img_decoded my_img_convert; // 自定义转换函数 }对于没有硬件加速的MCU可以启用LVGL的软件优化。在lv_conf.h中设置LV_DRAW_SW_COMPLEX1会启用多边形抗锯齿等高级特性虽然会占用更多CPU资源但视觉效果提升明显。4. 多屏显示与特殊配置最近做的工控项目需要驱动主屏和副屏LVGL的多显示支持派上了大用场。注册第二个显示器时需要特别注意以下几点每个显示器要有独立的draw_buf为每个显示器创建不同的刷新定时器使用lv_disp_set_default()切换当前显示器双屏初始化示例// 主屏初始化 lv_disp_drv_init(disp_drv1); disp_drv1.draw_buf draw_buf1; lv_disp_t *disp1 lv_disp_drv_register(disp_drv1); // 副屏初始化 lv_disp_drv_init(disp_drv2); disp_drv2.draw_buf draw_buf2; lv_disp_t *disp2 lv_disp_drv_register(disp_drv2); // 设置默认显示器 lv_disp_set_default(disp1);旋转显示也是常见需求。LVGL支持0°、90°、180°、270°四种旋转但要注意旋转是在软件层面完成的会消耗额外CPU资源。我在STM32H743上测试旋转90°会使刷新率下降约15%。启用旋转的配置disp_drv.rotated LV_DISP_ROT_90; // 90度旋转 disp_drv.sw_rotate 1; // 使用软件旋转对于内存极度紧张的设备比如只有32KB RAM的STM32F030可以启用direct_mode。这种模式下LVGL会直接操作显存省去中间缓冲区但需要实现set_px_cb回调disp_drv.direct_mode 1; disp_drv.set_px_cb my_set_pixel; // 实现单个像素写入5. 性能优化与调试LVGL的刷新性能可以通过多种手段优化。首先建议启用LV_USE_PERF_MONITOR我在实际项目中用它发现了不少性能瓶颈。显示驱动优化 checklist[ ] 使用DMA传输代替CPU拷贝[ ] 缓冲区大小至少为屏幕1/10[ ] 启用合适的硬件加速[ ] 避免在flush_cb中做复杂计算[ ] 合理设置LV_DISP_DEF_REFR_PERIOD监控回调特别有用这是我常用的监控实现void monitor_cb(lv_disp_drv_t *drv, uint32_t time, uint32_t px) { static uint32_t avg_time; avg_time (avg_time * 9 time) / 10; if(time 50) { // 超过50ms警告 printf(Slow refresh: %dms for %dpx\n, time, px); } }遇到刷新不同步的问题时可以临时修改缓冲区背景色辅助调试。比如在双缓冲配置下把第二个缓冲区初始化为红色很容易观察缓冲区交换时机memset(buf2, 0xF800, sizeof(buf2)); // RGB565红色LVGL的日志系统也很有帮助。在lv_conf.h中设置LV_LOG_LEVELTRACE可以显示详细的渲染过程我靠这个功能解决过不少诡异的显示异常。

更多文章