https://github.com/lvgl/lvgl.git
下载指定版本,版本9和版本8变化有点大,我这里以用的比较多的版本8为例。
git clone --branch v8.4.0 https://github.com/lvgl/lvgl.git
删除不必要的文件,只保留src,examples/porting和demos文件夹,以及2个h文件lv_conf_template.h和lvgl.h。
|-- demos
| |-- README.md
| |-- benchmark
| |-- keypad_encoder
| |-- lv_demos.h
| |-- lv_demos.mk
| |-- music
| |-- stress
| `-- widgets
|-- examples
| `-- porting
|-- lv_conf_template.h
|-- lvgl.h
`-- src
|-- core
|-- draw
|-- extra
|-- font
|-- hal
|-- lv_api_map.h
|-- lv_conf_internal.h
|-- lv_conf_kconfig.h
|-- lvgl.h
|-- misc
`-- widgets
Src下是源码,按需添加无需修改
demos下是demo程序
examples/porting下有显示,输入,文件等移植模板
lv_conf_template.h是配置文件模板
lvgl.h是总的头文件
lvgl/src/core/*.c
lvgl/src/draw/*.c
lvgl/src/draw/sw/*.c
lvgl/src/extra/*.c
lvgl/src/extra/layouts/flex/*.c
lvgl/src/extra/layouts/grid/*.c
lvgl/src/extra/libs/bmp/*.c
lvgl/src/extra/libs/ffmpeg/*.c
lvgl/src/extra/libs/freetype/*.c
lvgl/src/extra/libs/gif/*.c
lvgl/src/extra/libs/png/*.c
lvgl/src/extra/libs/qrcode/*.c
lvgl/src/extra/libs/rlottie/*.c
lvgl/src/extra/libs/sjpg/*.c
lvgl/src/extra/libs/tiny_ttf/*.c
lvgl/src/extra/others/fragment/*.c
lvgl/src/extra/others/grianav/*.c
lvgl/src/extra/others/ime/*.c
lvgl/src/extra/others/imgfont/*.c
lvgl/src/extra/others/monkey/*.c
lvgl/src/extra/others/msg/*.c
lvgl/src/extra/others/snapshot/*.c
lvgl/src/extra/themes/basic/*.c
lvgl/src/extra/themes/default/*.c
lvgl/src/extra/themes/mono/*.c
lvgl/src/extra/widgets/animimg/*.c
lvgl/src/extra/widgets/calendar/*.c
lvgl/src/extra/widgets/chart/*.c
lvgl/src/extra/widgets/colorwheel/*.c
lvgl/src/extra/widgets/imgbtn/*.c
lvgl/src/extra/widgets/keyboard/*.c
lvgl/src/extra/widgets/led/*.c
lvgl/src/extra/widgets/list/*.c
lvgl/src/extra/widgets/menu/*.c
lvgl/src/extra/widgets/meter/*.c
lvgl/src/extra/widgets/msgbox/*.c
lvgl/src/extra/widgets/span/*.c
lvgl/src/extra/widgets/spinbox/*.c
lvgl/src/extra/widgets/spinner/*.c
lvgl/src/extra/widgets/tabview/*.c
lvgl/src/extra/widgets/tileview/*.c
lvgl/src/extra/widgets/win/*.c
lvgl/src/font/*.c
lvgl/src/hal/*.c
lvgl/src/misc/*.c
lvgl/src/widgets/*.c
复制lv_conf_template.h为lv_conf.h放置在lvgl文件夹外和lvgl并列。
#if 0 /*Set it to "1" to enable content*/
改为
#if 1 /*Set it to "1" to enable content*/
复制examples/porting下的lv_port_disp_template.c/h到工程目录,修改为
lv_port_disp.c/h
添加源码lv_port_disp.c到工程,有必要的话需要添加头文件lv_port_disp.h包含路径。
将lvgl目录添加到头文件包含路径
完成以上工作后,基本完成了框架,但是还没有适配接口,编译还有问题,详细的适配见后面。
前面已经准备好了lv_port_disp.c和lv_port_disp.h文件。
现在来实现接口,显示适配很简单,只需要实现自己的写点函数或者显示区域函数即可,最开始可以先适配为写点,后面再优化为写区域。
1.使能条件编译
lv_port_disp.c中#if 0改为#if 1
lv_port_disp.h中#if 0改为#if 1
2.头文件改名
#include "lv_port_disp_template.h"
改为
#include "lv_port_disp.h"
3.设置分辨率
#include
添加
#define MY_DISP_HOR_RES 240
#define MY_DISP_VER_RES 320
4.显示初始化
lv_port_disp_init中保留
Example for 1) 后的代码,注释掉
Example for 2) 和 Example for 3)后的代码,即使用一个行缓存。
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 2) */
//static lv_disp_draw_buf_t draw_buf_dsc_2;
//static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
//static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
//lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 3) also set disp_drv.full_refresh = 1 below*/
//static lv_disp_draw_buf_t draw_buf_dsc_3;
//static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
//static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
//lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
// MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/
实现初始化
static void disp_init(void)
{
st7789_itf_init();
/*You code here*/
}
5.显示
disp_flush中
/*put_px(x, y, *color_p)*/
后添加我们的写点函数
st7789_itf_set_pixel(x, y, *((uint16_t*)color_p));当然前面要包含头文件
#include "st7789_itf.h"
6.颜色格式设置
根据实际配置,我这里是RGB565,所以是16位。
lv_conf.h中
#define LV_COLOR_DEPTH 16
这里还可以使用宏LV_COLOR_16_SWAP配置是否交换高低字节(即大小端交换),这适用于适配8位接口传输16位数据时的大小端对应。
LV_COLOR_16_SWAP配置为0不交换,配置为1交换。
7.调用初始化
注意在lv_init后调用lv_port_disp_init
lv_init();
lv_port_disp_init();
lvgl有两种方式获取时间戳,一种是
lv_conf.h中配置宏LV_TICK_CUSTOM为0(默认),
然后周期调用lv_tick_inc,比如1ms调用一次即lv_tick_inc(1).
宏LV_TICK_CUSTOM配置为1则
实现宏LV_TICK_CUSTOM_INCLUDE 和LV_TICK_CUSTOM_SYS_TIME_EXPR
分别是用户提供的实现时间戳获取的头文件和函数。
我这里使用前者,固定周期调用lv_tick_inc。
lv_conf.h中,设置
#define LV_USE_LOG 1 使能日志
设置日志等级,调试时可以设置LV_LOG_LEVEL_TRACE看详细过程,一般设置WARN即可。
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
设置打印接口,
#define LV_LOG_PRINTF 1,则使用printf,需要有stdio.h
#define LV_LOG_PRINTF 0,则需要调用lv_log_register_print_cb初始化打印,
比如
static void my_print(const char * buf)
{
wq_printf("%s\r\n",buf);
}
lv_log_register_print_cb(my_print);
lv_init();
lv_conf.h中
LV_USE_ASSERT开头的宏设置为1,使能对应的assert
下次再分享
下次再分享
参考
examples/get_started/lv_example_get_started_1.c
代码如下:
static void tick_callback(void)
{
lv_tick_inc(1);
}
static void my_print(const char * buf)
{
wq_printf("%s\r\n",buf);
}
static void btn_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;
/*Get the first child of the button which is the label and change its text*/
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
/**
* Create a button with a label and react on click event.
*/
static void lv_example_get_started_1(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/
lv_obj_set_pos(btn, 10, 10); /*Set its position*/
lv_obj_set_size(btn, 120, 50); /*Set its size*/
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/
lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/
lv_label_set_text(label, "Button"); /*Set the labels text*/
lv_obj_center(label);
}
void main(void)
{
while(1)
{
lv_log_register_print_cb(my_print);
lv_init();
lv_port_disp_init();
os_hook_tick_register_callback(tick_callback);
lv_example_get_started_1();
while (1) {
uint32_t delay = lv_timer_handler();
if (delay < 1) delay = 1;
os_delay(delay);
st7789_itf_sync();
}
lv_deinit();
}
return;
}
测试效果如下
添加如下代码到自己的工程
lvgl/demos/benchmark/*.c
lvgl/demos/benchmark/assets/*.c
lvgl/demos/keypad_encoder/*.c
lvgl/demos/music/*.c
lvgl/demos/music/assets/*.c
lvgl/demos/stress/*.c
lvgl/demos/widgets/*.c
lvgl/demos/widgets/assets/*.c
lv_conf.h中将带DEMO的宏配置为1,可以挑选部分。
这里以MUSIC的DEMO为例,如果空间不够可以设置LV_DEMO_MUSIC_LARGE为0。
/*Music player demo*/
demo需要用到一些字体,lv_conf.h设置LV_FONT_MONTSERRAT_12,LV_FONT_MONTSERRAT_22,LV_FONT_MONTSERRAT_32,,LV_FONT_MONTSERRAT_16的宏为1
使能以下宏计算帧率
#define LV_USE_PERF_MONITOR 1
代码如下
static void tick_callback(void)
{
lv_tick_inc(1);
}
static void my_print(const char * buf)
{
wq_printf("%s\r\n",buf);
}
void main(void)
{
while(1)
{
lv_log_register_print_cb(my_print);
lv_init();
lv_port_disp_init();
os_hook_tick_register_callback(tick_callback);
lv_demo_music();
uint32_t pre_time = os_get_ticks();
uint32_t cur_time = pre_time;
while (1) {
uint32_t delay = lv_timer_handler();
cur_time = os_get_ticks();
if(cur_time - pre_time >= 25)
{
pre_time = cur_time;
st7789_itf_sync();
}
if (delay < 1) delay = 1;
os_delay(delay);
//wq_printf("delay %d\r\n",delay);
}
lv_deinit();
}
return;
}
测试结果如下
LVGL的可移植性超级好,只需要实现一个简单的写点或者刷指定范围屏的接口即可,我们也可以其相关可移植性相关的设计。本分享实现了LVGL移植以及DEMO演示,后面可以考虑输入,文件系统相关移植,以及性能的优化。