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

news2024/11/24 9:54:08

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

文章目录

  • 1. LVGL组件简介与LVGL仿真
    • 1.1 LVGL组件
    • 1.2 LVGL仿真
  • 2. 代码结构
  • 3. 编程目标
  • 4. 编程前的准备
  • 5. LVGL编程基础
    • 5.1 简单示例代码
    • 5.2 设置组件位置
    • 5.3 图片的显示
    • 5.4 组件的事件响应
    • 5.5 设置定时任务
  • 6. 各个组件编程的实现
    • 6.1 文本标签组件(lv_label)
    • 6.2 开关组件(lv_switch)
    • 6.3 图片按键组件(lv_btn)
    • 6.4 设置定时任务(lv_timer)
    • 6.5 显示其他组件
  • 7. 代码测试
  • 8. 工程源码
  • 参考文档

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模拟仿真

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594980.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【P46】JMeter 响应断言(Response Assertion)

文章目录 一、响应断言&#xff08;Response Assertion&#xff09; 参数说明二、准备工作三、测试计划设计3.1、包括3.2、匹配3.3、相等3.4、字符串3.5、字符串3.6、或者 一、响应断言&#xff08;Response Assertion&#xff09; 参数说明 可以对 Jmeter 取样器的响应消息进…

0x10 会话服务 DiagnosticSessionControl

0x10 会话服务 相当是一种分类&#xff0c;通过控制会话模式&#xff0c;使用不同的服务。应始终只有一个诊断会话在服务器中处于活动状态。 服务器应在启动时始终启动默认的诊断会话。 如果没有启动其他诊断会话&#xff0c;则只要服务器通电&#xff0c;默认诊断会话就应该运…

Hudi(五)集成Flink(3)

12、离线compaction MOR表的compaction默认是自动打开的&#xff0c;策略是5个commits执行一次压缩。因为压缩操作比较耗费内存&#xff0c;和写流程放在同一个pipeline&#xff0c;在数据量比较大的时候&#xff08;10w/sqps&#xff09;&#xff0c;容易干扰写流程&#xff0…

LLM:LoRA: Low-Rank Adaptation of Large Language Models

随着模型规模的不断扩大&#xff0c;微调模型的所有参数&#xff08;所谓full fine-tuning&#xff09;的可行性变得越来越低。以GPT-3的175B参数为例&#xff0c;每增加一个新领域就需要完整微调一个新模型&#xff0c;代价和成本很高。 为解决微调大规模语言模型到不同领域和…

西南交通大学智能监测 培训课程练习3

2023.05.31培训 task1&#xff1a;MybatisPlus的使用 task2&#xff1a;SpringMVC常用接口开发 task3&#xff1a;JSON、接口测试 task4&#xff1a;SpringMVC拦截器与过滤器 目录 一、MybatisPlus 1.1DO类 1.2Mapper接口 1.3编写测试类测试 1.4Mybatis和MybatisPlus 二、…

Verilog学习笔记(串口RS232,基于野火教程)

目录 一、串口简介 二、设计与实现 串口数据回环顶层模块设计 串口接收模块uart_rx 串口发送模块uart_tx 顶层模块rs32_top 三、上板验证 一、串口简介 其中SPI和I2C为同步通信接口&#xff0c;双方时钟频率相同。而UART属于异步通信接口&#xff0c;没有统一时钟&…

旅游网项目(SpringBoot2.7.1 + SpringMVC + Mybatis-Plus3.5.0)

技术选型 JAVA版本&#xff1a;JDK17 数据库&#xff1a;Mysql5.7Navicat 后端框架&#xff1a;SpringBoot3.0.6 SpringMVC Mybatis-Plus3.5.0 权限控制&#xff1a;SpringSecurity 前端框架&#xff1a;AdminLTE2 模板引擎&#xff1a;Thymeleaf 工具类&#xff1a;发…

WIN10系统解决IDEA动不动就卡顿一下

1、前言 不知为啥&#xff0c;最近idea一直在卡顿&#xff0c;输入几个代码都会卡两秒&#xff0c;鼠标滚动文件卡两秒&#xff0c;点击打开文件卡两秒&#xff0c;就算是点击上方的工具栏&#xff0c;它也要等两秒才会出来菜单&#xff01; 卡顿的时候整个idea直接无响应&a…

Java性能权威指南-总结3

Java性能权威指南-总结3 性能测试方法原则4:尽早频繁测试小结 Java性能调优工具箱操作系统的工具和分析CPU使用率 性能测试方法 原则4:尽早频繁测试 这是最后的原则。性能测试应该作为开发周期不可或缺的一部分。理想情况下&#xff0c;在代码提交到中心源代码仓库前&#xf…

【数据分享】1929-2022年全球站点的逐月最高气温(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2022年全球气象站…

Win10搭建Nacos2.2.3集群版

Nacos是Alibaba提供的服务注册发现的管理平台&#xff0c;其优异的性能越来越受到广大开发者的喜爱&#xff0c;在构建分布式微服务项目中通常会首选Nacos作为注册/配置中心&#xff0c;在实际开发中为了提升服务的可用性和稳定性&#xff0c;通常都会搭建集群版&#xff0c;有…

《强风吹拂》呐!你喜欢跑步吗?

《强风吹拂》呐&#xff01;你喜欢跑步吗&#xff1f; 三浦紫苑&#xff0c;1976生于东京。主要作品有《多田便利屋》《强风吹拂》《哪啊哪啊~神去村》《编舟记》等 林佩瑾、李建铨、杨正敏 译 文章目录 《强风吹拂》呐&#xff01;你喜欢跑步吗&#xff1f;[toc]动漫摘录箱根驿…

Go Web下gin框架使用(一)

〇、前言 在前面&#xff0c;已经在这篇文章中详细地讨论了 gin 框架下的模板渲染问题&#xff0c;这篇文章主要对 gin 框架的使用进行讨论。 一、不同的路由 以下可以选择不同的路由进行渲染&#xff1a; r : gin.Default()type usr struct {Name string json:"name&…

八、go语言键盘输入和打印输出

键盘输入和打印输出 一、打印输出 1.1 fmt包 fmt包实现了类似C语言printf和scanf的格式化I/O。格式化verb&#xff08;‘verb’&#xff09;源自C语言但更简单。 详见官网fmt的API&#xff1a;https://golang.google.cn/pkg/fmt/ 1.2 导入包 import "fmt"1.3 常…

MyBatis——MyBatis项目搭建

但凡是框架&#xff0c;使用都是分三步走 1.导入jar文件&#xff0c;用maven导入 2.处理配置文件 3.开发业务代码 1.创建maven项目导入相关依赖 在pom文件中导入MyBatis相关依赖jar文件 安装lombok 在File->Settings Pugins 中安装lombok 要想启动lombok的话还需要在B…

GPT带你飞:Chat GPT吊打面试官,实时获取答案,分享调用OpenAI API key+完整源码脚本哦!

目录 福利&#xff1a;文末纯分享中文版CHAT GPT镜像&#xff0c;不存在魔法&#xff0c;纯分享免费使用 故事发生了 火爆GitHub 所以大家注意 网友看了之后调侃到&#xff0c;为了防止线上面试作弊&#xff0c;以后只好把面试都改成线下了。 如何安装 既然是调用GPT的AP…

nodejs基于vue的汽车订票客运站售票网站

使用Mysql创建数据表保存本系统产生的数据。系统可以提供信息显示和相应服务&#xff0c;其管理员负责审核会员充值&#xff0c;审核客户购票信息以及会员购票信息&#xff0c;管理客运班次与留言板&#xff0c;管理会员等级。客户查看客运班次&#xff0c;购买并支付车票&…

【Selenium】常用的Selenium基础使用模板和简单封装

前言 近来又用上了 Selneium &#xff0c;因为反复用到&#xff0c;所以在这里将一些常用的方法封装起来&#xff0c;方便后续的使用。 在这篇文章中&#xff0c;我们将探讨 Selenium 的基础模板和基础封装&#xff0c;以便更好地理解 Selenium 的使用方法。 在Selenium的使用…

python基础----03-----if语句、while、for循环、range语句、continue和break

一 布尔类型和比较运算符 1.1 布尔类型和比较运算符 定义变量存储布尔类型数据&#xff1a;变量名称 布尔类型字面量。 布尔类型不仅可以自行定义同时也可以通过计算的来。也就是使用比较运算符进行比较运算得到布尔类型的结果。在C/C中&#xff0c;比较运算符称之为关系运算…