LVGL 移植教程
在本教程中,我们将详细介绍如何利用 LVGL 图形库实现简易控件,以及移植应用程序到 Luckfox Pico Ultra W 开发板来展示运行效果
1.源码结构简介
LVGL( LittlevGL )是一款轻量级、开源的嵌入式图形库,专注于提供灵活易用的图形用户界面( GUI )解决方案。通过 LVGL ,开发者可以实现丰富的图形界面设计,包括按钮、滑块、文本框等各种常用的 GUI 控件,以及动画和触摸交互等功能。接下来,我们将分析其源码目录中的核心文件和目录,使得在移植过程中更加清晰其运行流程。
-
lvgl源码结构
文件/目录名称 描述 CMakeLists.txtCMake 构建系统的配置文件,用于定义如何编译和链接 LVGL 库。它包含了源文件、库依赖和构建选项的设置。 src存放 LVGL 的源代码,包括库的核心实现和功能模块。这里包含了 LVGL 的主要逻辑和 API。 examples示例代码,用于演示如何使用 LVGL 创建图形用户界面。可以帮助开发者了解 LVGL 的用法和功能。 demos提供实际的演示程序,展示 LVGL 在不同场景和设备上的应用。这些演示通常包含可视化效果和交互功能,帮助用户理解库的能力。 tests包含单元测试和集成测试代码,用于验证 LVGL 的功能和稳定性。确保在不同版本和平台上的一致性。 lv_conf_template.h配置模板文件,用于用户自定义 LVGL 的参数和设置。用户可以复制并重命名此文件以适应他们的项目需求。 lvgl.hLVGL 的主头文件,包含了库的主要接口和数据结构的定义。用户在使用 LVGL 时通常会包括此文件。 
-
lv_drivers 源码结构
lv_drivers 是 LVGL 项目中的一个子模块,它包含了用于与各种硬件设备通信的驱动程序。这些驱动程序使得 LVGL 可以适配不同的显示和输入设备,如 LCD 显示器、触摸屏、按键等。
目录名称 描述 display包含多种显示屏驱动的实现文件,每个文件通常对应一种特定的显示器型号。 indev包含输入设备驱动的实现文件,这些文件处理各种触摸屏和输入设备的输入。 gtkdrv包含针对 GTK 图形界面的驱动实现,可能用于在 GTK 应用中使用 LVGL。包含的文件如 gtkdrv.c和gtkdrv.h提供了与 GTK 交互的功能。sdl包含与 SDL 相关的驱动,主要用于在使用 SDL 的应用程序中实现 LVGL。文件如 sdl.c提供了与 SDL 相关的绘图和事件处理功能。wayland包含与 Wayland 显示服务器协议相关的驱动。包含的文件(如 wayland.c)提供了与 Wayland 的集成。win32drv包含 Windows 平台相关的驱动实现,可能用于在 Windows 环境中运行 LVGL。相关的源文件和头文件提供了在 Windows 上运行 LVGL 所需的功能。 
-
官方资源链接
资源名称 仓库地址 描述 lvglhttps://github.com/lvgl/lvgl.git LVGL图形界面控件的源码 lv_drivershttps://github.com/lvgl/lv_drivers.git 驱动LVGL图形界面的驱动接口源代码 lv_port_linux_frame_bufferhttps://github.com/lvgl/lv_port_linux_frame_buffer.git 适配有Framebuffer的linux系统的示例项目
2.LVGL项目搭建
-
创建目录,存放官方源码:
luckfox@luckfox:~$ mkdir lvglluckfox@luckfox:~$ cd lvgl -
获取 lvgl 源码:
git clone -b release/v8.3 https://github.com/lvgl/lvgl.gitgit clone -b release/v8.3 https://github.com/lvgl/lv_drivers.gitgit clone --branch release/v8.2 --single-branch https://github.com/lvgl/lv_port_linux_frame_buffer.git -
创建待移植的工程目录:
luckfox@luckfox:~/lvgl$ mkdir -p lvgl_projectluckfox@luckfox:~/lvgl$ cd lvgl_project -
复制
lvgl核心库,lvgl是LVGL的核心库,包含了所有绘图、组件、和核心逻辑。cp -r ~/lvgl/lvgl ./ -
复制
lv_drivers驱动库,lv_drivers提供了硬件接口驱动,例如 Frame Buffer(帧缓冲区)、输入设备(鼠标、触摸屏)等。cp -r ~/lvgl/lv_drivers ./ -
复制
lv_conf_template.h核心配置文件,它提供了设置库的基本行为、禁用未使用的模块、在编译时调整内存缓冲区的大小等功能。重命名为lv_conf.h便于编译时引用。cp ~/lvgl/lvgl/lv_conf_template.h ./lv_conf.h -
复制
lv_drv_conf_template.h驱动配置文件,它提供了读取驱动的配置选项,里面包含了各种驱动相关的设置,如:触摸屏、显示器等驱动。重命名为lv_drv_conf.h便于编译时引用。cp ~/lvgl/lv_drivers/lv_drv_conf_template.h ./lv_drv_conf.h -
复制
main.c文件, 它提供了 LVGL 库的基本状态初始化, Frame Buffer设备初始化,设置Tick接口,设置显示接口,设置输入设备接口。cp ~/lvgl/lv_port_linux_frame_buffer/main.c ./ -
复制
mouse_cursor_icon.c文件,mouse_cursor_icon.c是鼠标光标图片转换后的 C 文件,方便显示鼠标图标。cp ~/lvgl/lv_port_linux_frame_buffer/mouse_cursor_icon.c ./ -
复制
Makefile文件,LVGL官方提供的Makefile包含了必要的设置和依赖项,确保了正确的构建配置。cp ~/lvgl/lv_port_linux_frame_buffer/Makefile ./ -
至此,所需的工程文件已复制完成,
lvgl_project工程目录下的文件如下:luckfox@luckfox:~/lvgl/lvgl_project$ ls -ltotal 88-rw-rw-r-- 1 luckfox luckfox 26765 1月 3 17:04 lv_conf.hdrwxrwxr-x 11 luckfox luckfox 4096 1月 3 20:52 lv_drivers-rw-rw-r-- 1 luckfox luckfox 15131 1月 2 19:00 lv_drv_conf.hdrwxrwxr-x 11 luckfox luckfox 4096 1月 2 17:21 lvgl-rw-rw-r-- 1 luckfox luckfox 2637 1月 3 17:31 main.c-rw-rw-r-- 1 luckfox luckfox 2155 1月 3 17:52 Makefile-rw-rw-r-- 1 luckfox luckfox 21242 1月 2 17:42 mouse_cursor_icon.c -
修改
lv_conf.h配置,将一开始的#if 0改为#if 1整个文件的配置内容才会生效,将LV_TICK_CUSTOM置1,使用户自定义的时钟源使能,LVGL 需要系统不断的提供一个毫秒级( ms )的 Tick 维持 LVGL 的心跳。/* clang-format on */#if 1 /*Set to '1' to launch content*/...#define LV_TICK_CUSTOM 1#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*/#define LV_TICK_CUSTOM_INCLUDE <stdint.h> /*Header for the system time function*/#define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time*/#endif /*LV_TICK_CUSTOM*/ -
修改
lv_drv_conf.h配置,将一开始的#if 0改为#if 1整个文件的配置内容才会生效,将USE_FBDEV置1,使程序支持Framebuffer设备。/* clang-format on */#if 1 /*Set to '1' to launch content*/.../*-----------------------------------------* Linux frame buffer device (/dev/fbx)*-----------------------------------------*/#ifndef USE_FBDEV# define USE_FBDEV 1#endif#if USE_FBDEV# define FBDEV_PATH "/dev/fb0"#endif -
注释以下
main.c代码,未使用官方 demo 库。// #include "lv_demos/lv_demo.h"···/*Create a Demo*/// lv_demo_widgets(); -
修改
makefile方便管理编译过程文件。CC = gccLVGL_DIR_NAME ?= lvglLVGL_DIR ?= .WARNINGS := -Wall -Wshadow -Wundef -Wmissing-prototypes \-Wno-discarded-qualifiers -Wextra -Wno-unused-function \-Wno-error=strict-prototypes -Wpointer-arith \-fno-strict-aliasing -Wno-error=cpp -Wuninitialized \-Wmaybe-uninitialized -Wno-unused-parameter \-Wno-missing-field-initializers -Wtype-limits \-Wsizeof-pointer-memaccess -Wno-format-nonliteral \-Wno-cast-qual -Wunreachable-code -Wno-switch-default \-Wreturn-type -Wmultichar -Wformat-security \-Wno-ignored-qualifiers -Wno-sign-compare \-Wno-error=missing-prototypes -Wdouble-promotion \-Wclobbered -Wdeprecated -Wempty-body -Wshift-negative-value \-Wstack-usage=2048 -Wno-unused-value \-std=c99CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ $(WARNINGS)LDFLAGS ?= -lmBIN = demoBUILD_DIR = ./buildBUILD_OBJ_DIR = $(BUILD_DIR)/objBUILD_BIN_DIR = $(BUILD_DIR)/binprefix ?= /usrbindir ?= $(prefix)/bin#Collect the files to compileMAINSRC = $(wildcard ./*.c)include $(LVGL_DIR)/lvgl/lvgl.mkinclude $(LVGL_DIR)/lv_drivers/lv_drivers.mk# CSRCS +=$(LVGL_DIR)/mouse_cursor_icon.cOBJEXT ?= .oAOBJS = $(ASRCS:.S=$(OBJEXT))COBJS = $(CSRCS:.c=$(OBJEXT))MAINOBJ = $(MAINSRC:.c=$(OBJEXT))SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)SRC_DIR = $(LVGL_DIR)/source/multexCSRCS += $(wildcard $(SRC_DIR)/*.c)CSRCS += $(wildcard $(SRC_DIR)/components/*.c)CSRCS += $(wildcard $(SRC_DIR)/fonts/*.c)CSRCS += $(wildcard $(SRC_DIR)/images/*.c)CSRCS += $(wildcard $(SRC_DIR)/screens/*.c)CSRCS +=$(LVGL_DIR)/source/button/button.cCSRCS +=$(LVGL_DIR)/source/slider/slider.cCSRCS +=$(LVGL_DIR)/source/switch/switch.cOBJS = $(AOBJS) $(COBJS) $(MAINOBJ)TARGET = $(addprefix $(BUILD_OBJ_DIR)/, $(patsubst ./%, %, $(OBJS)))## MAINOBJ -> OBJFILESall: default$(BUILD_OBJ_DIR)/%.o: %.c@mkdir -p $(dir $@)@$(CC) $(CFLAGS) -c $< -o $@@echo "CC $<"default: $(TARGET)@mkdir -p $(dir $(BUILD_BIN_DIR)/)$(CC) -o $(BUILD_BIN_DIR)/$(BIN) $(TARGET) $(LDFLAGS)clean:rm -rf $(BUILD_DIR)install:install -d $(DESTDIR)$(bindir)install $(BUILD_BIN_DIR)/$(BIN) $(DESTDIR)$(bindir)uninstall:$(RM) -r $(addprefix $(DESTDIR)$(bindir)/,$(BIN))OBJ_DIR := $(BUILD_DIR)/objBIN_DIR := $(BUILD_DIR)/bin# Ensure build directories exist$(OBJ_DIR) $(BIN_DIR):mkdir -p $@all: default%.o: %.c@$(CC) $(CFLAGS) -c $< -o $(OBJ_DIR)/$@@echo "CC $<"default: $(AOBJS) $(COBJS) $(MAINOBJ)$(CC) -o $(BIN_DIR)/$(BIN) $(OBJ_DIR)/$(MAINOBJ) $(addprefix $(OBJ_DIR)/,$(AOBJS)) $(addprefix $(OBJ_DIR)/,$(COBJS)) $(LDFLAGS)clean:rm -f $(BUILD_DIR) -
执行
make指令。make -
检查生成文件。
tree build/ -L 2- 生成文件如下即执行成功:

- 生成文件如下即执行成功:
3.示例程序
示例程序:lvgl_project.tar.gz
3.1 按键例程
-
创建并移动到例程文件存储目录。
mkdir -p source/buttoncd source/button -
编辑 button.c 文件。
vi button.c复制粘贴以下内容,代码创建了一个带有标签的按钮居中于屏幕,并对点击事件做出计数+1响应:
#include "button.h"#include "../../lvgl/lvgl.h"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(void){/*Add a button the current screen*/lv_obj_t * btn = lv_btn_create(lv_scr_act());/* Align the button to the center of the screen */lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);/*Set its size*/lv_obj_set_size(btn, 120, 50);/*Assign a callback to the button*/lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);/*Add a label to the button*/lv_obj_t * label = lv_label_create(btn);/*Set the labels text*/lv_label_set_text(label, "Button");lv_obj_center(label);}void button_demo_init(){lv_obj_clean(lv_scr_act());lv_example_get_started();} -
编辑 button.h 文件。
vi button.h复制粘贴以下内容,在头文件声明函数方便外部调用:
#ifndef __BUTTON_H__#define __BUTTON_H__void button_demo_init();#endif // __BASEDEMO_H__ -
修改
main.c调用button_demo_init函数,以及设置disp_drv.hor_res水平分辨率和disp_drv.ver_res垂直分辨率适配屏幕。#include "source/button/button.h"#define DISP_BUF_SIZE (480 * 480 * 2) //Buffer size for LVGL's screen drawing. A larger buffer reduces update frequency to the display and increases memory usage.int main(void){.../*According to the actual screen resolution used, the following is the setting for a screen resolution of 480x480 specification*/disp_drv.hor_res = 480; //Set horizontal resolutiondisp_drv.ver_res = 480; //Set vertical resolution.../*Create a Demo*/// lv_demo_widgets();button_demo_init();...}
3.2 滑动条例程
-
创建并移动到例程文件存储目录。
mkdir -p source/slidercd source/slider -
编辑 slider.c 文件。
vi slider.c复制粘贴以下内容,代码创建了一个滑块控制背光并将其值写在标签上:
#include "slider.h"#include <dirent.h>#include <unistd.h>#include <fcntl.h>#include "../../lvgl.h"static lv_obj_t * label;int set_backlight(unsigned int duty){char path[120] = {0};char buff[10];int value;int fd;int ret = -1;memset(path, 0x0, sizeof(path));sprintf(path, "/sys/class/backlight/backlight/max_brightness");fd = open(path, O_RDONLY);if (fd < 0) return -2;memset(buff, 0x0, sizeof(buff));ret = read(fd, buff, sizeof(buff));close(fd);value = atoi(buff);if (ret < 0){return -1;}value = value * ((double)duty / 100.0);memset(path, 0x0, sizeof(path));sprintf(path, "/sys/class/backlight/backlight/brightness");fd = open(path, O_WRONLY | O_CREAT, 0777);if (fd < 0) return -2;memset(buff, 0x0, sizeof(buff));int len = sprintf(buff, "%d", value);ret = write(fd, buff, len);close(fd);return ret;}int get_backlight(){char path[120] = {0};char buff[10];int value;int max;int fd;int ret = -1;memset(path, 0x0, sizeof(path));sprintf(path, "/sys/class/backlight/backlight/max_brightness");fd = open(path, O_RDONLY);if (fd < 0) return -2;memset(buff, 0x0, sizeof(buff));ret = read(fd, buff, sizeof(buff));close(fd);max = atoi(buff);printf("max:%d\n",max);if (ret < 0){return -1;}memset(path, 0x0, sizeof(path));sprintf(path, "/sys/class/backlight/backlight/brightness");fd = open(path, O_RDONLY);if (fd < 0) return -2;memset(buff, 0x0, sizeof(buff));ret = read(fd, buff, sizeof(buff));if (ret < 0){return -1;}value = atoi(buff);printf("value:%d\n",value);ret = value * (100.0 / (double)max);close(fd);return ret;}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, "%"LV_PRId32, lv_slider_get_value(slider));/*Align top of the slider*/lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15);int ret = set_backlight(lv_slider_get_value(slider));if (ret < 0){perror("set_backlight");}}/*** Create a slider and write its value on a label.*/void lv_example_slider(void){char bl_label[3];int backlight_value = get_backlight();if(backlight_value < 0){perror("get_backlight");}printf("backlight_value:%d\n",backlight_value);sprintf(bl_label,"%d",backlight_value);/*Create a slider in the center of the display*/lv_obj_t * slider = lv_slider_create(lv_scr_act());/*Set the width*/lv_obj_set_width(slider, 200);/*Align to the center of the parent (screen)*/lv_obj_center(slider);/*Assign an event function*/lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);lv_slider_set_value(slider, backlight_value, LV_ANIM_OFF);/*Create a label above the slider*/label = lv_label_create(lv_scr_act());lv_label_set_text(label, bl_label);/*Align top of the slider*/lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_MID, 0, -15);}void slider_demo_init(){lv_obj_clean(lv_scr_act());lv_example_slider();} -
编辑 slider.h 文件。
vi slider.h复制粘贴以下内容,在头文件声明函数方便外部调用:
#ifndef __SLIDER_DEMO_H__#define __SLIDER_DEMO_H__void slider_demo_init();int set_backlight(unsigned int duty);int get_backlight();#endif // __BASEDEMO_H__ -
修改 main.c 调用
slider_demo_init函数,以及修改disp_drv.hor_res水平分辨率和disp_drv.ver_res垂直分辨率适配屏幕。#include "source/slider/slider.h"#define DISP_BUF_SIZE (480 * 480 * 2) //Buffer size for LVGL's screen drawing. A larger buffer reduces update frequency to the display and increases memory usage.int main(void){.../*According to the actual screen resolution used, the following is the setting for a screen resolution of 480x480 specification*/disp_drv.hor_res = 480; //Set horizontal resolutiondisp_drv.ver_res = 480; //Set vertical resolution.../*Create a Demo*/// lv_demo_widgets();slider_demo_init();...}
3.3 开关例程
-
创建并移动到例程文件存储目录。
mkdir -p source/switchcd source/switch -
编辑 switch.c 文件。
vi switch.c复制粘贴以下内容,代码创建了一个开关控件用于控制GPIO_B1_d:
#include "switch.h"#include <fcntl.h>#include <stdio.h>#include "../../lvgl.h"int gpio_export(int gpio_pin){FILE *export_file = fopen("/sys/class/gpio/export", "w");if (export_file == NULL) {perror("Failed to open GPIO export file");return -1;}fprintf(export_file, "%d", gpio_pin);fclose(export_file);return 0;}int gpio_out_direction(int gpio_pin){char direction_path[50];snprintf(direction_path, sizeof(direction_path), "/sys/class/gpio/gpio%d/direction", gpio_pin);FILE *direction_file = fopen(direction_path, "w");if (direction_file == NULL) {perror("Failed to open GPIO direction file");return -1;}fprintf(direction_file, "out");fclose(direction_file);return 0;}int set_gpio(int gpio_pin, int val){int len;char buff[10];char filename[64];int ret,result;memset(filename, 0x0, sizeof(filename));sprintf(filename, "/sys/class/gpio/gpio%d/value", gpio_pin);FILE *value_file = fopen(filename, "w");if (value_file == NULL){ret = gpio_export(gpio_pin);if (ret < 0){return ret;}else{result = gpio_out_direction(gpio_pin);if(result < 0)return result;FILE *value_file = fopen(filename, "w");if (value_file == NULL)return ret;}}memset(buff, 0x0, sizeof(buff));len = sprintf(buff, "%s", val ? "1" : "0");fprintf(value_file,buff);if (ret != len){fclose(value_file);return ret;}fclose(value_file);return 0;}static void anim_x_cb(void * var, int32_t v){lv_obj_set_x(var, v);}static void sw_event_cb(lv_event_t * e){lv_obj_t * sw = lv_event_get_target(e);lv_obj_t * label = lv_obj_get_user_data(sw);if(lv_obj_has_state(sw, LV_STATE_CHECKED)) {int ret = set_gpio(41,1);if(ret == 0){lv_label_set_text(label, "GPIO1_B1_d set High level");lv_obj_align_to(label,sw,LV_ALIGN_CENTER,0,-30);}else{lv_label_set_text(label, "GPIO1_B1_d set fail");lv_obj_align_to(label,sw,LV_ALIGN_CENTER,0,-30);}}else {int ret = set_gpio(41,0);if(ret == 0){lv_label_set_text(label, "GPIO1_B1_d set Low level");lv_obj_align_to(label,sw,LV_ALIGN_CENTER,0,-30);}else{lv_label_set_text(label, "GPIO1_B1_d set fail");lv_obj_align_to(label,sw,LV_ALIGN_CENTER,0,-30);}}}/*** Created a switch control to control GPIO-B1_d*/void lv_demos_switch(void){lv_obj_t * sw = lv_switch_create(lv_scr_act());lv_obj_center(sw);lv_obj_add_state(sw, LV_STATE_CHECKED);lv_obj_t * label = lv_label_create(lv_scr_act());lv_label_set_text(label, "GPIO Control");lv_obj_align_to(label,sw,LV_ALIGN_CENTER,0,-30);lv_obj_set_user_data(sw, label);lv_obj_add_event_cb(sw, sw_event_cb, LV_EVENT_VALUE_CHANGED, NULL);}void switch_demo_init(){lv_obj_clean(lv_scr_act());lv_demos_switch();} -
编辑 switch.h 文件。
vi switch.h复制粘贴以下内容,在头文件声明函数方便外部调用:
#ifndef __SLIDER_DEMO_H__#define __SLIDER_DEMO_H__void switch_demo_init();int gpio_export(int gpio_pin);int gpio_out_direction(int gpio_pin);int set_gpio(int gpio_pin, int val);#endif // __BASEDEMO_H__ -
修改
main.c调用switch_demo_init函数,以及设置disp_drv.hor_res水平分辨率和disp_drv.ver_res垂直分辨率适配屏幕。#include "source/switch/switch.h"#define DISP_BUF_SIZE (480 * 480 * 2) //Buffer size for LVGL's screen drawing. A larger buffer reduces update frequency to the display and increases memory usage.int main(void){.../*According to the actual screen resolution used, the following is the setting for a screen resolution of 480x480 specification*/disp_drv.hor_res = 480; //Set horizontal resolutiondisp_drv.ver_res = 480; //Set vertical resolution.../*Create a Demo*/// lv_demo_widgets();switch_demo_init();...}
3.4 SquareLine Studio创建综合例程组件
-
打开
SquareLine Studio 1.5.0,在弹出的界面中选择Create,选择主要的LVGL版本为8.3,修改屏幕分辨率为480 x 480(请根据自己屏幕的分辨率进行修改),选择具体LVGL版本为8.3.11。
-
点击右下角
CREATE创建工程,这里对模拟器的选择不重要,后续需要移植到对应开发板,而可选模拟器中并没有直接生成一整套可移植例程的功能。
-
界面编辑器初始创建的状态如下,有三个主要部分:面板窗口,可编辑视图,标题菜单。

- 面板窗口的各元素功能介绍
- Animations :创建和管理动画
- Assets :可在项目中使用的图像和字体
- Console :程序日志和错误消息
- Font Maker :创建自定义字体
- Hierarchy :选择并放置添加到屏幕的元素
- History :程序保存您的操作。它最多可以记录 200 个操作
- Inspector :在 Inspector 面板中,您可以查看所选小组件的参数,并可以为其分配事件
- Error :错误面板主要显示缺少参数的错误
- Screens :您可以找到屏幕列表,选择并管理它们
- Widgets :您可以将此处列出的 Widget 放置在屏幕上
- 面板窗口的各元素功能介绍
-
通过面板窗口的
Inspector子面板设置屏幕背景色,选择STYLE SETTINGS样式设置,主样式中的Background,设置了屏幕对象的背景颜色Bg ColorHEX值为21202E,设置背景的渐变颜色Bg Gradient color为2D3B50,设置颜色梯度Bg Gradient为VER水平梯度。
-
在
Assets中添加图像。
-
拖动图像控件到可视化视图中,并在
Inspector子面板选择所添加的图像并使其水平居中。
-
拖动文本框控件
Textarea设置用户名,设置文本框控件为单行模式One line mode,设置字体颜色Text Color为FFFFFF,设置字号Text Font为montserrat_24,设置背景角弧形半径Bg Raduis为60,设置背景颜色Bg Color为1A192B。
-
拖动文本控件
Label标识文本框,设置字体颜色以及大小。
-
以此类推,输入密码的文本框以及文本标识也是同样绘制或者复制粘贴,主要差别在于密码文本框设置为
Password mode。
-
拖动按键控件
button,创建登录按键,按键默认不带文本,需要将文本标签嵌入按键。
-
与此类似,创建退出按键,右键控件组,选择
Dupilcate,会复制粘贴一个按键,拖动到合适位置并修改。
-
复制一个新界面,在此基础上修改。

-
清空新界面控件,创建弧形滑块组件,个性化修改样式,增加滑块控制文本事件效果。添加事件,设置事件名为
EventBright,设置事件触发类型为VALUE_CHANGED,设置事件执行动作为SET TEXT VALUE FROM ARC改变文本值。
-
创建GPIO开关控制组件,个性化修改样式,增加滑块控制文本事件效果。添加事件,设置事件名为
EventGPIO,设置事件触发类型为CLICKED,设置事件执行动作为SET TEXT VALUE FROM CHECKED改变文本值,On text时为H表示高电平,Off text时为L表示低电平。
-
创建GPIO开关控制组件,个性化修改样式,增加返回界面效果。添加事件,设置事件名为
EventBack,设置事件触发类型为CLICKED,设置事件执行动作为CHANGE SCREEN切换屏幕,切换到的屏幕设置为ScreenLogin前一个屏幕,此屏幕名是自定义的,需根据实际情况设置。
- 注意:对于前界面的登录与退出按键事件添加逻辑也是类似,可以自己通过面板窗口添加,也可以在代码中自行调用。
-
至此综合例程组件初步设计完成,导出
UI文件。
-
ui文件结构介绍:
| 目录 | 文件名 | 说明 |
|---|---|---|
| components | ui_comp_backlightarc.c | 背光弧形组件实现 |
| ui_comp_backlightarc.h | 背光弧形组件头文件 | |
| ui_comp.c | 组件逻辑实现 | |
| ui_comp.h | 组件头文件 | |
| ui_comp_hook.c | 事件处理钩子实现 | |
| ui_comp_hook.h | 事件处理钩子头文件 | |
| ui_comp_panel1.c | 面板1组件实现 | |
| ui_comp_panel1.h | 面板1组件头文件 | |
| fonts | --- | 存放字体文件 |
| images | ui_img_1072195271.c | 幸狐LOGO图像数据 |
| ui_img_183911018.c | 返回按键图像数据 | |
| screens | ui_ScreenCtrl.c | 控制屏幕实现 |
| ui_ScreenLogin.c | 登录屏幕实现 | |
| ui.c | 主界面初始化及事件处理逻辑 | |
| ui_events.h | 用户界面事件和回调声明 | |
| ui.h | UI相关函数和结构体声明 | |
| ui_helpers.c | 辅助函数实现 | |
| ui_helpers.h | 辅助函数头文件 |
3.5 综合例程
-
回到搭建好的 LVGL 项目文件夹中,创建并移动到例程文件存储目录。
luckfox@luckfox:~/lvgl/lvgl_project$ mkdir -p source/multexluckfox@luckfox:~/lvgl/lvgl_project$ cd source/multex -
将
SquareLine Studio创建的综合例程组件传入multex目录,传输完成后文件列表如下:
-
基础屏幕 UI 显示界面代码实现在
ui_ScreenCtrl.c和ui_ScreenLogin.c中,可以在ui_ScreenLogin.c中添加跳转控制界面事件回调函数和退出登录界面回调函数。#include "../ui.h"void ui_ScreenLogin_screen_init(void){...lv_obj_add_event_cb(ui_ButtonLogin, ui_event_ButtonLogin, LV_EVENT_ALL, NULL);lv_obj_add_event_cb(ui_ButtonExit, ui_event_ButtonExit, LV_EVENT_ALL, NULL);} -
编辑
ui.c文件,ui.c主要存放控件触发事件代码,实现了控制界面跳转事件函数,登录界面退出事件函数,GPIO电平切换事件函数,按键返回上一界面事件函数。#include "../switch/switch.h"void ui_event_ButtonLogin(lv_event_t * e){lv_event_code_t event_code = lv_event_get_code(e);lv_obj_t *account = ui_TextAreaACount;const char *account_txt = lv_textarea_get_text(account);lv_obj_t *passwd = ui_TextAreaPasswd;const char *passwd_txt = lv_textarea_get_text(passwd);if(event_code == LV_EVENT_CLICKED) {if(!strcmp(account_txt,"root")&&!strcmp(passwd_txt,"luckfox")){_ui_screen_change(&ui_ScreenCtrl, LV_SCR_LOAD_ANIM_MOVE_TOP, 500, 0, &ui_ScreenCtrl_screen_init);}}}void ui_event_ButtonExit(lv_event_t * e){lv_event_code_t event_code = lv_event_get_code(e);if(event_code == LV_EVENT_CLICKED) {_ui_screen_delete(&ui_ScreenLogin);lv_obj_clean(ui_ScreenLogin);lv_refr_now(lv_disp_get_default());exit(1);}}void ui_event_SwitchGPIO(lv_event_t * e){lv_obj_t * target = lv_event_get_target(e);if(lv_obj_has_state(target, LV_STATE_CHECKED)) {_ui_checked_set_text_value(ui_LabelLow, target, "H", "L");set_gpio(41,1);lv_obj_set_x(ui_LabelGPIO, -27);lv_obj_set_x(ui_LabelLow, 70);}else {_ui_checked_set_text_value(ui_LabelLow, target, "H", "L");set_gpio(41,0);lv_obj_set_x(ui_LabelGPIO, 25);lv_obj_set_x(ui_LabelLow, -66);}}void ui_event_ButtonClick(lv_event_t * e){lv_event_code_t event_code = lv_event_get_code(e);if(event_code == LV_EVENT_CLICKED) {_ui_screen_change(&ui_ScreenLogin, LV_SCR_LOAD_ANIM_MOVE_BOTTOM, 500, 0, &ui_ScreenLogin_screen_init);}} -
编辑
ui_comp_backlightarc.c文件,ui_comp_backlightarc.c主要存放子组件的行为和属性实现代码,实现了弧形滑块控件控制背光事件函数。void ui_event_comp_BackLightArc_BacklightArc(lv_event_t * e){lv_event_code_t event_code = lv_event_get_code(e);lv_obj_t * target = lv_event_get_target(e);lv_obj_t ** comp_BackLightArc = lv_event_get_user_data(e);if(event_code == LV_EVENT_CLICKED) {_ui_arc_set_text_value(comp_BackLightArc[UI_COMP_BACKLIGHTARC_BACKLIGHTARC_PANELBOX2_PANELBOXDISPLAY_LIGHTVALUE],target, "", "");int ret = set_backlight(lv_arc_get_value(target));if(ret < 0){perror("set_backlight");}}} -
修改lv_conf.h配置,让字体适配更多字号。
#define LV_FONT_MONTSERRAT_18 1#define LV_FONT_MONTSERRAT_20 1#define LV_FONT_MONTSERRAT_24 1#define LV_FONT_MONTSERRAT_36 1#define LV_FONT_MONTSERRAT_40 1 -
修改
main.c调用ui_init函数,以及修改disp_drv.hor_res水平分辨率和disp_drv.ver_res垂直分辨率适配屏幕。#include "source/multex/ui.h"#define DISP_BUF_SIZE (480 * 480 * 2) //Buffer size for LVGL's screen drawing. A larger buffer reduces update frequency to the display and increases memory usage.int main(void){.../*According to the actual screen resolution used, the following is the setting for a screen resolution of 480x480 specification*/disp_drv.hor_res = 480; //Set horizontal resolutiondisp_drv.ver_res = 480; //Set vertical resolution.../*Create a Demo*/// lv_demo_widgets();ui_init();...}
4.移植例程到开发板
4.1 交叉编译并运行按键例程
-
修改 Makefile。
CC := /home/buildroot/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gccBIN = button_demoCSRCS +=$(LVGL_DIR)/source/button/button.c- 注意:CC 变量内容请根据自己的 SDK 路径填写
-
执行 make 指令。
make -
scp 传输例程。
scp button_demo root@192.168.9.110:/root- 注意:请根据自己开发板的实际 IP 地址填写
-
在开发板终端运行例程。
[root@luckfox root]# ./button_demo- 以下为例程运行效果图:
- 以下为例程运行效果图:
4.2 交叉编译并运行滑动块例程
-
修改Makefile文件。
CC := /opt/workspace/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gccBIN = slider_demoCSRCS +=$(LVGL_DIR)/source/slider/slider.c- 注意:CC 变量内容请根据自己的 SDK 路径填写
-
执行 make 指令。
make -
scp 传输例程。
scp slider_demo root@192.168.9.110:/root- 注意:请根据自己开发板的实际 IP 地址填写
-
在开发板终端运行例程。
[root@luckfox root]# ./slider_demo- 以下为例程运行效果图:
- 以下为例程运行效果图:
4.3 交叉编译并运行开关例程
-
修改Makefile文件。
CC := /opt/workspace/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gccBIN = switch_demoCSRCS +=$(LVGL_DIR)/source/switch/switch.c- 注意:CC 变量内容请根据自己的 SDK 路径填写
-
执行 make 指令。
make -
scp 传输例程。
scp switch_demo root@192.168.9.110:/root- 注意:请根据自己开发板的实际 IP 地址填写
-
在开发板终端运行例程。
[root@luckfox root]# ./switch_demo- 以下为例程运行效果图:
- 以下为例程运行效果图:
4.4 交叉编译并运行综合例程
-
修改 Makefile。
CC := /home/buildroot/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gccBIN = multex_demoSRC_DIR = $(LVGL_DIR)/source/multexCSRCS += $(wildcard $(SRC_DIR)/*.c)CSRCS += $(wildcard $(SRC_DIR)/components/*.c)CSRCS += $(wildcard $(SRC_DIR)/fonts/*.c)CSRCS += $(wildcard $(SRC_DIR)/images/*.c)CSRCS += $(wildcard $(SRC_DIR)/screens/*.c)- 注意:CC变量内容请根据自己的 SDK 路径填写
-
执行 make 指令。
make -
scp 传输例程。
scp multex_demo root@192.168.9.110:/root- 注意:请根据自己开发板的实际 IP 地址填写
-
在开发板终端运行例程。
[root@luckfox root]# ./multex_demo-
以下为例程运行效果图:

-