一、LVGL介绍:
lvgl是一款轻量级的嵌入式开源界面代码,广泛用于图形化的界面操作,控件丰富。
二、资源介绍:
1.lvgl资源连接,从GitHub上可以直接搜索lvgl,可以搜索到各个版本的资源连接,连接如下:https://github.com/lvgl/lvgl
我使用的不是最新的版本,7.11.zip是我挑选的历史版本
lvgl源码工程如图所示:源码压缩包
在分支里选择你所需要的工程包
找到文件夹里的HTML,会有lvgl的所有操作
包括移植、控件的使用等等一系列操作。关于移植后续补充,其实没啥难度,很多地方都有介绍
2、lvgl字体转换
链接如下:https://lvgl.io/tools/fontconverter
你本地的字库里仿宋,微软雅黑等 ttf后缀的文件
至此就完成了汉字的生成,后续使用就可以直接用了
3、lvgl图片转换
链接如下:https://lvgl.io/tools/imageconverter
至此图片也可以生成了,用的时候declare一下图片即可
三、界面管理机制介绍:
基于lvgl的实现,为了方便页面的管理和实现,使用了面向对象的方式进行界面的管理和实现,方便后续作为模板化生成界面,新增界面和维护界面非常方便
1、相关技术介绍
1.1、堆栈
我们界面总共有100个左右的界面,所以界面句柄的管理使用了堆栈进行页面句柄的入栈出栈操作,源码如下
/*定义数据结构*/
#define pt_stack(T,size)\
struct{\
T buf[size];\
int top;\
}
/*初始化堆栈*/
#define pt_stack_init()\
{.top = -1}
#define pt_stack_len(s) (sizeof((s)->buf)/sizeof((s)->buf[0]))//<计算堆栈长度
#define pt_stack_cap(s) ((s)->top + 1)//<计算堆栈容量
#define pt_stack_empty(s) ((s)->top == -1)//<判断堆栈是否为空
#define pt_stack_reset(s) ((s)->top == -1)//<复位
#define pt_stack_full(s) ((s)->top == pt_stack_len(s) - 1)//<判断队列是否满
#define pt_stack_push(s,el)\
(!pt_stack_empty(s) && ((s)->buf[++(s)->top] = (el),1))//<入栈
#define pt_stack_pop(s)\
(pt_stack_empty(s) ? NULL: (s)->buf[(s)->top--])//<出栈
#define pt_stack_peek(s)\
(pt_stack_empty(s) ? NULL : (s)->buf[(s)->top]))//<取出栈顶元素
页面数据结构定义page.h
struct _PAGE;
struct _lv_obj_t;
struct _lv_group_t;
/**
*@brief:页面类型
*/
typedef enum{
GUI_NONE_PAGE_TYPE = 0,//<页面无类型
/*常驻界面-不需要删除的*/
LOCK_SCREEN_PAGE_TYPE,//<锁屏页
MAIN_PAGE_TYPE,//<主运行页
MENU_PAGE_TYPE,//<菜单页
/*动态页面,退出后注销的,不常用的页面资源*/
}PAGE_TYPE
/**
*@brief:页面存储类型
*/
typedef enum{
PERMANENT_PAGE_TYPE, //<常驻页面,静态创建,不删除
DYNAMIC_PAGE_TYPE //<动态页面,动态创建以及删除,有页面栈进行维护
}
/**
*@brief:页面公共回调函数
*/
typedef struct{
int (*tp_gui_create)(struct _PAGE *self); //<创建页面
int (*tp_gui_show)(struct _PAGE *self); //<显示页面
int (*tp_gui_hidden)(struct _PAGE *self); //<隐藏页面
int (*tp_gui_create_timer_task)(struct _PAGE *self); //<创建页面后初始化
int (*tp_gui_delete_timer_task)(struct _PAGE *self); //<删除页面前清理
int (*tp_gui_cleanup)(struct _PAGE *self); //<清理页面
int (*tp_gui_delete)(struct _PAGE *self); //<删除页面
int (*tp_gui_dispatch_logic_key)(struct _PAGE *self,uint8_t logic_key,uint8_t *lvgl_key);//<按键转换
}PAGE_VTABLE;
/**
*@brief:定义页面数据结构
*/
typedef struct _PAGE{
PAGE_VTABLE v_table; //<页面公共回调函数
PAGE_TYPE type; //<页面类型
PAGE_MEMORY_TYPE mem_type; //<页面存储类型
uint8_t register_flag; //<注册标记
uint8_t init_flag; //<初始化标记
uint8_t minimal_permission; //<页面最低需求权限
uint8_t page_building; //<1:功能开发中
/*lvgl核心控件*/
struct _lv_obj_t *root; //<根容器
struct _lv_group_t *active_group//<活跃焦点组
/*页脚和页眉*/
PAGE_HEADER *active_header;
PAGE_FOOTER *active_footer;
}PAGE;
页面初始化相关函数page.c
/**
*@brief:页面初始化
*/
int page_init(PAGE *self)
{
if(self == NULL)
return -1;
memset(self,0,sizeof(PAGE));
self->v_table.tp_gui_show = __default_gui_show;
self->v_table.tp_gui_hidden = __default_gui_hidden;
self->v_table.tp_gui_delete = __default_gui_delete;
}
int default_gui_show(PAGE *page)
{
__default_gui_show(page);
}
/**
*@brief:默认显示界面
*/
static int __default_gui_show(PAGE *self)
{
if(self == NULL)
return -1;
/*1.统一显示和刷新头部组件*/
if(self->active_header && self->active_header->v_table.tp_show){
self->active_header->v_table.tp_show(self->active_header);
}
if(self->active_header && self->active_header->v_table.tp_flush){
self->active_header->v_table.tp_flush(self->active_header,NULL);
}
/*2.统一显示和刷新底部组件*/
if(self->active_footer && self->active_footer->v_table.tp_show){
self->active_footer->v_table.tp_show(self->active_footer);
}
if(self->active_footer && self->active_footer->v_table.tp_flush){
self->active_footer->v_table.tp_flush(self->active_footer,NULL);
}
/*3.统一创建定时任务*/
if(self->v_table.tp_gui_create_timer_task && self->init_flag){
self->v_table.tp_gui_create_timer_task(self);
}
/*4.创建焦点组*/
if(self->init_flag && self->root){
lv_obj_set_hidden(self->root,false);
lv_scr_load(self->root);
if(self->active_group){
lvgl_task_register_group(self->active_group);
}
}
return 0;
}
/**
*@brief:默认界面隐藏
*/
static int __default_gui_hidden(PAGE *self)
{
int ret = -1;
if(self == NULL)
return -2;
/*1.统一控制header组件*/
if(self->active_header && self->active_header->v_table.tp_hidden){
self->active_header->v_table.tp_hidden(self->active_header);
}
/*2.统一控制footer组件*/
if(self->active_footer && self->active_footer->v_table.tp_hidden){
self->active_footer->v_table.tp_hidden(self->active_footer);
}
/*3.统一清除定时任务*/
if(self->v_table.tp_gui_delete_timer_task){
self->v_table.tp_gui_delete_timer_task(self);
}
/*4.清除焦点组*/
lvgl_task_clear_group();
if(self->root){
lv_obj_set_hidden(self->root,true);
ret = 0;
}
}
/**
*@brief:默认界面删除
*/
static int __default_gui_delete(PAGE *self)
{
int ret = -1;
if(self == NULL)
return -2;
/*1.统一控制header组件*/
if(self->active_header && self->active_header->v_table.tp_delete_timer_task){
self->active_header->v_table.tp_delete_timer_task(self->active_header);
}
/*2.统一控制footer组件*/
if(self->active_footer && self->active_footer->v_table.tp_delete_timer_task){
self->active_footer->v_table.tp_delete_timer_task(self->active_footer);
}
/*3.统一清除定时任务*/
if(self->v_table.tp_gui_delete_timer_task){
self->v_table.tp_gui_delete_timer_task(self);
}
/*4.清除焦点组*/
lvgl_task_clear_group();
/*5.删除界面*/
if(self->root){
lv_obj_del_async(self->root);
self->root = NULL;
}
return 0;
}
页面栈结构定义page_mgr.c
typedef pt_stack(PAGE*,PAGE_MGR_STACK_SZIE) LV_STACK; //<定义页面栈结构
static LV_STACK page_stack = pt_stack_init(); //<页面栈-实体
static uint8_t __page_mgr_init_flag = 0; //<页面管理器-初始化标志
static int __page_size = 0; //<页面数量,总共页面的数量
static PAGE *__pages[PAGE_MGR_STACK_SIZE]; //<页面缓冲区,二级指针用于存放页面栈数据
static int __page_register(void); //<页面注册管理
/**
*@brief:页面注册管理初始化
*/
int page_mgr_init(void)
{
if(__page_mgr_init_flag == 0){
__page_size = 0; //< 页面管理数量0
memset(__pages,0,PAGE_MGR_STACK_SIZE * sizeof(PAGE)); //< 初始化为所有页面栈为0
__page_mgr_init_flag = 1;
__page_register();//<页面注册管理
}
return 0;
}
/**
*@brief:通过页面类型查找页面
*@param:PAGE_TYPE 页面类型
*@ret:PAGE:当前活跃页
*/
PAGE *page_mgr_lookup_by_type(PAGE_TYPE type)
{
PAGE *page = NULL;
for(int i = 0; i < PAGE_MGR_STACK_SIZE; i++){
page = __pages[i];
if((page->register_flag) == 1 && (page->type) == type){
return page;
}
}
return NULL;
}
/**
*@brief:查找栈顶页面
*@ret:PAGE 栈顶页面
*/
PAGE *page_mgr_lookup_active_page(void)
{
return page_mgr_lookup_check_active_page(GUI_NONE_PAGE_TYPE);
}
/**
*@brief:根据类型查找栈顶页面,主动调用的,在当前页面取出页面的句柄
*@param:PAGE_TYPE 页面类型
*@ret:返回栈顶页面
*/
PAGE *page_mgr_lookup_check_active_page(PAGE_TYPE type)
{
PAGE **page = pt_stack_peek(&page_stack);
if(__page_mgr_init_flag == 0)
return NULL;
if(*page == NULL)
return NULL;
if(type != GUI_NONE_PAGE_TYPE){
if((*page)->type != type){
return NULL;
}
}
return *page;
}
/**
*@brief:打开一个新页面
*@ret:返回码
*/
int page_mgr_open_new(PAGE_TYPE type)
{
int ret = -1;
PAGE *page = page_mgr_lookup_by_type(type);
if(page == NULL){
return -1;
}
PAGE **cur_page = NULL;
cur_page = pt_stack_peek(&page_stack); //<取当前栈顶页面
if(cur_page){
if((*cur_page)->type == type){ //<表示栈顶页面就是当前页面,可以直接不打开,页面去重
return -2;
}
}
if(page->init_flag == 0){ //<如果页面不存在,创建一个新的页面
if(page->v_table.tp_gui_create){
ret = page->v_table.tp_gui_create(page); //<创建一个新的页面
}
}else{ //<不需要动态创建
ret = 0;
}
if(ret == 0){ //表示可以显示
page->init_flag = 1;
if(cur_page && (*cur_page)->v_table.tp_gui_hidden){ //<当前栈顶旧页面隐藏
ret = (*cur_page)->v_table.tp_gui_hidden(*cur_page);
}
pt_stack_push(&page_stack,page); //<新页面入栈
if(page->v_table.tp_gui_show){ //<新界面显示
ret = page->v_table.tp_gui_show(page);
}
}
return ret;
}
/**
*@brief:返回上一个界面
*@ret:状态返回值
*/
int page_mgr_back_open(void)
{
int ret = 0;
PAGE **cur_page = NULL;
PAGE **pre_page = NULL;
if(__page_mgr_init_flag == 0){//<如果没有页面初始化,直接返回
return -1;
}
taskENTER_CRITICAL();
/*取出来一个栈页面*/
cur_page = pt_stack_pop(&page_stack);
if(cur_page == NULL){
ret = -1;
goto clean;
}
/*取出当前栈顶元素*/
pre_page = pt_stack_peek(&page_stack);
if(pre_page == NULL){
ret = -2;
goto clean;
}
/*判断当前取出来的页面是常驻的还是动态的,静态的就隐藏,动态的就删除*/
if((*cur_page)->mem_type == PERMANENT_PAGE_TYPE){//<如果是常驻界面隐藏掉
if((*cur_page)->v_table.tp_gui_hidden){//<如果有隐藏回调,直接隐藏
(*cur_page)->v_table.tp_gui_hidden(*cur_page);
}
}else{//<动态页面直接删除掉
if((*cur_page)->v_table.tp_gui_cleanup){
(*cur_page)->v_table.tp_gui_cleanup(*cur_page);
}
if((*cur_page)->v_table.tp_gui_delete){
(*cur_page)->v_table.tp_gui_delete(*cur_page);
}
(*cur_page)->init_flag = 0;//<删除了页面
}
if((*pre_page)->v_table.tp_gui_show){
(*pre_page)->v_table.tp_gui_show(*pre_page);
}
clean:
taskEXIT_CRITICAL();
return ret;
}
/**
*@brief:跳转到指定类型的页面,从页面管理栈中查找
*@param:type页面类型
*@ret: 0成功 其他:失败
*/
int page_mgr_back_to_target_page(PAGE_TYPE type)
{
int ret = 0;
PAGE *page = NULL;
PAGE **pr_page = NULL;//<寻找页面栈指针
char page_exist_flag = 0;//<页面类型是否在页面栈中
if(__page_mgr_init_flag == 0){
return -1;
}
taskENTER_CRITICAL();
/*1.遍历页面栈中是否存在当前类型的页面*/
for(uint8_t i = 0; i <= page_stack.top; ++i){
if(page_stack.buf[i]->type == type){
page_exist_flag = 1;
break;
}
}
if(page_exist_flag == 0){//<表示页面不存在.创建一个新页面
page_mgr_open_new(type);
ret = 0;
goto clean;
}
/*2.遍历页面栈,清除相关的页面资源直至到指定页面类型栈位置*/
while((pr_page = pt_stack_pop(&page_stack)) != NULL){
page = *pr_page;
if(page->type == type){
ret = 0;
break;
}
/*2.1 隐藏常驻界面元素*/
if(page->mem_type == PERMANENT_PAGE_TYPE){
if(page->v_table.tp_gui_hidden){
page->v_table.tp_gui_hidden(page);
}
}else{
/*2.2 删除动态界面元素*/
if(page->v_table.tp_gui_cleanup){
page->v_table.tp_gui_cleanup(page);
}
if(page->v_table.tp_gui_delete){
page->v_table.tp_gui_delete(page);
}
}
}
/*3.指定类型页面入栈,显示*/
if(ret == 0){
pt_stack_push(&page_stack,page);//<指定页面入栈
if(page->v_table.tp_gui_show){//<显示
page->v_table.tp_gui_show(page);
}
}
clean:
taskEXIT_CRITICAL();
}
/**
*@brief:跳转到主运行页
*/
int page_mgr_back_to_main_page(void)
{
return page_mgr_back_to_target_page(MAIN_PAGE_TYPE);
}
/**
*@brief:跳转到菜单页
*/
int page_mgr_back_to_menu_page(void)
{
return page_mgr_back_to_target_page(MENU_PAGE_TYPE);
}
/**
*@brief:跳转到锁屏页
*/
int page_mgr_back_to_lockscreen_page(void)
{
return page_mgr_back_to_target_page(LOCK_SCREEN_PAGE_TYPE);
}
/**
*@brief:页面管理分发key
*@param:
-#1:logic_key 物理key
-#2:lvgl_key lvgl key
*/
int page_mgr_dispatch_logic_key(uint8_t logic_key,uint8_t *lvgl_key)
{
int ret = -1;
PAGE *page = page_mgr_lookup_active_page();//<取栈顶页面
if(page == NULL){
return -2;
}
if(page->init_flag && page->v_table.tp_gui_dispatch_logic_key){
ret = page->v_table.tp_gui_dispatch_logic_key(page,logic_key,lvgl_key);
}
return ret;
}
#include "gui_lock_screen_page.h"
//<一系列页面头文件
/**
*
*
*/
static int __page_register(void)
{
/*屏保界面*/
static LOCK_SCREEN_PAGE __lock_screen_page;
lock_screen_page_init(&__lock_screen_page);
__pages[__page_size++] = (PAGE *)&__lock_screen_page;
// 主运行界面
static MAIN_PAGE __main_page;
main_page_init(&__main_page);
__pages[__page_size++] = (PAGE*)&__main_page;
// 主运行界面
static MENU_PAGE __menu_page;
menu_page_init(&__menu_page);
__pages[__page_size++] = (PAGE*)&__menu_page;
// 系统查询-组网信息界面
DEVICE_RECORD_PAGE *__device_record_page = MALLOC(sizeof(DEVICE_RECORD_PAGE));
device_record_page_init(__device_record_page);
__pages[__page_size++] = (PAGE*)__device_record_page;
// 系统查询-接口信息界面
INTERFACE_INFO_PAGE *__interface_info_page = MALLOC(sizeof(INTERFACE_INFO_PAGE));
interface_info_page_init(__interface_info_page);
__pages[__page_size++] = (PAGE*)__interface_info_page;
// 系统查询-联动程序-菜单界面
PROGRAM_MENU_PAGE *__program_menu_page = MALLOC(sizeof(PROGRAM_MENU_PAGE));
program_menu_page_init(__program_menu_page);
__pages[__page_size++] = (PAGE*)__program_menu_page;
//<新增页面即可
}
未完待续...