【LVGL】学习笔记--(3)界面切换以及显示优化

文章介绍了在LVGL框架下如何实现GUI页面的切换,包括通过隐藏属性和删除容器两种方法,并提到使用lv_scr_load_Anim和lv_scr_load函数。此外,文章还讨论了显示优化的几个关键点,如disp_flush方法改进,帧率限制,DPI配置,编译器优化,堆栈和内存设置,以及显存类型的选取,如单BUFFER和双BUFFER策略,以提升嵌入式系统的性能和用户体验。
摘要由CSDN通过智能技术生成

一 界面切换

利用lvgl框架绘制GUI免不了需要实现多个页面的切换,毕竟把所有功能和接口都放在一页上有些不太优雅,而且对于嵌入式硬件的小屏幕也有些过于困难。因此这里就需要实现多个页面(或者说lvgl里的screen)及其互相切换。

实现方案(参考文章《LVGL V8应用——通过按键切换页面》),有以下两类实现方案:

【1】通过LV_OBJ_FLAG_HIDDEN隐藏属性实现页面切换:

显示窗口1之前给窗口2的容器设置LV_OBJ_FLAG_HIDDEN隐藏属性,显示窗口2之前给窗口1的容器设置LV_OBJ_FLAG_HIDDEN隐藏属性。

【2】切换到窗口2时,删除窗口1的容器:

这边的具体实现也可以分为两种方法:

  1. 切换到窗口2时,可以在回调函数中删除窗口1的容器,这样可以删除窗口1下的所有子对象,这种方式适用于运存较小的设备。

static void win_btn_event_callback1(lv_event_t* event)
{
    lv_event_code_t code = lv_event_get_code(event);
    if (code == LV_EVENT_CLICKED)
    {
        // 删除窗口1的win1_contanier容器,这样就可以把win1_contanier上的子对象全部删除,然后显示窗口2
        lv_obj_del(win1_contanier);
        show_button_win2();
    }
}
 
 
static void win_btn_event_callback2(lv_event_t* event)
{
    lv_event_code_t code = lv_event_get_code(event);
    if (code == LV_EVENT_CLICKED)
    {
        // 删除窗口2的win2_contanier容器,这样就可以把win2_contanier上的子对象全部删除,然后显示窗口1
        lv_obj_del(win2_contanier);
        show_button_win1();
    }
}

  1. 使用函数lv_scr_load_anim和lv_scr_load加载和切换屏幕

/* 直接加载该屏幕scr
参数1:要加载的屏幕 */
static inline void lv_scr_load(lv_obj_t * scr)
{
    lv_disp_load_scr(scr);
}

/* 通过动画的方式显示
参数1:要加载的屏幕
参数2:动画类别
参数3:切换动画需要的时间
参数4:为true时则会在切换界面后将旧界面删除,节约内存*/
void lv_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del);

我这边用到的是第二类方式,即GUI Guider中自动生成的事件+加载页面动作的方式:

二 显示优化

因为用到界面的切换,涉及整屏(320*240)的刷新,如果绘制较为复杂的画面,界面切换的过程中会有拉窗帘的效果,非常影响美观。所以显示的优化就不得不考虑了。

参考《LVGL显示优化—基本优化》、《LVGL 优化帧率技巧》,有以下几种优化方法以供参考:

【1】disp_flush刷屏方法改进

这一点之前移植的时候也提过(《【LVGL】学习笔记--(1)Keil中嵌入式系统移植LVGL》),在disp_flush中用封装好的LCD_Fill_LVGL刷屏函数替代原先默认的画点函数。

【2】LVGL帧率限制

LVGL是有一个帧率刷新周期的宏定义,在lv_conf.h里。LVGL会通过LVGL内部的tick,定时去刷屏幕,也就是说该宏定义限定了LVGL刷屏帧率的上限,默认满帧33帧。

这里我们直接改成10ms刷新一次,满帧100帧。顺手再把输入设备读取周期也改成10ms。

/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 10      /*[ms]*/  //默认30

/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 10     /*[ms]*/  //默认30

【3】LV_DPI_DEF配置

在lv_conf.h里配置DPI:

/*
  * LV_DPI_DEF 注意这里,虽然LVGL的作者说这个没这么重要,但他会严重影响到LVGL的动画效果
  * 你应该进行DPI的手动计算,例如320*240分辨率3.2英寸的屏幕,那么 DPI = (320*320+240*240)^0.5 / 3.2 = 125
  */
#define LV_DPI_DEF                  125     /*[px/inch]*/

【4】编译器版本以及代码优化等级

编译器版本尽量用arm compiler5。代码优化等级可以提高到Level 2:

【5】堆栈设置

  1. 栈设置

官方推荐:

在启动文件startup_hc32f460.s中设置大一点,我这边设置为8K:

Stack_Size      EQU     0x00002000

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit


                PRESERVE8
                THUMB

  1. 堆设置

lv_conf.h中的LV_MEM_SIZE进行设置,上面推荐大于32K,我这边设为了48K:

/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
    #define LV_MEM_SIZE (48U * 1024U)          /*[bytes]*/

【6】显存类型

LVGL其实提供了三种显存类型。三种分别对应不同环境,都可以试试 (B站有大神进行过三种类型的测试LVGL 最优配置测试 (STM32),可以参考一下,我自己测了单BUFFER和非全尺寸双BUFFER,区别不大)。先看下结论:

  1. 单BUFFER

画面会被拆分成buffer的size大小,分块刷新,当只有局部刷新时,比如说点击了一个按钮按钮变高亮,那么他就只会刷局部画面。官方推荐 1/10 screen size,拆分太小,刷屏接口又慢的话,不仅帧率低,还会有感人的拉窗帘。整屏幕刷速度快是快,但是太占内存了。

我这边调整为1/4全屏,效果还不错:

/* 单缓冲区示例) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * MY_DISP_VER_RES / 4]; 
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * MY_DISP_VER_RES / 4);   /*Initialize the display buffer*/

  1. 非全尺寸双BUFFER

顾名思义,两个buffer,实际上就是PING-PONG模式,这种情况适合有dma或者其他显存加速设备的芯片,在一块buffer使用dma等外设后台刷屏时,lvgl可以再前台渲染显示进另一块显存,也就是说渲染和刷屏并发执行,理论是比单显存要好的,不过跟硬件和屏幕大小有关,具体要实测。

  1. 全尺寸双BUFFER

两个全尺寸buffer,虽然直觉看上去和非全尺寸双buffer应该区别不大,但实际上,这个是专门为带TFT驱动的MCU准备的。双全尺寸与前两种不同,在任何时候,给出的buffer都是全屏大小的buffer,而不是局部刷新只提供刷新部分的buffer窗口,这么做的的好处是刷屏,只需要更改TFT驱动的显存地址,而不需要将LVGL的buffer再搬运到TFT驱动的显存,就可完成刷屏动作。

以上。

LVGL提供了丰富的过渡效果动画,可以通过lv_anim_t结构体来实现。 首先,我们需要创建两个界面,例如一个主界面和一个设置界面。然后,我们定义一个lv_anim_t类型的变量,并使用lv_anim_init()函数将其初始化。在初始化函数中,我们需要设置动画的目标对象、属性、时间和回调函数。 例如,我们可以定义一个从主界面淡出的动画: ```c static lv_obj_t * main_screen; static lv_obj_t * setting_screen; //创建两个界面对象 void create_main_screen() { main_screen = lv_obj_create(NULL, NULL); //... } void create_setting_screen() { setting_screen = lv_obj_create(NULL, NULL); //... } //定义动画回调函数 void on_fade_out_finished(lv_anim_t * anim) { lv_obj_del(main_screen); } //添加动画 void switch_to_setting_screen() { lv_anim_t anim; lv_anim_init(&anim); lv_anim_set_var(&anim, main_screen); lv_anim_set_time(&anim, 500); lv_anim_set_values(&anim, LV_OPA_COVER, LV_OPA_TRANSP); lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t) lv_obj_set_opa_scale); lv_anim_set_ready_cb(&anim, on_fade_out_finished); lv_anim_start(&anim); } ``` 这里我们定义了一个从LV_OPA_COVER(完全不透明)到LV_OPA_TRANSP(完全透明)的动画,回调函数on_fade_out_finished将在动画完成时被调用。在回调函数中,我们可以删除旧界面切换到新界面。 类似地,我们可以定义其他动画效果,如平移、旋转和缩放。具体请参考LVGL文档中关于动画的章节。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值