当前位置: 首页 > 工具软件 > LVGL > 使用案例 >

LVGL系列(一) 一文了解LVGL的学习路线 轻松了解LVGL的全部 快速入门LVGL

严修谨
2023-12-01

" 名字取的这么花里胡哨是为了让大家尽可能的快速找到我,少走一些弯路。"


系列文章目录

一、LVGL系列(一) 一文了解LVGL的学习路线 轻松了解LVGL的全部 

二、LVGL系列(二)之一 LVGL必读介绍  为什么要学习LVGL

       LVGL系列(二)之二 LVGL常见问题解答 整理自官方文档

二、LVGL系列(三)LVGL仿真环境的搭建(WIN下)

        2.1 VS下搭建LVGL仿真环境

        2.1 如何在仿真环境下运行自己的代码

三、LVGL系列(三) LVGL移植教程

四、LVGL系列(四)概述

        4.1 对象 LVGL核心概念

        4.2  位置、尺寸和布局

五、LVGL系列(五)部件

六、LVGL系列(六)布局



前言

·“本文全部内容参考自LVGL官方文档,也可以说是官方文档的中文版。手翻版,水平有限,如有错误,还望指正”

 阅读提醒:本文字多,喜欢看图的可能要失望了


一、LVGL推荐学习步骤

  1. 从在线的demo查看LVGL的运行效果。 (3 minutes)
  2. 阅读文章的介绍部分。 (5 minutes)
  3. 阅读文档的快速概览部分。 (15 minutes)
  4. 部署一个 Simulator(模拟器)。 (10 minutes)
  5. 尝试跑一些 Examples (示例)。
  6. 把LVGL移植到开发板. 请参阅 Porting(移植) 或者 使用一些已经移植好可以使用的工程。
  7. 阅读 Overview (概览)部分,深入了解库的相关知识。 (2-3 hours)
  8. 查阅文档的Widgets (小部件)部分,去了解他们的特点和用法。
  9. 如果你有问题请访问论坛。
  10. 阅读 Contributing (贡献)指南,了解如何帮助LVGL发展 。(15 minutes)

此处待系列文章完成后会添加超链接跳转


二、LVGL快速概览

        阅读完本节您将了解到有关LVGL最重要的事情,通过阅读本节建立的大致印象,再去细读本系列的Porting(移植Overview(概述)部分将会事半功倍。


2.1 首先从仿真环境开始学习LVGL

        强烈建议大家先去在模拟器中开始LVGL的学习,而不是先去移植到开发板中。在没有了解个大概的情况下去移植只会浪费自己的宝贵时间。

        LVGL已经被移植到很多IDE中了,从中选择你喜欢的IDE。在Simulators 可以获取到在适合你PC上运行的即用型工程。这个方式可以让你节省移植的时间,让你立即上手LVGL。


2.2 添加LVGL到你自己工程

如果你想在你自己的工程中尝试LVGL,请按照下面的步骤来:

  • 从GitHub下载或者克隆LVGL的库。  
git  clone  https://github.com/lvgl/lvgl.git.
  • 把 /lvgl 文件夹复制到你的工程中
  • 复制 lvgl/lv_conf_template.h 并修改文件名为 lv_conf.h到与lvgl同级的目录下,修改第一个宏   #if 0 把0改成1来启用文件,然后设置宏LV_COLOR_DEPTH
  • 在你需要使用LVGL相关功能的文件代码中引用lvgl/lvgl.h
  • 在定时器或者任务中每x毫秒调用一次lv_tick_inc(x) (x 应该设置在1-10之间).  它是LVGL内部定时所必需的。或者, 配置LV_TICK_CUSTOM (参见 lv_conf.h)以便于LVGL能直接检索当前时间。
  • 调用 lv_init()
  • 创建一个 darw  buffer: LVGL 首先在此渲染图形,然后将渲染过后的图形发送给显示器。buffer 大小可以自由设置但是请大于1/10屏幕的大小。
static lv_disp_draw_buf_t draw_buf; 
static lv_color_t buf1[DISP_HOR_RES * DISP_VER_RES / 10];
/*Declare a buffer for 1/10 screen size*/
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, MY_DISP_HOR_RES * MY_DISP_VER_SER / 10);
/*Initialize the display buffer.*/

  • 实现并注册一个可以将渲染的图像在你显示器上一定区域显示的函数。
3	static lv_disp_drv_t disp_drv;       /*Descriptor of a display driver*/
4	lv_disp_drv_init(&disp_drv);         /*Basic initialization*/
5	disp_drv.flush_cb = my_disp_flush;   /*Set your driver function*/
6	disp_drv.buffer = &draw_buf;         /*Assign the buffer to the display*/
7	disp_drv.hor_res = MY_DISP_HOR_RES;  /*Set the horizontal resolution of the display*/ 
8	disp_drv.hor_res = MY_DISP_VER_RES;  /*Set the verizontal resolution of the display*/
9	lv_disp_drv_register(&disp_drv);     /*Finally register the driver*/
10	void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p) {
11	    int32_t x, y;
12	    /*It's a very slow but simple implementation. 
13	    *`set_pixel` needs to be written by you to a set pixel on the screen*/
14	    for(y = area->y1; y <= area->y2; y++) { 
15	        for(x = area->x1; x <= area->x2; x++) { 
16	            set_pixel(x, y, *color_p); 
17	            color_p++;
18	        }
19	    } 
20	    lv_disp_flush_ready(disp);  /* Indicate you are ready with the flushing*/
21	}
  • 实现并注册一个可以读取输入设备的函数。例如:对于一个触摸板。
23	static lv_indev_drv_t indev_drv;        /*Descriptor of a input device driver*/ 
24	lv_indev_drv_init(&indev_drv);          /*Basic initialization*/
25	indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/ 
26	indev_drv.read_cb = my_touchpad_read;   /*Set your driver function*/
27	lv_indev_drv_register(&indev_drv);     /*Finally register the driver*/
28	
29	bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
30	{
31	    /*`touchpad_is_pressed` and `touchpad_get_xy` needs to be implemented by you*/ 
32	    if(touchpad_is_pressed()) {
33	         data->state = LV_INDEV_STATE_PRESSED; 
34	         touchpad_get_xy(&data->point.x, &data->point.y);
35	    } else { 
36	        data->state = LV_INDEV_STATE_RELEASED;
37	    }
38	}
39	bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
40	{
41	    /*`touchpad_is_pressed` and `touchpad_get_xy` needs to be implemented by you*/ 
42	    if(touchpad_is_pressed()) {
43	         data->state = LV_INDEV_STATE_PRESSED; 
44	         touchpad_get_xy(&data->point.x, &data->point.y);
45	    } else { 
46	        data->state = LV_INDEV_STATE_RELEASED;
47	    }
48	}
  • 在主函数中的while(1)循环中或者在一个操作系统的任务中需要定期的每隔几毫秒调用一次lv_timer_handler()。如果需要它将重新绘制屏幕的画面、处理输入设备、动画等。

更多的细节请参考Porting移植部分。

2.3 了解一点入门知识

2.3.1 Widgets窗口小部件

        像按钮、滑块、标签、图表等图形化组件被称为对象或者窗口小部件,在文档的Widgets(小部件)可以找到可用小部件的全部列表。

        每一个创建的对象都有一个它的父类。例如:如果在按钮上创建一个标签,那么按钮就是这个标签的父对象。

        子对象随着父对象移动而移动,并且如果父对象被删除那么子对象也会被删除。

        子对象只可以在父对象上可见,换句话说,子对象超出父对象的部分是被裁剪的或者说是被遮挡的。

        屏幕对象是所有对象的根对象,你可以有任意数量的屏幕。

        获取当前屏幕对象调用lv_scr_act() ,加载一个屏幕使用lv_scr_load(scr1).

        你可以使用lv_<type>_create(parent)来创建一个新的对象,它会返回一个lv_obj_t  *  型变量(句柄),可以通过这个句柄来设置新创建对象的参数。

For example:

lv_obj_t  *  slider1  =  lv_slider_create(lv_scr_act());

        可以使用函数 lv_obj_set_<parameter_name>(obj,  <value>) 去设置一些基础属性。

For example:

lv_obj_set_x(btn1,  30);
lv_obj_set_y(btn1,  10);
lv_obj_set_size(btn1,  200,  50);

        小部件有一些特定的参数可以通过lv_<widget_type>_set_<parameter_name>(obj,<value>)进行设置 。

For example:

lv_slider_set_value(slider1,  70,  LV_ANIM_ON);

详细的API请阅读小部件部分的文档或者阅读相关的头文件 (e.g.  lvgl/src/widgets/lv_slider.h).

2.3.2 Events(事件)

        事件用来通知用户对象发生了什么事情。你可以给一个对象绑定很多动作的回调函数,例如:点击、释放、拖拽、甚至被删除等。

回调函数是这样被分配的:

v_obj_add_event_cb(btn,  btn_event_cb,  LV_EVENT_CLICKED,  NULL);  /*Assign a callback␣to the button*/

...

void btn_event_cb(lv_event_t  *  e)
{
printf("Clicked\n");
}

也可以使用LV_EVENT_CLICKED LV_EVENT_ALL,以便对任何事件调用回调。

从 lv_event_t  *  e  当前事件的代码可以这样获取:

lv_event_code_t  code  =  lv_event_get_code(e);

触发事件的对象可以被这样接受: 

lv_obj_t  *  obj  =  lv_event_get_target(e);

要了解事件的所有功能,请转到Event overview部分。

2.3.3 Parts

        widgets部件可以由一个或者多个零件构建而成。例如: 一个按钮只有一个零件组成 LV_PART_MAIN. 然而, 一个 Slider 滑块 LV_PART_MAIN, LV_PART_INDICATOR and LV_PART_KNOB. 三个零件组成。

通过使用Parts 你能够在不同的零件使用不同的风格。(见下文)

要了解一个部件对象可以使用那个零件请阅读widgets 部分的文档。

2.3.4 States

对象可以处于以下状态组合中:

  • LV_STATE_DEFAULT 正常, 释放状态默认状态。
  • LV_STATE_CHECKED 切换或者选中状态。
  • LV_STATE_FOCUSED 通过键盘或编码器聚焦或通过触摸板/鼠标点击。
  • LV_STATE_FOCUS_KEY 通过键盘或编码器聚焦,但不通过触摸板/鼠标。
  • LV_STATE_EDITED  编码态。
  • LV_STATE_HOVERED 鼠标悬停 (现在不支持)。
  • LV_STATE_PRESSED 被按着。
  • LV_STATE_SCROLLED 滑动中。
  • LV_STATE_DISABLED 失效的

例如:如果按压一个对象,他将会自动的变成 LV_STATE_FOCUSED LV_STATE_PRESSED 状态,而且当你释放它时,LV_STATE_PRESSED状态就会移除 。

去检查一个对象处于何种状态可以使用lv_obj_has_state(obj,  LV_STATE_...).如果对象当时处于这个状态它会返回ture。

手动添加或者移除状态使用:

lv_obj_add_state(obj,  LV_STATE_...);

lv_obj_clear_state(obj,  LV_STATE_...);

2.3.5 Style

Styles包含一些描绘对象外观的一些参数,例如:背景颜色、边框宽度、字体等内容。

风格对象是lv_style_t类型的变量,只有它们的指针被保存在对象中,因此它们应该是静态的或者是全局的。 在使用一个风格之前它需要被lv_style_init(&style1)方法初始化,然后再添加相关的属性.。

例如:

static lv_style_t  style1;
lv_style_init(&style1);
lv_style_set_bg_color(&style1,  lv_color_hex(0xa03080));
lv_style_set_border_width(&style1,  2));

样式可以指定给对象的零件和状态.例如:"按下滑块时在滑块上显示这种样式" :

lv_obj_add_style(slider1,  &style1,  LV_PART_INDICATOR  |  LV_STATE_PRESSED);

如果这个零件是LV_PART_MAIN,那么它将被省略:

lv_obj_add_style(btn1,  &style1,  LV_STATE_PRESSED);  /*Equal to LV_PART_MAIN | LV_STATE_PRESSED*/

同样,也可以省略LV_STATE_DEFAULT:

lv_obj_add_style(slider1,  &style1,  LV_PART_INDICATOR);  /*Equal to LV_PART_INDICATOR |␣LV_STATE_DEFAULT*/

对于LV_STATE_DEFAULT 和LV_PART_MAIN 可以简单的写0:

lv_obj_add_style(btn1,  &style1,  0);  /*Equal to LV_PART_MAIN | LV_STATE_DEFAULT*/

风格可以级联(与CSS相似)。 这意味着你可以给对象的某个零件添加更多的样式。

For example :style_btn 可以设置一个默认的外观,而style_btn_red能够覆盖背景颜色使按钮变成红色。

lv_obj_add_style(btn1,  &style_btn,  0);
lv_obj_add_style(btn1,  &style1_btn_red,  0);

如果未设置属性,则当前状态将使用LV_State_Default的样式。如果即使在默认状态下也未定义该属性,则使用默认值。 

一些属性(通常相关的文本相关的属性)是可以被继承的。这意味着如果未在对象中设置属性,则它将在其父项中搜索。例如,您可以在屏幕的样式中设置字体,并且屏幕上的所有文本将默认继承。

本地样式属性也可以添加到对象中。它创建了一种驻留在对象内部的样式,其仅由对象使用:

lv_obj_set_style_bg_color(slider1,  lv_color_hex(0x2080bb),  LV_PART_INDICATOR  |  LV_STATE_PRESSED);

要了解样式的所有功能,请参阅Style overview部分。

2.3.6 Themes(主题)

主题是对象的默认样式。当对象创建时,主题的样式就自动的被应用到对象上了。

你可以在lv_conf.h里面选择主题。

2.4 示例(C语言)

带有一个标签并且在点击事件做出响应的按钮示例

#include "../lv_examples.h" 
#if LV_BUILD_EXAMPLES && LV_USE_BTN
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. */
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);
} 
 
  

#endif

从头为一个按钮创建一个样式

#include "../lv_examples.h"
#if LV_USE_BTN && LV_BUILD_EXAMPLES

static lv_style_t  style_btn;
static lv_style_t  style_btn_pressed;
static lv_style_t  style_btn_red;

static lv_color_t  darken(const lv_color_filter_dsc_t  *  dsc,  lv_color_t  color,  lv_opa_t  opa)
{
    LV_UNUSED(dsc);
    return lv_color_darken(color,  opa);
}

static void style_init(void)
{
    /*Create a simple button style*/
    lv_style_init(&style_btn);                         
    lv_style_set_radius(&style_btn,  10); 
    lv_style_set_bg_opa(&style_btn,  LV_OPA_COVER);
    lv_style_set_bg_color(&style_btn,  lv_palette_lighten(LV_PALETTE_GREY,  3));         
    lv_style_set_bg_grad_color(&style_btn,  lv_palette_main(LV_PALETTE_GREY));     
    lv_style_set_bg_grad_dir(&style_btn,  LV_GRAD_DIR_VER);

    lv_style_set_border_color(&style_btn,  lv_color_black());     
    lv_style_set_border_opa(&style_btn,  LV_OPA_20);     
    lv_style_set_border_width(&style_btn,  2);

    lv_style_set_text_color(&style_btn,  lv_color_black());

    /*Create a style for the pressed state.
    *Use a color filter to simply modify all colors in this state*/ static         
    lv_color_filter_dsc_t  color_filter; lv_color_filter_dsc_init(&color_filter, darken); 
    lv_style_init(&style_btn_pressed);         
    lv_style_set_color_filter_dsc(&style_btn_pressed,&color_filter); 
    lv_style_set_color_filter_opa(&style_btn_pressed,  LV_OPA_20);

    /*Create a red style. Change only some colors.*/
    lv_style_init(&style_btn_red);
    lv_style_set_bg_color(&style_btn_red,  lv_palette_main(LV_PALETTE_RED)); 
    lv_style_set_bg_grad_color(&style_btn_red,  lv_palette_lighten(LV_PALETTE_RED,  3));
}

/**
* Create styles from scratch for buttons.
*/
void lv_example_get_started_2(void)
{
    /*Initialize the style*/
    style_init();

    /*Create a button and use the new styles*/
    lv_obj_t  *  btn  =  lv_btn_create(lv_scr_act());
    /* Remove the styles coming from the theme
     *	Note that size and position are also stored as style properties
     *	so lv_obj_remove_style_all will remove the set size and position too */
    lv_obj_remove_style_all(btn); 
    lv_obj_set_pos(btn,  10,  10);
    lv_obj_set_size(btn,  120,  50);
    lv_obj_add_style(btn,  &style_btn,  0);
    lv_obj_add_style(btn,  &style_btn_pressed,  LV_STATE_PRESSED);

    /*Add a label to the button*/
    lv_obj_t  *  label  =  lv_label_create(btn); 
    lv_label_set_text(label,  "Button");         
    lv_obj_center(label);

    /*Create an other button and use the red style too*/
    lv_obj_t  *  btn2  =  lv_btn_create(lv_scr_act());
    lv_obj_remove_style_all(btn2);	/*Remove the styles coming␣from the theme*/
    lv_obj_set_pos(btn2,  10,  80);
    lv_obj_set_size(btn2,  120,  50);
    lv_obj_add_style(btn2,  &style_btn,  0);
    lv_obj_add_style(btn2,  &style_btn_red,  0);
     lv_obj_add_style(btn2,  &style_btn_pressed,  LV_STATE_PRESSED);
    lv_obj_set_style_radius(btn2,  LV_RADIUS_CIRCLE,  0);
      /*Add a local style too*/

    label  =  lv_label_create(btn2); 
    lv_label_set_text(label,  "Button  2");            
    lv_obj_center(label);
}

#endif

创建一个滑块并且将其值写到标签上。

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SLIDER

static lv_obj_t  *  label;

static void slider_event_cb(lv_event_t  *  e)
{
    lv_obj_t  *  slider  =  lv_event_get_target(e);

    /*Refresh the text*/
    lv_label_set_text_fmt(label,  "%d",  lv_slider_get_value(slider));
    lv_obj_align_to(label,  slider,  LV_ALIGN_OUT_TOP_MID,  0,  -15);	/*Align top of the slider*/
}


/* Create a slider and write its value on a label. */
void lv_example_get_started_3(void)
{
    /*Create a slider in the center of the display*/ 
    lv_obj_t * slider =  lv_slider_create(lv_scr_act()); 
    lv_obj_set_width(slider, 200);     /*Set the width*/
    lv_obj_center(slider);/*Align to the center the parent (screen)*/ 
    lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); /*Assign an event function*/
    /*Create a label below the slider*/ 
    label = lv_label_create(lv_scr_act());                 
    lv_label_set_text(label, "0"); 
    lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15);/*Align top of
the slider*/
}
#endif



总结

 类似资料: