D133 RISC-V KunLun Pi开发板评测 02 LVGL移植与应用测试
- LVGL简介
LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素,美丽的视觉效果和低内存占用。
Key features(主要特性)
- 丰富且强大的模块化图形组件:按钮 (buttons)、图表 (charts)、列表 (lists)、滑动条 (sliders)、图片 (images) 等
- 高级的图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果
- 支持多种输入设备:触摸屏、 键盘、编码器、按键等
- 支持多显示设备
- 不依赖特定的硬件平台,可以在任何显示屏上运行
- 配置可裁剪(最低资源占用:64 kB Flash,16 kB RAM)
- 基于UTF-8的多语种支持,例如中文、日文、韩文、阿拉伯文等
- 可以通过类CSS的方式来设计、布局图形界面(例如:Flexbox、Grid)
- 支持操作系统、外置内存、以及硬件加速(LVGL已内建支持STM32 DMA2D、NXP PXP和VGLite)
- 即便仅有单缓冲区(frame buffer)的情况下,也可保证渲染如丝般顺滑
- 全部由C编写完成,并支持C++调用
- 支持Micropython编程,参见:LVGL API in Micropython
- 支持模拟器仿真,可以无硬件依托进行开发
- 丰富详实的例程
- 详尽的文档以及API参考手册,可线上查阅或可下载为PDF格式
- 在 MIT 许可下免费和开源
Requirements(配置要求)
基本上,每个能够驱动显示器的现代控制器都适合运行 LVGL。 最低要求是:
- 16、32 或 64 位微控制器或处理器
- 建议使用 >16 MHz 时钟速度
- 闪存/ROM: > 64 kB 用于非常重要的组件 (> 建议使用 180 kB)
- RAM:
静态 RAM 使用量:~2 kB,取决于使用的功能和对象类型堆: > 2kB (> 建议使用 8 kB)
动态数据(堆): > 2 KB (> 如果使用多个对象,建议使用 16 kB). 在 lv_conf.h 文件中配置 LV_MEM_SIZE 生效。
显示缓冲区:> “水平分辨率”像素(推荐 >10 × 10ד 水平分辨率”)MCU或外部显示控制器中的一个帧缓冲区
- RAM:
- C99 或更新的编译器
- C99 或更新的编译器
- LVGL在匠芯创中的使用
- LVGL 使用指南
- LVGL 使用指南
匠芯创提供了LVGL 使用指南,可以参考RTOS SDK中的多媒体章节的介绍。
- packages lvgl
- packages lvgl
在匠芯创芯片的lite开发源码中,packages里面提供大量的第三方的库文件,其中lvgl-ui是一个重要的案例,里面包含了大量的测试应用。
aic_demo包含大量的测试案例,aic_ui,lv_conf,lv_demo.c等配置文件
在第一次开箱测试中,其实选择了官方的一个showcase_demo进行展示,简单的配置如下
输入scons --menuconfig进行配置界面,选择Application options,主要是选择demo加配置好相关的源文件的位置
保存退出,scons编译即可,下载代码,效果如下。
- 自定义使用
上面的都是测试官方的lvgl的配置流程,下面是进行简单的自定义的使用。
1. 编写lvgl界面代码
参考B站大佬的代码
- #include "yang.h"
- #include "lvgl.h"
- #include "stdlib.h"
-
- #define card_img_type_count 15
- #define left_count 20
- #define right_count 20
- #define mid_count 100
- #define bottom_count 8
- #define max_layer 4
- #define layer_row_count 8
- #define layer_col_count 6
- #define mid_x_start 230
- #define mid_y_start 20
- #define card_width 40
- #define card_height 50
- #define left_x_start 190
- #define right_x_start 570
- #define left_y_start 150
- #define right_y_start 150
- #define left_y_distance 5
- #define right_y_distance 5
- #define bottom_start_x 256
- #define bottom_start_y 395
-
- LV_IMG_DECLARE(grass2_img)
- LV_IMG_DECLARE(grass1_img)
- LV_IMG_DECLARE(yang_start_btn_img)
- LV_IMG_DECLARE(bottom_dock_img)
- LV_IMG_DECLARE(yang_card1_img)
- LV_IMG_DECLARE(yang_card2_img)
- LV_IMG_DECLARE(yang_card3_img)
- LV_IMG_DECLARE(yang_card4_img)
- LV_IMG_DECLARE(yang_card5_img)
- LV_IMG_DECLARE(yang_card6_img)
- LV_IMG_DECLARE(yang_card9_img)
- LV_IMG_DECLARE(yang_card7_img)
- LV_IMG_DECLARE(yang_card8_img)
- LV_IMG_DECLARE(yang_card9_img)
- LV_IMG_DECLARE(yang_card10_img)
- LV_IMG_DECLARE(yang_card11_img)
- LV_IMG_DECLARE(yang_card12_img)
- LV_IMG_DECLARE(yang_card13_img)
- LV_IMG_DECLARE(yang_card14_img)
- LV_IMG_DECLARE(yang_card15_img)
-
- typedef enum
- {
- left =0,
- mid,
- right,
- bottom,
-
- }area_enum;
-
- typedef struct
- {
- lv_obj_t * obj;
- bool alive;
- char x;
- char y;
- area_enum area;
- char layer;
- char img_index;
-
- }card_typedef;
-
- card_typedef left_card[left_count]={0,};
- card_typedef right_card[right_count]={0,};
- card_typedef mid_card[max_layer*layer_col_count*layer_row_count]={0,};
- card_typedef bottom_card[bottom_count]={0,};
-
- static const lv_img_dsc_t * card_img[card_img_type_count] = { &yang_card1_img ,&yang_card2_img ,&yang_card3_img,&yang_card4_img,&yang_card5_img,
- &yang_card6_img,&yang_card7_img,&yang_card8_img,&yang_card9_img,&yang_card10_img,&yang_card11_img,&yang_card12_img,&yang_card13_img,&yang_card14_img,&yang_card15_img,};
- static const lv_img_dsc_t * grass_img[2] = { &grass1_img,&grass2_img,};
- lv_obj_t * screen,* map1,* start_btn,*dock;
- static void game_start(lv_event_t * e);
- static void cover_test();
- static void clicked_cb(lv_event_t * e);
- static void left_cover_test();
- static void right_cover_test();
- static void move_to_right(char index);
- static char find_same_card(char img_index);
- static void del_same_card();
- static void x_move_cb(void * var, int32_t v);
- static void y_move_cb(void * var, int32_t v);
- static void move_to_left();
- static void move_done_cb(lv_anim_t * a);
- static void game_over();
- static void card_anim_cb(void * var, int32_t v);
- static void card_del_cb(lv_anim_t* a);
- static void right_x_move_cb(void * var, int32_t v);
-
- void yang_game()
- {
-
- lv_memset_00(left_card,sizeof(left_card));
- lv_memset_00(right_card,sizeof(right_card));
- lv_memset_00(mid_card,sizeof(mid_card));
- lv_memset_00(bottom_card,sizeof(bottom_card));
-
- screen=lv_tileview_create(lv_scr_act());
- lv_obj_set_size(screen,800,480);
- lv_obj_set_style_bg_color(screen,lv_color_hex(0xcdfd8b),0);
- lv_obj_clear_flag(screen, LV_OBJ_FLAG_SCROLLABLE);
-
- for(int i=0;i<20;i++)
- {
- lv_obj_t * g=lv_img_create(screen);
- lv_img_set_src(g,grass_img[rand()%2]);
- lv_obj_set_pos(g,rand()%750,rand()%450);
- }
-
- dock=lv_img_create(screen);
- lv_img_set_src(dock, &bottom_dock_img);
- lv_obj_align(dock,LV_ALIGN_BOTTOM_MID,0,-5);
-
- start_btn=lv_img_create(screen);
- lv_img_set_src(start_btn, &yang_start_btn_img);
- lv_obj_center(start_btn);
- lv_obj_add_flag(start_btn, LV_OBJ_FLAG_CLICKABLE);
- lv_obj_add_event_cb(start_btn,game_start,LV_EVENT_RELEASED,0);
-
- }
-
-
- static void game_start(lv_event_t * e)
- {
- int i,j;
-
- lv_obj_del((lv_obj_t *)e->target);
-
- for(i=0;i<left_count;i++)
- {
- left_card[i].obj=lv_img_create(screen);
- left_card[i].alive=true;
- left_card[i].y=i;
- left_card[i].area=left;
- left_card[i].layer=i;
- left_card[i].img_index=rand()%card_img_type_count;
- lv_img_set_src(left_card[i].obj, card_img[left_card[i].img_index]);
- lv_obj_set_pos(left_card[i].obj,left_x_start,left_y_start+i*left_y_distance);
- left_card[i].obj->user_data=&left_card[i].obj;
- lv_obj_set_style_img_recolor(left_card[i].obj,lv_color_hex(0x000000),0);
- lv_obj_set_style_img_recolor_opa(left_card[i].obj,100,0);
- lv_obj_clear_flag(left_card[i].obj, LV_OBJ_FLAG_CLICKABLE);
- lv_obj_add_event_cb(left_card[i].obj,clicked_cb,LV_EVENT_RELEASED,0);
- }
-
- for(i=0;i<right_count;i++)
- {
- right_card[i].obj=lv_img_create(screen);
- right_card[i].alive=true;
- right_card[i].y=i;
- right_card[i].area=right;
- right_card[i].layer=i;
- right_card[i].img_index=rand()%card_img_type_count;
- lv_img_set_src(right_card[i].obj, card_img[right_card[i].img_index]);
- lv_obj_set_pos(right_card[i].obj,right_x_start,right_y_start+i*right_y_distance);
- right_card[i].obj->user_data=&right_card[i].obj;
- lv_obj_set_style_img_recolor(right_card[i].obj,lv_color_hex(0x000000),0);
- lv_obj_set_style_img_recolor_opa(right_card[i].obj,100,0);
- lv_obj_clear_flag(right_card[i].obj, LV_OBJ_FLAG_CLICKABLE);
- lv_obj_add_event_cb(right_card[i].obj,clicked_cb,LV_EVENT_RELEASED,0);
- }
-
-
-
- for(j=0;j<max_layer;j++)
- {
- for(i=0;i<layer_row_count*layer_col_count ;i++)
- {
- mid_card[j*layer_row_count*layer_col_count+i].obj=lv_img_create(screen);
- mid_card[j*layer_row_count*layer_col_count+i].alive=true;
- mid_card[j*layer_row_count*layer_col_count+i].x=i%layer_row_count*2+j%2;
- mid_card[j*layer_row_count*layer_col_count+i].y=i/layer_row_count*2+j%2;
- mid_card[j*layer_row_count*layer_col_count+i].area=mid;
- mid_card[j*layer_row_count*layer_col_count+i].layer=j;
- mid_card[j*layer_row_count*layer_col_count+i].img_index=rand()%10;
- lv_img_set_src(mid_card[j*layer_row_count*layer_col_count+i].obj, card_img[mid_card[j*layer_row_count*layer_col_count+i].img_index]);
- lv_obj_set_pos(mid_card[j*layer_row_count*layer_col_count+i].obj,mid_x_start+mid_card[j*layer_row_count*layer_col_count+i].x*card_width/2,mid_y_start+mid_card[j*layer_row_count*layer_col_count+i].y*card_height/2);
- mid_card[j*layer_row_count*layer_col_count+i].obj->user_data=&mid_card[j*layer_row_count*layer_col_count+i];
- lv_obj_set_style_img_recolor(mid_card[j*layer_row_count*layer_col_count+i].obj,lv_color_hex(0x000000),0);
- lv_obj_set_style_img_recolor_opa(mid_card[j*layer_row_count*layer_col_count+i].obj,0,0);
- lv_obj_add_flag(mid_card[j*layer_row_count*layer_col_count+i].obj, LV_OBJ_FLAG_CLICKABLE);
- lv_obj_add_event_cb(mid_card[j*layer_row_count*layer_col_count+i].obj,clicked_cb,LV_EVENT_RELEASED,0);
- }
- }
- cover_test();
- left_cover_test();
- right_cover_test();
- }
-
-
- static void clicked_cb(lv_event_t * e)
- {
- int i,j,k,m;
- card_typedef * card=(card_typedef *)((lv_obj_t *)(e->target)->user_data);
- card->alive=false;
-
- cover_test();
- left_cover_test();
- right_cover_test();
-
- m=find_same_card(card->img_index);
- move_to_right(m);
-
- bottom_card[m].alive=true;
- bottom_card[m].obj=e->target;
- bottom_card[m].img_index=card->img_index;
- lv_obj_clear_flag(bottom_card[m].obj, LV_OBJ_FLAG_CLICKABLE);
- lv_obj_move_foreground(bottom_card[m].obj);
-
-
- lv_anim_t a1;
- lv_anim_init(&a1);
- lv_anim_set_var(&a1,bottom_card[m].obj);
- lv_anim_set_exec_cb(&a1,x_move_cb);
- lv_anim_set_time(&a1,300);
- lv_anim_set_path_cb(&a1,lv_anim_path_ease_out);
- lv_anim_set_values(&a1,lv_obj_get_x(bottom_card[m].obj),bottom_start_x+m*(card_width+2));
- lv_anim_start(&a1);
-
- lv_anim_t a2;
- lv_anim_init(&a2);
- lv_anim_set_var(&a2,bottom_card[m].obj);
- lv_anim_set_exec_cb(&a2,y_move_cb);
- lv_anim_set_time(&a2,300);
- lv_anim_set_path_cb(&a2,lv_anim_path_ease_out);
- lv_anim_set_deleted_cb(&a2,move_done_cb);
- lv_anim_set_values(&a2,lv_obj_get_y(bottom_card[m].obj),bottom_start_y);
- lv_anim_start(&a2);
-
- del_same_card();
- move_to_left();
- }
-
-
- static void cover_test()
- {
- int i,j;
-
- for(i=0;i<layer_row_count*layer_col_count*(max_layer);i++)
- {
- if(mid_card[i].alive==true)
- {
- for(j=i+1;j<layer_row_count*layer_col_count*max_layer;j++)
- {
- if(mid_card[j].alive==true&&mid_card[j].layer>mid_card[i].layer)
- {
- if((mid_card[j].x- mid_card[i].x)<2&&(mid_card[i].x- mid_card[j].x)<2&&(mid_card[j].y- mid_card[i].y)<2&&(mid_card[i].y- mid_card[j].y)<2)
- {
- lv_obj_set_style_img_recolor_opa(mid_card[i].obj,100,0);
- lv_obj_clear_flag(mid_card[i].obj, LV_OBJ_FLAG_CLICKABLE);break;
- }
- }
- lv_obj_set_style_img_recolor_opa(mid_card[i].obj,0,0);
- lv_obj_add_flag(mid_card[i].obj, LV_OBJ_FLAG_CLICKABLE);
- }
- }
- }
- if(mid_card[layer_row_count*layer_col_count*max_layer-1].alive==true)lv_obj_add_flag(mid_card[layer_row_count*layer_col_count*max_layer-1].obj, LV_OBJ_FLAG_CLICKABLE);
- }
-
-
- static void left_cover_test()
- {
- int i;
-
- for(i=left_count-1;i>=0;i--)
- {
- if(left_card[i].alive==true)
- {
- lv_obj_set_style_img_recolor_opa(left_card[i].obj,0,0);
- lv_obj_add_flag(left_card[i].obj, LV_OBJ_FLAG_CLICKABLE);
- return;
- }
- }
- }
-
- static void right_cover_test()
- {
- int i;
-
- for(i=right_count-1;i>=0;i--)
- {
- if(right_card[i].alive==true)
- {
- lv_obj_set_style_img_recolor_opa(right_card[i].obj,0,0);
- lv_obj_add_flag(right_card[i].obj, LV_OBJ_FLAG_CLICKABLE);
- return;
- }
- }
- }
-
-
- static void move_to_right(char index)
- {
- int i;
-
- for(i=6;i>index;i--)
- {
- bottom_card[i].alive=bottom_card[i-1].alive;
- bottom_card[i].img_index=bottom_card[i-1].img_index;
- bottom_card[i].obj=bottom_card[i-1].obj;
- bottom_card[i-1].alive=false;
-
- if(bottom_card[i].alive==true)
- {
- lv_anim_t a1;
- lv_anim_init(&a1);
- lv_anim_set_var(&a1,bottom_card[i].obj);
- lv_anim_set_exec_cb(&a1,right_x_move_cb);
- lv_anim_set_time(&a1,200);
- lv_anim_set_values(&a1,bottom_start_x+(i-1)*(card_width+2),bottom_start_x+(i)*(card_width+2));
- lv_anim_start(&a1);
- }
- }
- }
-
-
-
- static void move_to_left()
- {
- int i,j;
-
- for(j=0;j<6;j++)
- {
- if(bottom_card[j].alive==false)
- {
- for(i=j+1;i<7;i++)
- {
- if(bottom_card[i].alive==true)
- {
- bottom_card[j].alive=bottom_card[i].alive;
- bottom_card[j].img_index=bottom_card[i].img_index;
- bottom_card[j].obj=bottom_card[i].obj;
- bottom_card[i].alive=false;
-
- lv_anim_t a1;
- lv_anim_init(&a1);
- lv_anim_set_var(&a1,bottom_card[j].obj);
- lv_anim_set_delay(&a1,500);
- lv_anim_set_exec_cb(&a1,x_move_cb);
- lv_anim_set_time(&a1,200);
- lv_anim_set_values(&a1,bottom_start_x+(i)*(card_width+2),bottom_start_x+(j)*(card_width+2));
- lv_anim_start(&a1);
-
- break;
- }
- }
- }
- }
- }
-
-
-
- static char find_same_card(char img_index)
- {
- int i,j,k;
-
- for(i=5;i>=0;i--)
- {
- if(bottom_card[i].alive&&bottom_card[i].img_index==img_index)
- {
- return i+1;
- }
-
- }
-
- for(i=5;i>=0;i--)
- {
- if(bottom_card[i].alive)
- {
- return i+1;
- }
-
- }
- return 0;
-
- }
-
-
- static void del_same_card()
- {
- int i,j,k;
-
- for(i=0;i<7;i++)
- {
- if(bottom_card[i].alive==true)
- {
- for(j=i+1;j<7;j++)
- {
- if(bottom_card[j].alive==true&&bottom_card[i].img_index==bottom_card[j].img_index)
- {
- for(k=j+1;k<7;k++)
- {
- if(bottom_card[k].alive==true&&bottom_card[j].img_index==bottom_card[k].img_index)
- {
- bottom_card[i].alive=false;
- bottom_card[j].alive=false;
- bottom_card[k].alive=false;
-
- lv_anim_t a1;
- lv_anim_init(&a1);
- lv_anim_set_var(&a1,bottom_card[i].obj);
- lv_anim_set_exec_cb(&a1,card_anim_cb);
- lv_anim_set_time(&a1,300);
- lv_anim_set_delay(&a1,300);
- lv_anim_set_deleted_cb(&a1,card_del_cb);
- lv_anim_set_values(&a1,256,1);
- lv_anim_start(&a1);
-
- lv_anim_init(&a1);
- lv_anim_set_var(&a1,bottom_card[j].obj);
- lv_anim_set_exec_cb(&a1,card_anim_cb);
- lv_anim_set_time(&a1,300);
- lv_anim_set_delay(&a1,300);
- lv_anim_set_deleted_cb(&a1,card_del_cb);
- lv_anim_set_values(&a1,256,1);
- lv_anim_start(&a1);
-
- lv_anim_init(&a1);
- lv_anim_set_var(&a1,bottom_card[k].obj);
- lv_anim_set_exec_cb(&a1,card_anim_cb);
- lv_anim_set_time(&a1,300);
- lv_anim_set_delay(&a1,300);
- lv_anim_set_deleted_cb(&a1,card_del_cb);
- lv_anim_set_values(&a1,256,1);
- lv_anim_start(&a1);
-
- }
- }
-
- }
-
- }
- }
- }
- }
-
- static void card_anim_cb(void * var, int32_t v)
- {
- lv_img_set_zoom(var,v);
- lv_obj_set_style_img_opa(var,v-1,0);
- }
-
- static void card_del_cb(lv_anim_t* a)
- {
- lv_obj_del(a->var);
- }
-
-
- static void x_move_cb(void * var, int32_t v)
- {
- lv_obj_t * xxx=(lv_obj_t *)var;
- lv_obj_set_x(xxx,v);
- }
-
-
- static void right_x_move_cb(void * var, int32_t v)
- {
- lv_obj_t * xxx=(lv_obj_t *)var;
- lv_obj_set_x(xxx,v);
- }
-
- static void y_move_cb(void * var, int32_t v)
- {
- lv_obj_t * xxx=(lv_obj_t *)var;
- lv_obj_set_y(xxx,v);
- }
-
- static void move_done_cb(lv_anim_t * a)
- {
- if(bottom_card[6].alive==true){game_over();}
-
- }
-
- static void game_over()
- {
- lv_anim_del_all();
- lv_obj_del(screen);
- yang_game();
- }
- 编写脚本Scons
- 编写脚本Scons
- from building import *
- import os
-
- cwd = GetCurrentDir()
- group = []
- src = []
- CPPPATH = [cwd]
-
- src += Glob('*.c')
-
- list = os.listdir(cwd)
- for d in list:
- path = os.path.join(cwd, d)
- if os.path.isfile(os.path.join(path, 'SConscript')):
- group = group + SConscript(os.path.join(d, 'SConscript'))
-
- group = group + DefineGroup('LVGL-port', src, depend = ['AIC_LVGL_METER_DEMO'], CPPPATH = CPPPATH)
-
- Return('group')
- 创建文件夹工程
- 创建文件夹工程
- 配置LVGL线程相关文件
- 配置LVGL线程相关文件
LVGL线程相关的代码在lvgl_demo文件中
- #include <lvgl.h>
- #ifdef KERNEL_RTTHREAD
- #include <rtthread.h>
- #endif
- #include "aic_core.h"
- #include "aic_osal.h"
- #include "aic_ui.h"
- #include "aic_dec.h"
- #include <dfs_fs.h>
- #include "aic_time.h"
-
- #ifndef LV_CACHE_IMG_NUM
- #define LV_CACHE_IMG_NUM 1
- #endif
-
- void lv_user_gui_init(void)
- {
- // wait sdcard mounted
- if (!strcmp(LVGL_STORAGE_PATH, "/sdcard")) {
- aicos_msleep(1000);
- }
-
- lv_img_cache_set_size(LV_CACHE_IMG_NUM);
- aic_dec_create();
- aic_ui_init();
- }
-
- #ifdef KERNEL_RTTHREAD
- extern int lvgl_thread_init(void);
-
- INIT_APP_EXPORT(lvgl_thread_init);
- #endif
重要是修改aic_ui.c,在AIC_LVGL_METER_DEMO进行了修改,注释掉了原始的东西,添加我们编写的东西。
- menuconfig配置
- menuconfig配置
编译代码
- 下载代码测试效果