LVGL简介
相比较于老式的嵌入式UI界面设计方案,lvgl的优势在于:
LVGL官网链接:
https://lvgl.io/
LVGL源码
https://github.com/lvgl/lvgl
LVGL特性


/*** @file lv_conf.h* Configuration file for v8.3.0-dev*//** Copy this file as `lv_conf.h`* 1. simply next to the `lvgl` folder* 2. or any other places and* - define `LV_CONF_INCLUDE_SIMPLE`* - add the path as include path*//* clang-format off */#if 0 /*Set it to "1" to enable content*/
从文件注释看到,这里明确的告诉大家,首先需要将该文件改名(或者Copy)为lv_conf.h的文件;其次在宏定义中,将#if 0 改为 #if 1,即使能这个文件下面的内容,用于编译,因为在lvgl的核心代码中使用#include “lv_conf.h”,所以lv_conf_template.h文件改名为v_conf.h是必须的。#define ST7735S_WIDTH 128
#define ST7735S_HEIGHT 128
在lv_conf.h中,修改lvgl有关屏幕尺寸的定义:#define MY_DISP_HOR_RES 128
#define MY_DISP_VER_RES 128
#define LV_VER_RES_MAX 128
在lv_port_disp.c文件中,开始移植屏幕驱动:/*** @file lv_port_disp_templ.c**//*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content */#if 1
源文件lv_port_disp.c源码如下:void lv_port_disp_init(void)
{/*-------------------------* Initialize your display* -----------------------*/disp_init();/*-----------------------------* Create a buffer for drawing*----------------------------*//*** LVGL requires a buffer where it internally draws the widgets.* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.* The buffer has to be greater than 1 display row** There are 3 buffering configurations:* 1. Create ONE buffer:* LVGL will draw the display's content here and writes it to your display** 2. Create TWO buffer:* LVGL will draw the display's content to a buffer and writes it your display.* You should use DMA to write the buffer's content to the display.* It will enable LVGL to draw the next part of the screen to the other buffer while* the data is being sent form the first buffer. It makes rendering and flushing parallel.** 3. Double buffering* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.* This way LVGL will always provide the whole rendered screen in `flush_cb`* and you only need to change the frame buffer's address.*//* 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*//*-----------------------------------* Register the display in LVGL*----------------------------------*/static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/lv_disp_drv_init(&disp_drv); /*Basic initialization*//*Set up the functions to access to your display*//*Set the resolution of the display*/disp_drv.hor_res = ST7735S_WIDTH;disp_drv.ver_res = ST7735S_HEIGHT;/*Used to copy the buffer's content to the display*/disp_drv.flush_cb = disp_flush;/*Set a display buffer*/disp_drv.draw_buf = &draw_buf_dsc_1;/*Required for Example 3)*///disp_drv.full_refresh = 1/* Fill a memory array with a color if you have GPU.* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.* But if you have a different GPU you can use with this callback.*///disp_drv.gpu_fill_cb = gpu_fill;/*Finally register the driver*/lv_disp_drv_register(&disp_drv);
}
disp_drv.hor_res = ST7735S_WIDTH;
disp_drv.ver_res = ST7735S_HEIGHT;
disp_drv.flush_cb = disp_flush;
这个接口需要我们自己根据触摸驱动芯片和LCD来实现的接口,它将像素数据通过触摸驱动芯片刷新指定的屏幕,具体的接口如下:extern void LCD_DrawPoint(uint16_t x,uint16_t y,uint16_t color);/*Flush the content of the internal buffer the specific area on the display*You can use DMA or any hardware acceleration to do this operation in the background but*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/int32_t x;int32_t y;for(y = area->y1; y <= area->y2; y++) {for(x = area->x1; x <= area->x2; x++) {/*Put a pixel to the display. For example:*//*put_px(x, y, *color_p)*/// 画点函数,例如常见的一些屏用的是16位颜色,你把16位数据输出到屏幕即可LCD_DrawPoint(x, y, color_p->full);color_p++;}}// LCD_Fill(area->x1, area->y1, area->x2, area->y2, *((uint16_t *)color_p));/*IMPORTANT!!!*Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);
}
从该接口可看到,disp_flush函数参数传递的是:待绘制的区域的xy坐标和以及要绘制的颜色;开发者在此函数实现lvgl于外接显示器的对接。#if 1 /*Set it to "1" to enable content*/#ifndef LV_CONF_H
#define LV_CONF_H#include
// 屏幕尺寸需要配置
#define MY_DISP_HOR_RES 128
#define MY_DISP_VER_RES 128
#define LV_VER_RES_MAX 128
/*====================COLOR SETTINGS 颜色相关的配置*====================*/
// 颜色的配置,这里我们使用 RGB565。所以配置为 16
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0/*Enable more complex drawing routines to manage screens transparency.*Can be used if the UI is above another layer, e.g. an OSD menu or video player.*Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
#define LV_COLOR_SCREEN_TRANSP 0/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
#if 1 /*Set it to "1" to enable content*//*=========================MEMORY SETTINGS 内存相关的配置,如果没有定义 LV_MEM_CUSTOM,那么代表动态内存分配释放,使用 LVGL 那套内存管理算法默认情况下 LVGL 使用 TLSF 内存管理算法,LV_MEM_SIZE 指定了 LVGL 管理的内存大小;这个需要配置*=========================*//*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/// 此内存用于 LVGL 的动态申请/释放内存,这里我们配置的 48KB 的全局数组给 LVGL 使用#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*//*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/#define LV_MEM_ADR 0 /*0: unused*//*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/#if LV_MEM_ADR == 0//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*///#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/#endif#else /*LV_MEM_CUSTOM*/#define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/#define LV_MEM_CUSTOM_ALLOC malloc#define LV_MEM_CUSTOM_FREE free#define LV_MEM_CUSTOM_REALLOC realloc
#endif /*LV_MEM_CUSTOM*//*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.*You will see an error log message if there wasn't enough buffers. */// 用于渲染和 LVGL 内部处理机制的 buffer 个数,如果配置不够的话,LVGL 会打印 ERROR 信息// 这个其实是一个 lv_mem_buf_arr_t[LV_MEM_BUF_MAX_NUM] 结构的数组个数;
#define LV_MEM_BUF_MAX_NUM 16/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*====================HAL SETTINGS HAL 层的配置*====================*//*Default display refresh period. LVG will redraw changed areas with this period time*/
// 这个参数决定了多久处理一起屏幕刷新,默认情况是 30ms
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*//*Input device read period in milliseconds*/
// 这个参数决定了多久处理一起input,默认情况是 30ms
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*//*Use a custom tick source that tells the elapsed time in milliseconds.*It removes the need to manually update the tick with `lv_tick_inc()`)*/
#define LV_TICK_CUSTOM 0
#if LV_TICK_CUSTOM#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*//*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.*(Not so important, you can adjust it to modify default sizes and spaces)*//* * LV_DPI_DEF 注意这里,虽然LVGL的作者说这个没这么重要,但他会严重影响到LVGL的动画效果* 你应该进行DPI的手动计算,例如128x128分辨率1.44英寸的屏幕,那么 DPI = ((√128*128) / 1.44) ≈ 89*/
#define LV_DPI_DEF 89 /*[px/inch]*/
/*-------------* Others*-----------*//*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 1
#if LV_USE_PERF_MONITOR#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif/*1: Show the used memory and the memory fragmentation* Requires LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR 0
#if LV_USE_MEM_MONITOR#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif/*1: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM#define LV_SPRINTF_INCLUDE #define lv_snprintf snprintf#define lv_vsnprintf vsnprintf
#else /*LV_SPRINTF_CUSTOM*/#define LV_SPRINTF_USE_FLOAT 0
#endif /*LV_SPRINTF_CUSTOM*/#define LV_USE_USER_DATA 1/*Garbage Collector settings*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
/*=====================* COMPILER SETTINGS*====================*//*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).* E.g. __attribute__((aligned(4)))*/
#define LV_ATTRIBUTE_MEM_ALIGN/*Attribute to mark large constant arrays for example font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST/*Compiler prefix for a big array declaration in RAM*/
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY/*Place performance critical functions into a faster memory (e.g RAM)*/
#define LV_ATTRIBUTE_FAST_MEM/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA/*Export integer constant to binding. This macro is used with constants in the form of LV_ that*should also appear on LVGL binding API such as Micropython.*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*//*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
/*==================* WIDGET USAGE*================*//*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/#define LV_USE_ARC 1#define LV_USE_ANIMIMG 1#define LV_USE_BAR 1#define LV_USE_BTN 1#define LV_USE_BTNMATRIX 1#define LV_USE_CANVAS 1#define LV_USE_CHECKBOX 1#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/#define LV_USE_IMG 1 /*Requires: lv_label*/#define LV_USE_LABEL 1
#if LV_USE_LABEL#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
#endif#define LV_USE_LINE 1#define LV_USE_ROLLER 1 /*Requires: lv_label*/
#if LV_USE_ROLLER#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif#define LV_USE_SLIDER 1 /*Requires: lv_bar*/#define LV_USE_SWITCH 1#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
#if LV_USE_TEXTAREA != 0#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#endif#define LV_USE_TABLE 1/*==================* EXTRA COMPONENTS*==================*//*-----------* Widgets*----------*/
#define LV_USE_CALENDAR 1
#if LV_USE_CALENDAR#define LV_CALENDAR_WEEK_STARTS_MONDAY 0#if LV_CALENDAR_WEEK_STARTS_MONDAY#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}#else#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}#endif#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}#define LV_USE_CALENDAR_HEADER_ARROW 1#define LV_USE_CALENDAR_HEADER_DROPDOWN 1
#endif /*LV_USE_CALENDAR*/#define LV_USE_CHART 1#define LV_USE_COLORWHEEL 1#define LV_USE_IMGBTN 1#define LV_USE_KEYBOARD 1#define LV_USE_LED 1#define LV_USE_LIST 1#define LV_USE_MENU 1#define LV_USE_METER 1#define LV_USE_MSGBOX 1#define LV_USE_SPINBOX 1#define LV_USE_SPINNER 1#define LV_USE_TABVIEW 1#define LV_USE_TILEVIEW 1#define LV_USE_WIN 1#define LV_USE_SPAN 1
#if LV_USE_SPAN/*A line text can contain maximum num of span descriptor */#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/*===================* DEMO USAGE====================*//*Show some widget. It might be required to increase `LV_MEM_SIZE` */#define LV_USE_DEMO_WIDGETS 0#if LV_USE_DEMO_WIDGETS#define LV_DEMO_WIDGETS_SLIDESHOW 0#endif/*Demonstrate the usage of encoder and keyboard*/#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0/*Benchmark your system*/#define LV_USE_DEMO_BENCHMARK 1/*Stress test for LVGL*/#define LV_USE_DEMO_STRESS 0/*Music player demo*/#define LV_USE_DEMO_MUSIC 0#if LV_USE_DEMO_MUSIC# define LV_DEMO_MUSIC_SQUARE 0# define LV_DEMO_MUSIC_LANDSCAPE 0# define LV_DEMO_MUSIC_ROUND 0# define LV_DEMO_MUSIC_LARGE 0# define LV_DEMO_MUSIC_AUTO_PLAY 0#endif

/*** @brief This function handles SysTick Handler.* @param None* @retval None*/
void SysTick_Handler(void)
{lv_tick_inc(1);
}
while(1)
{lv_timer_handler();LVGL_Delay();
}
void LVGL_CentralButton(void)
{lv_obj_t *btn = lv_btn_create(lv_scr_act());lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);lv_obj_set_height(btn, 30);lv_obj_t *label;label = lv_label_create(btn);lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);lv_label_set_text(label, "LVGL");static lv_style_t style_btn;lv_style_init(&style_btn);lv_style_set_radius(&style_btn, 10);lv_style_set_border_color(&style_btn, lv_color_white());lv_style_set_border_opa(&style_btn, LV_OPA_30);lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT);
}void LVGL_Init(void)
{lv_init(); // lvgl初始化,如果这个没有初始化,那么下面的初始化会崩溃lv_port_disp_init(); // 显示器初始化// lv_port_indev_init(); // 输入设备初始化(如果没有实现就注释掉)// lv_port_fs_init(); // 文件系统设备初始化(如果没有实现就注释掉)
}int main(void)
{LogInit()BspLedInit();LCD_ST7735S_Init();LVGL_Init();LVGL_CentralButton();SystemTickInit();while(1){lv_timer_handler();LVGL_Delay();}
}
Stack_Size EQU 0x000040000AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp Heap_Size EQU 0x00002000AREA HEAP, NOINIT,READWRITE,ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit PRESERVE8THUMB
--diag_suppress=188 --diag_suppress=111 --diag_suppress=550
即可屏蔽一些错误和警告。通过本篇文章的总结,初步对lvgl有了全新的认识。希望对其他开发者有用,谢谢!