龙空技术网

嵌入式开源GUI LVGL学习——LVGL的初始化和屏幕的翻转

有AI野心的电工和码农 905

前言:

如今姐妹们对“linux系统图形化界面切换”都比较看重,各位老铁们都想要知道一些“linux系统图形化界面切换”的相关知识。那么小编同时在网络上汇集了一些有关“linux系统图形化界面切换””的相关内容,希望兄弟们能喜欢,小伙伴们快快来学习一下吧!

0. 前言

前一段买了ESP32-S2-HMI-DevKit-1开发板,通过折腾了几个官方自带的例程之后,也算是对这个开发板和其所用的LVGL有了个初步的了解。

下面要进一步的学习LVGL了。

1. LVGL的Get_started

LVGL官方文档的第三章就是Get started, 官方文档写得很好, 看完这一章基本也就可入门了.

(在此, 不得不吐槽一下, 网上搜索LVGL的中文文档, 我只搜到韦*山的百*网一家, 但写得真不怎么样, 条理不清晰, 重点不突出, 看它远没有看官方英文文档效率高. 我一开始先在百问网上看了大概一个小时, 实在觉得烂看不下去, 就下载了官方英文文档的pdf看, 觉得我在百问网上白白浪费了一个小时, 后悔之极.)

现将官方文档"第三章 Get started中的 3.1.2 Add LVGL into your project" 下节, 列在下面, 我觉得这部分比较重要,

且本文下一节也要用到.

If you would rather try LVGL on your own project follow these steps:

Download or clone the library from GitHub with git clone .Copy the lvgl folder into your project.Copy lvgl/lv_conf_template.h as lv_conf.h next to the lvgl folder, change the first #if 0 to 1 to enable the file's content and set the LV_COLOR_DEPTH defines.Include lvgl/lvgl.h in files where you need to use LVGL related functions.Call lv_tick_inc(x) every x milliseconds in a Timer or Task (x should be between 1 and 10). It is required for the internal timing of LVGL. Alternatively, configure LV_TICK_CUSTOM (see lv_conf.h) so that LVGL can retrieve the current time directly.Call lv_init()Create a draw buffer: LVGL will render the graphics here first, and send the rendered image to the display. The buffer size can be set freely but 1/10 screen size is a good starting point.

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.*/
Implement and register a function which can copy the rendered image to an area of your display:
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/lv_disp_drv_init(&disp_drv); /*Basic initialization*/disp_drv.flush_cb = my_disp_flush; /*Set your driver function*/disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/disp_drv.hor_res = MY_DISP_HOR_RES; /*Set the horizontal resolution of the display*/disp_drv.ver_res = MY_DISP_VER_RES; /*Set the vertical resolution of the display*/lv_disp_drv_register(&disp_drv); /*Finally register the driver*/void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p){int32_t x, y;/*It's a very slow but simple implementation.*`set_pixel` needs to be written by you to a set pixel on the screen*/for(y = area->y1; y <= area->y2; y++) {for(x = area->x1; x <= area->x2; x++) {set_pixel(x, y, *color_p);color_p++;}}lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/}
Implement and register a function which can read an input device. E.g. for a touchpad:
static lv_indev_drv_t indev_drv; /*Descriptor of a input device driver*/lv_indev_drv_init(&indev_drv); /*Basic initialization*/indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/indev_drv.read_cb = my_touchpad_read; /*Set your driver function*/lv_indev_drv_register(&indev_drv); /*Finally register the driver*/void my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data){/*`touchpad_is_pressed` and `touchpad_get_xy` needs to be implemented by you*/if(touchpad_is_pressed()) {data->state = LV_INDEV_STATE_PRESSED;touchpad_get_xy(&data->point.x, &data->point.y);} else {data->state = LV_INDEV_STATE_RELEASED;}}
Call lv_timer_handler() periodically every few milliseconds in the main while(1) loop or in an operating system task. It will redraw the screen if required, handle input devices, animation etc.

For a more detailed guide go to the Porting section.

1.1 关于ESP32-S2-HMI开发板上LVGL的初始化

阅读开发板的源代码, 发现其main函数里调用了一个lvgl_init()函数,

这个函数的定义在lvlg_port.c文件中, 在其源码里, 实现了上面所列文档中的大部分步骤:

Call lv_init()Create a draw buffer: LVGL will render the graphics here first, and send the rendered image to the display.Implement and register a function which can copy the rendered image to an area of your display.Implement and register a function which can read an input device.Call lv_timer_handler() periodically every few milliseconds in the main while(1) loop or in an operating system task. It will redraw the screen if required, handle input devices, animation etc.

详细阅读此函数源码, 对于了解LVGL的初始化过程, 还是很有收益的.

2. LVGL屏幕翻转的实现

开发板的例程都是横屏的, 而我想做个竖屏的界面.

那我就需要选择屏幕, 先看看屏幕旋转LVGL的官方文档是怎么说的吧:

官方文档的 4.2.3 Rotation 小节专门讲这个, 摘抄如下:

LVGL supports rotation of the display in 90 degree increments. You can select whether you'd like software rotation or hardware rotation.If you select software rotation (sw_rotate flag set to 1), LVGL will perform the rotation for you. Your driver can and should assume that the screen width and height have not changed. Simply flush pixels to the display as normal. Software rotation requires no additional logic in your flush_cb callback.There is a noticeable amount of overhead to performing rotation in software, which is why hardware rotation is also available. In this mode, LVGL draws into the buffer as though your screen now has the width and height inverted. You are responsible for rotating the provided pixels yourself.The default rotation of your display when it is initialized can be set using the rotated flag. The available options are LV_DISP_ROT_NONE, LV_DISP_ROT_90, LV_DISP_ROT_180, or LV_DISP_ROT_270. The rotation values are relative to how you would rotate the physical display in the clockwise direction. Thus, LV_DISP_ROT_90 means you rotate the hardware 90 degrees clockwise, and the display rotates 90 degrees counterclockwise to compensate.Display rotation can also be changed at runtime using the lv_disp_set_rotation(disp, rot) API.

总结一下:

设sw_rotate标志为1, 可实现屏幕软旋转LV_DISP_ROT_NONE, LV_DISP_ROT_90, LV_DISP_ROT_180, or LV_DISP_ROT_270 这几个常量代表了旋转的角度在运行时, 可通过lv_disp_set_rotation(disp, rot)函数来改变旋转角度.2.1 代码实现2.1.1 初始化时 旋转屏幕

实现屏幕旋转, 关键是将sw_rotate标志为1, 那sw_rotate在哪里呢?

sw_rotate实际上是lv_disp_drv_t结构体中的一个field, 此结构体的定义在…/lv_hal/lv_hal_disp.h文件中,

节选如下:

/*** Display Driver structure to be registered by HAL*/typedef struct _disp_drv_t {lv_coord_t hor_res; /**< Horizontal resolution. */lv_coord_t ver_res; /**< Vertical resolution. *//** Pointer to a buffer initialized with `lv_disp_buf_init()`.* LVGL will use this buffer(s) to draw the screens contents */lv_disp_buf_t * buffer;#if LV_ANTIALIASuint32_t antialiasing : 1; /**< 1: antialiasing is enabled on this display. */#endifuint32_t rotated : 2;uint32_t sw_rotate : 1; /**< 1: use software rotation (slower) */..........} lv_disp_drv_t;

细心的读者可能会发现lv_disp_drv_t这个名字, 本文前面出现过,

在前面讲述官方文档"第三章 Get started中的 3.1.2 Add LVGL into your project" 一节中,

Implement and register a function which can copy the rendered image to an area of your display:

这一步的实例代码里:

static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/lv_disp_drv_init(&disp_drv); /*Basic initialization*/disp_drv.flush_cb = my_disp_flush; /*Set your driver function*/disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/disp_drv.hor_res = MY_DISP_HOR_RES; /*Set the horizontal resolution of the display*/disp_drv.ver_res = MY_DISP_VER_RES; /*Set the vertical resolution of the display*/lv_disp_drv_register(&disp_drv); /*Finally register the driver*/

已经用到过lv_disp_drv_t, 定义了一个lv_disp_drv_t变量, 并初始化了其中的一些field值.

所以我们要想在初始化设定sw_rotate的值, 也要在这里.

上面 1.1节已经提到, 开发板的相关初始化代码在lvgl_init()函数里, 我们在此函数里修改即可, 代码如下:

.../*Create a display*/lv_disp_drv_t disp_drv;lv_disp_drv_init(&disp_drv);disp_drv.buffer = &disp_buf;disp_drv.flush_cb = lvgl_flush_cb;disp_drv.sw_rotate = 1;   // add for rotationdisp_drv.rotated = LV_DISP_ROT_90;   // add for rotationlv_disp_drv_register(&disp_drv);...

如上, 增加2行, 就成功将屏幕旋转了90度.

2.1.2 运行时 旋转屏幕

根据官方文档的这一句话:

Display rotation can also be changed at runtime using the lv_disp_set_rotation(disp, rot) API.

我们实现了代码如下:

static void btn_rotate_cb(lv_obj_t *obj, lv_event_t event){if (LV_EVENT_CLICKED == event) {lv_disp_set_rotation(NULL, LV_DISP_ROT_NONE);}}

我在屏幕上添加了一个按钮, 上面函数是此按钮的事件响应函数, 此按钮一旦被点击, 就将屏幕的旋转归0.

至此, 关于LVGL的屏幕旋转功能摸得差不多了, 对LVGL的学习又更进了一步.

标签: #linux系统图形化界面切换