项目经验分享:LVGL编程举例

本文介绍如何在成功移植LVGL的基础之上,编写自己的LVGL GUI程序。

1. LVGL组件简介与LVGL仿真

1.1 LVGL组件

与绝大多数GUI组件相似,LVGL的GUI组件主要包括Lable(标签)、Button(按键)、Image(图片)、Image Button(图片按钮)、Keyboard(键盘)、Calendar(日历表)、Chart(数据表)等。LVGL组件的官方文档如下:

Welcome to the documentation of LVGL! — LVGL documentation

LVGL编程采用了类似QT的面向对象的编程方法,但是使用的编程语言是C而不是C++,创建LVGL组件对象的方法如下:

lv_obj_t              *my_obj = lv_XXX_create(lv_scr_act());

LVGL中所有的组件对象都是由lv_obj_t来定义的,并通过对应的组件创建函数lv_XXX_create来创建相关的对象。lv_scr_act()表示对组件赋予显示功能。

1.2 LVGL仿真

直接在Linux系统中进行LVGL GUI界面编程不方便,也不利于时刻显示编程结果,这里建议在CodeBlock中使用LVGL模拟仿真器,来实时知晓自己编程的结果,对应的教程如下:

在CodeBlock中实现LVGL模拟仿真

本文先在CodeBlock中进行LVGL的仿真,然后再将源码放在Linux下进行编译与移植,使用的显示屏分辨率为800*480。

2. 代码结构

编程的代码结构如下:

在这里插入图片描述

官方的demo代码都是写在lv_demo_widgets.c中的,所以我们主要对其中的lv_demo_widgets.c文件进行编程和修改。assets目录中的两个文件是将图像数组化之后生成的C文件,这两个C文件里面包含了图像的像素信息,图像数组化转换的方法会在之后的导入图片步骤中进行介绍。lv_events文件夹是新创建的文件夹,其中的文件声明并定义了各个组件的事件响应函数。

3. 编程目标

本文设计的LVGL GUI界面如下:

在这里插入图片描述

我们只实现上述图片中LED灯控制开关、蜂鸣器控制开关、温湿度实时显示的三个相关功能。其中LED开关开启后,外接的LED灯会被点亮,开关关闭之后灯会熄灭;Buzzer开关开启之后,板载的蜂鸣器会响,

4. 编程前的准备

上一章展示的GUI界面涉及到不同大小的字体,所以在构建我们的程序之前,需要将所有大小的字体使能,我们先打开源码根目录下的文件lv_conf.h,看到第326行,我们将所有字体大小的宏定义全部改为1:

/*Montserrat fonts with ASCII range and some symbols using bpp = 4
 *https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8  1
#define LV_FONT_MONTSERRAT_10 1
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
/*... ...*/
#define LV_FONT_MONTSERRAT_48 1

5. LVGL编程基础

5.1 简单示例代码

下面是一个简单的LVGL编程实例,在一个Label中显示”Hello World!”:

#include "lv_demo_widgets.h"
#include "lv_events/lv_events.h"

lv_obj_t              *label_example;

void lv_demo_widgets(void){
    label_example = lv_label_create(lv_scr_act());
    lv_label_set_text(label_example, "Hello World!");
    lv_obj_set_style_text_font(label_example, &lv_font_montserrat_24, LV_STATE_DEFAULT);
    lv_obj_align(label_example,LV_ALIGN_CENTER,0,0);
    return ;
};

将上述代码在LVGL仿真器中运行,效果如下:

在这里插入图片描述

简要说一下其中代码的逻辑:

  1. label_example = lv_label_create(lv_scr_act());这句代码的作用是创建一个Label组件的对象,lv_label_create就是创建它的函数,其返回值是lv_obj_t *类型,即所有组件对象的指针,其中函数lv_scr_act的作用是让创建的该组件能在屏幕上显示;

  2. lv_label_set_text(label_example, "Hello World!");这句代码的作用是设置Label组件文本的内容,其中第一个参数是创建好的Label对象指针,第二个参数是需要设置的文本内容;

  3. lv_obj_set_style_text_font(label_example, &lv_font_montserrat_24, LV_STATE_DEFAULT); 这句代码的作用是设置Label文本的字体大小,如第4章中头文件所展示的那样,这里的宏lv_font_montserrat_24表示将文本字体大小设置为24;

  4. lv_obj_align(label_example,LV_ALIGN_CENTER,0,0); 这句代码的作用是设置组件对齐。

5.2 设置组件位置

在LVGL的实际运用中,我们有两中方法来设置组件的位置。

  • 第一种是直接指定其在屏幕上的位置坐标,来确定组件的绝对位置,例如:
lv_obj_set_pos(calendar, 511, 32);

上面的代码是设置一个日历(Calendar)组件的坐标位置,函数lv_obj_set_pos原型为void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y),其中第一个参数是已经创建好的组件对象指针,第二和第三个参数分别是横向坐标值和纵向坐标值。

  • 第二种则是通过对齐的方式,来确定组件的相对位置,这时使用的函数为:

void lv_obj_align(lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)

或者

void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)

其中第一个函数是组件相对于显示屏对齐,第二个则是组件相对于其他组件进行对齐,形参align表示对齐的方式,LVGL中所有的对齐方式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0twYRi9V-1685537889428)(/align.png)]

这些对齐方式所对应宏的通用形式为:LV_ALIGN_OUT_XXX 。函数lv_obj_alignlv_obj_align_to 中共有的形参lv_coord_t x_ofs, lv_coord_t y_ofs表示相对于屏幕上的(x_ofs, y_ofs)坐标对齐。

5.3 图片的显示

本节我们讲述将图片lvgl_shallwing.jpg显示到LCD屏上的方法,图片lvgl_shallwing.jpg如下图所示:

在这里插入图片描述

在LVGL显示图片需要先将图片转化为像素数组,其转化的方法如下:

  1. 打开网页链接:LVGL官网

  2. 点击网页中的”Tools“,并选择其中的Image converter:

在这里插入图片描述

  1. 在弹出的网页中,可以看到在线图片转换器,图片转换器里面有许多选项,如下图所示:

在这里插入图片描述

  1. 我们现在Imgae file中选择需要转化的图片,然后选择图片的色彩格式为CF_TRUE_COLOR(真彩格式),Output format保持为C array不变,然后点击Convert进行转换;

  2. 点击“Convert”之后,网页会自动下载生成好的带有C array的C文件:
    在这里插入图片描述

    我们将生成好的lvgl_shallwing.c文件放在下列LVGL源码中的目录下,以便之后调用该文件中的生成的数组:

./lv_port_linux_frame_buffer/lvgl/demos/widgets/assets

以上就是利用LVGL官方的图片转换器进行图片像素数字化的方法。

先给出在LVGL中显示图片的代码:

void lv_demo_widgets(void){
	lv_obj_t              *image_shallwing = lv_img_create(lv_scr_act());

    //Set the image size and the position
    lv_img_set_src(image_shallwing, &lvgl_shallwing);
    lv_obj_set_pos(image_shallwing, 54, 52);
	return ;
}

这段代码的逻辑很简单,函数lv_img_create的作用是创建一个image图片组件,其使用方法和lv_label_create类似。函数lv_img_set_src用于设置图片的内容,函数lv_img_set_src的第二个参数是图片的像素数组名,图片lvgl_shallwing.jpg转换生成的C文件名称为lvgl_shallwing.c,所以该图片生成的像素数组的数组名叫lvgl_shallwing。函数lv_obj_set_pos的作用是设置图片左上角在界面上的坐标位置,这里我们将坐标设置为(54,52);

将上面的代码编译之后,可以在LVGL模拟器中看到如下效果:

在这里插入图片描述

5.4 组件的事件响应

与其他图形库的编程一样,LVGL也有自己的事件响应机制,LVGL中设置组件事件响应的函数为:

struct _lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter,
                                             void * user_data)

其中第一个参数obj为创建好的组件对象指针;event_cb是事件处理函数,当组件出发了事件时,就会执行此函数;lv_event_code_t表示事件码,事件码用来指代事件的类型,此形参需要通过具体组件来进行设置;事件响应函数中运行用户自己传入参数,这在形参user_data中有所体现。

LVGL组件中的事件码如下:

/**
 * Type of event being sent to the object.
 */
typedef enum {
    LV_EVENT_ALL = 0,
 
    /** Input device events*/
    LV_EVENT_PRESSED,             /**< The object has been pressed*/
    LV_EVENT_PRESSING,            /**< The object is being pressed (called continuously while pressing)*/
    LV_EVENT_PRESS_LOST,          /**< The object is still being pressed but slid cursor/finger off of the object */
    LV_EVENT_SHORT_CLICKED,       /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
    LV_EVENT_LONG_PRESSED,        /**< Object has been pressed for at least `long_press_time`.  Not called if scrolled.*/
    LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms.  Not called if scrolled.*/
    LV_EVENT_CLICKED,             /**< Called on release if not scrolled (regardless to long press)*/
    LV_EVENT_RELEASED,            /**< Called in every cases when the object has been released*/
    LV_EVENT_SCROLL_BEGIN,        /**< Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified*/
    LV_EVENT_SCROLL_END,          /**< Scrolling ends*/
    LV_EVENT_SCROLL,              /**< Scrolling*/
    LV_EVENT_GESTURE,             /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */
    LV_EVENT_KEY,                 /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/
    LV_EVENT_FOCUSED,             /**< The object is focused*/
    LV_EVENT_DEFOCUSED,           /**< The object is defocused*/
    LV_EVENT_LEAVE,               /**< The object is defocused but still selected*/
    LV_EVENT_HIT_TEST,            /**< Perform advanced hit-testing*/
 
    /** Drawing events*/
    LV_EVENT_COVER_CHECK,        /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/
    LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get the required extra draw area around the object (e.g. for shadow). The event parameter is `lv_coord_t *` to store the size.*/
    LV_EVENT_DRAW_MAIN_BEGIN,    /**< Starting the main drawing phase*/
    LV_EVENT_DRAW_MAIN,          /**< Perform the main drawing*/
    LV_EVENT_DRAW_MAIN_END,      /**< Finishing the main drawing phase*/
    LV_EVENT_DRAW_POST_BEGIN,    /**< Starting the post draw phase (when all children are drawn)*/
    LV_EVENT_DRAW_POST,          /**< Perform the post draw phase (when all children are drawn)*/
    LV_EVENT_DRAW_POST_END,      /**< Finishing the post draw phase (when all children are drawn)*/
    LV_EVENT_DRAW_PART_BEGIN,    /**< Starting to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
    LV_EVENT_DRAW_PART_END,      /**< Finishing to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
 
    /** Special events*/
    LV_EVENT_VALUE_CHANGED,       /**< The object's value has changed (i.e. slider moved)*/
    LV_EVENT_INSERT,              /**< A text is inserted to the object. The event data is `char *` being inserted.*/
    LV_EVENT_REFRESH,             /**< Notify the object to refresh something on it (for the user)*/
    LV_EVENT_READY,               /**< A process has finished*/
    LV_EVENT_CANCEL,              /**< A process has been cancelled */
 
    /** Other events*/
    LV_EVENT_DELETE,              /**< Object is being deleted*/
    LV_EVENT_CHILD_CHANGED,       /**< Child was removed, added, or its size, position were changed */
    LV_EVENT_CHILD_CREATED,       /**< Child was created, always bubbles up to all parents*/
    LV_EVENT_CHILD_DELETED,       /**< Child was deleted, always bubbles up to all parents*/
    LV_EVENT_SCREEN_UNLOAD_START, /**< A screen unload started, fired immediately when scr_load is called*/
    LV_EVENT_SCREEN_LOAD_START,   /**< A screen load started, fired when the screen change delay is expired*/
    LV_EVENT_SCREEN_LOADED,       /**< A screen was loaded*/
    LV_EVENT_SCREEN_UNLOADED,     /**< A screen was unloaded*/
    LV_EVENT_SIZE_CHANGED,        /**< Object coordinates/size have changed*/
    LV_EVENT_STYLE_CHANGED,       /**< Object's style has changed*/
    LV_EVENT_LAYOUT_CHANGED,      /**< The children position has changed due to a layout recalculation*/
    LV_EVENT_GET_SELF_SIZE,       /**< Get the internal size of a widget*/
 
    _LV_EVENT_LAST,               /** Number of default events*/
 
 
    LV_EVENT_PREPROCESS = 0x80,   /** This is a flag that can be set with an event so it's processed
                                      before the class default event processing */
} lv_event_code_t;

可以看到,LVGL中的事件大致分为输入设备事件(Input device events)、绘图事件(Drawing events)、特殊事件(Special events)以及其他事件四种,且事件码由枚举类型定义。本文需要实现的开关功能则对应于特殊事件中的 LV_EVENT_VALUE_CHANGED,意思就是此枚举与开关事件相关联时,开关组件的状态若发生了改变,系统就会调用事件响应函数进行处理

Button组件按下对应的事件码为LV_EVENT_PRESSED,表示有组件被点击的事件。

我们也可以在自己的程序中某时刻撤销组件的事件响应,撤销事件响应的函数为:

bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)

此函数的返回值类型是bool,若返回false,表示没有在组件中找到形参中的事件响应函数,当返回true时,则表示此时已经成功撤销该事件的响应机制。

后面的章节里会就开关组件事件响应编写对应的代码。

5.5 设置定时任务

在LVGL里,我们可以利用其中的定时机制来周期性完成某些任务,比如后面将会实现的定时20s显示环境温湿度值。

当前现存的一些中文教程中会告诉读者设置定时任务的函数是lv_task_create() ,但这是LVGL 7.0以前的版本使用的,目前的V8.2版本已经将其改为了lv_timer_create()

在这里插入图片描述

这在LVGL源码的./lvgl/docs/overview/timer.md文件中也有说明:

# Timers

LVGL has a built-in timer system. You can register a function to have it be called periodically. The timers are handled   and called in `lv_timer_handler()`, which needs to be called every few milliseconds.
See [Porting](/porting/task-handler) for more information.

Timers are non-preemptive, which means a timer cannot interrupt another timer. Therefore, you can call any LVGL related   function in a timer. 


## Create a timer
To create a new timer, use `lv_timer_create(timer_cb, period_ms, user_data)`. It will create an `lv_timer_t *` variable,  which can be used later to modify the parameters of the timer. 
`lv_timer_create_basic()` can also be used. This allows you to create a new timer without specifying any parameters.

V8.2 版本中设置定时任务的函数lv_timer_create 原型为:

lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data);

其中第一个参数timer_xcb是定时任务所对应的回调函数,第二个参数period是定时周期,第三个参数是用户传入的数据。

6. 各个组件编程的实现

本章节主要就第3章展示的目标,逐个讲述其中各个组件的创建和功能实现。

6.1 文本标签组件(lv_label)

第3章所展示的界面中有如下部分是文本标签组件:

在这里插入图片描述

在第5章已经介绍了文本标签组件的创建和显示,以及字体大小的设置,这里直接给出上面所有文本标签组件相应的代码:

/* The txt label for temperature and humidty are global variables, 
which are used for define event handler. */
static lv_obj_t              *label_temper;
static lv_obj_t              *label_humidity;

//...
//...
//...

void lv_demo_widgets(void){
/* Create the Label widgets and display on the LCD */    
    lv_obj_t              *label_lvgl_demo = lv_label_create(lv_scr_act());
    lv_obj_t              *label_author = lv_label_create(lv_scr_act());
    lv_obj_t              *label_studio = lv_label_create(lv_scr_act());

    lv_obj_t              *label_led = lv_label_create(lv_scr_act());
    lv_obj_t              *label_buzzer = lv_label_create(lv_scr_act());   
    
    label_temper = lv_label_create(lv_scr_act());
    label_humidity = lv_label_create(lv_scr_act());

/* Set the position and the text and its size */
    //"Donald Shallwing"
    lv_obj_set_pos(label_author, 193, 61);
    lv_label_set_text(label_author, "Donald Shallwing");
    lv_obj_set_style_text_font(label_author, &lv_font_montserrat_24, LV_STATE_DEFAULT);

    //"donaldshallwing@gmail.com"
    lv_obj_set_pos(label_lvgl_demo, 193, 108);
    lv_label_set_text(label_lvgl_demo, "donaldshallwing@gmail.com");
    lv_obj_set_style_text_font(label_lvgl_demo, &lv_font_montserrat_14, LV_STATE_DEFAULT);

    //"IoT-Yun"
    lv_obj_set_pos(label_studio, 193, 135);
    lv_label_set_text(label_studio, "IoT-Yun / CCNU");
    lv_obj_set_style_text_font(label_studio, &lv_font_montserrat_14, LV_STATE_DEFAULT);
    
    // "LED" & "Buzzer"
    lv_obj_set_pos(label_led, 90, 270);
    lv_label_set_text(label_led, "LED");
    lv_obj_set_style_text_font(label_led, &lv_font_montserrat_20, LV_STATE_DEFAULT);
    lv_obj_set_pos(label_buzzer, 80, 382);
    lv_label_set_text(label_buzzer, "Buzzer");
    lv_obj_set_style_text_font(label_buzzer, &lv_font_montserrat_20, LV_STATE_DEFAULT);

    // Label for temperature & humidty display, set " " for primary
    lv_obj_set_pos(label_temper, 662, 284);
    lv_label_set_text(label_temper, " ");
    lv_obj_set_style_text_font(label_temper, &lv_font_montserrat_22, LV_STATE_DEFAULT);
    lv_obj_set_pos(label_humidity, 662, 329);
    lv_label_set_text(label_humidity, " ");
    lv_obj_set_style_text_font(label_humidity, &lv_font_montserrat_22, LV_STATE_DEFAULT);
    
    return ;
}

温湿度需要使用定时任务机制来进行实时获取,而且我们要求温湿度的数值显示在文本标签组件中,所以这里把温湿度对应的文本标签组件变量label_temper与label_humidity设置为全局变量,为了与其他文件中的命名产生冲突,必须在定义时加上static关键字。

6.2 开关组件(lv_switch)

创建显示LED和Buzzer两个开关组件、并设置事件响应的代码如下:

static lv_obj_t              *sw_led;
static lv_obj_t              *sw_buzzer;

static void led_event_cb(lv_event_t *e){

    lv_event_code_t code = lv_event_get_code(e);
    if(LV_EVENT_VALUE_CHANGED == code){
        if(lv_obj_has_state(sw_led, LV_STATE_CHECKED))
            led(true);
        else
            led(false);
    }
    return ;
}

static void buzzer_event_cb(lv_event_t *e){

    lv_event_code_t code = lv_event_get_code(e);
    if(LV_EVENT_VALUE_CHANGED == code){
        if(lv_obj_has_state(sw_buzzer, LV_STATE_CHECKED))
            buzzer(true);
        else
            buzzer(false);
    }
    return ;
}

void lv_demo_widgets(void){
	sw_led = lv_switch_create(lv_scr_act());
    sw_buzzer = lv_switch_create(lv_scr_act());
    
    lv_obj_set_pos(sw_led, 83, 234);
    lv_obj_set_size(sw_led, 60, 30);
    lv_obj_add_event_cb(sw_led, led_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    lv_obj_set_pos(sw_buzzer, 83, 335);
    lv_obj_set_size(sw_buzzer, 60, 30);
    lv_obj_add_event_cb(sw_buzzer, buzzer_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    
    return ;
}

LVGL中所有事件的回调函数都具有相同的形参,即lv_event_t *e,在一个会掉函数里,我们首先通过函数lv_event_get_code取得事件码(lv_event_code_t),如果事件码与lv_obj_add_event_cb中添加的事件码一直,则运行后面的处理程序。我们可以看到在回调函数led_event_cb中,通过函数lv_obj_has_state取得开关的状态之后,如果此时开关是开启的,则通过自定的函数led来点亮LED灯,函数led的代码在第二章中所述的文件lv_events.c中。

蜂鸣器的事件响应函数逻辑与LED灯是一样的。

6.3 图片按键组件(lv_btn)

第3章里展示的温湿度logo是一个图片按键组件,从本质上来看,它和普通的Button没什么区别,其创建显示,以及事件响应的代码如下:

static lv_obj_t              *image_temper;

#if USE_IMGBTN_EVENT
static void sht20_event_cb(lv_event_t *e){

    double               temperature, humidity;
	char				 buffer[32] = {0};

    lv_event_code_t code = lv_event_get_code(e);

    if(LV_EVENT_PRESSED == code){
    	sht20(&temperature, &humidity);
    	sprintf(buffer, "%.2lf 'C", temperature);
		lv_label_set_text(label_temper, buffer);
		memset(buffer, 0, sizeof(buffer));
		sprintf(buffer, "%.2lf %%", humidity);
    	lv_label_set_text(label_humidity, buffer);
	}
    return ;
}
#endif

void lv_demo_widgets(void){
    //Image for temperature & humidity
    lv_imgbtn_set_src(image_temper, LV_IMGBTN_STATE_RELEASED,
                      NULL, &lvgl_temper, NULL);
    
    lv_obj_set_size(image_temper, 80, 100);
    lv_obj_set_pos(image_temper, 557, 267);
    
	#if USE_IMGBTN_EVENT
		lv_obj_add_event_cb(image_temper, sht20_event_cb, LV_EVENT_PRESSED, NULL);
	#endif
    return ;
}

上述代码中回调函数的逻辑是:当该图片按键按下之后,系统会通过函数sht20读取当前sht20温湿度传感器中的温湿度值,然后在文本标签组件label_temperlabel_humidity中进行显示。

我们这里只是演示一下图片按键的用法,在这个项目里面,我们并不是通过图片按键按下的方式获取温湿度值,而是设置定时任务实时显示,所以这里我们将上述代码中的宏USE_IMGBTN_EVENT定义为0。

6.4 设置定时任务(lv_timer)

由于在5.5小节已经讲述了定时任务的设置方法,这里直接给出其中温湿度实时显示的代码:

static void sht20_task(lv_timer_t *timer){

    double               temperature, humidity;
	char				 buffer[32] = {0};

    sht20(&temperature, &humidity);
    sprintf(buffer, "%.2lf 'C", temperature);
	lv_label_set_text(label_temper, buffer);
	memset(buffer, 0, sizeof(buffer));
	sprintf(buffer, "%.2lf %%", humidity);
    lv_label_set_text(label_humidity, buffer);
    
    return ;
}

void lv_demo_widgets(void){
	timer_sht20 = lv_timer_create(sht20_task, 1000*20, NULL);
	return ;
}

我们将温湿度读取的时间间隔设置为20s。

6.5 显示其他组件

第三章中所展示的日历(lv_calendar)和图表(lv_chart)组件我们只做了显示,没有实现其事件响应,此工作留在之后的文档中完成。显示日历和图表的代码如下:

static lv_obj_t              *calendar = lv_calendar_create(lv_scr_act());
static lv_obj_t              *temper_chart = lv_chart_create(lv_scr_act());

void lv_demo_widgets(void){
    
	//Calendar
    lv_obj_set_pos(calendar, 511, 32);
    lv_obj_set_size(calendar, 205, 155);

    //Chart
    lv_obj_set_pos(temper_chart, 245, 222);
    lv_obj_set_size(temper_chart, 254, 190);
    
    return ;
}

7. 代码测试

将上述代码在Linux下通过源码的主Makefile交叉编译之后,然后通过TFTP将可执行文件传输到IMX6ULL开发板上,进行如下测试:

  1. 若执行之后显示的效果和仿真一样,则说明组件的创建与显示部分正常;
  2. 若在点击LED的开关之后,LED灯被点亮,且再次按下开关之后,LED灯熄灭,则说明LED事件响应函数设置正确;
  3. 若在点击蜂鸣器的开关之后,板载蜂鸣器发出声响,且再次按下开关之后,不再发声,则说明蜂鸣器事件响应函数设置正确;
  4. 若在屏幕上每隔20s能看到温湿度在更新,则说明实时过去温湿度的功能已经实现。

8. 工程源码

本工程的所有源码已在Gitee上给出,链接如下:
lvgl-lcd
由于点亮LED灯和使能蜂鸣器的代码不属于LVGL编程的范围,故此代码未在文档中展示。

参考文档

【LVGL事件(Events)】事件代码
在CodeBlock中实现LVGL模拟仿真

  • 19
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值