LVGL(Light and Versatile Graphics Library)是一个免费、开源的嵌入式图形库,可以创建丰富、美观的界面,具有许多可以自定义样式的控件,支持按键或触摸响应,支持中文字符,并且内存占用较低。可以在 https://lvgl.io/demos 使用网页端体验 LVGL 的动态效果,再决定是否需要使用 LVGL 。
LVGL 使用 C 语言编写,可以用在树莓派、ESP32 、STM32 等单片机上,并支持各种中大型屏幕(只需要提供屏幕的绘图 API 即可)。LVGL 的官网地址为:https://lvgl.io/ ,GitHub 地址为:https://github.com/lvgl/lvgl 。
LVGL 提供了许多示例程序,还提供了 PC 端的模拟器,这都加快了 LVGL 的开发效率。
LVGL 并没有只针对哪一个单片机和哪一个屏幕,事实上它甚至在 PC 机也能运行的起来。完整的 LVGL 的移植可以参考官方文档的介绍 https://docs.lvgl.io/master/porting/index.html 。在移植前,请自行了解单片机及屏幕的使用方法并提供接口程序。
接下来以 STM32 系列单片机为例介绍 LVGL 的移植,不同单片机的移植过程也可以参考以下步骤。下表给出了 LVGL 所需的配置,在使用 LVGL 前请确保单片机性能满足要求:
| Name | Minimal | Recommended |
|---|---|---|
| Architecture | 16, 32 or 64 bit microcontroller or processor | |
| Clock | > 16 MHz | > 48 MHz |
| Flash/ROM | > 64 kB | > 180 kB |
| Static RAM | > 16 kB | > 48 kB |
| Draw buffer | > 1 ×hor. res. pixels | > 1/10 screen size |
| Compiler | C99 or newer |
注意:使用 Keil5 请开启“C99 Mode”,否则会编译不通过。还在使用 Keil4 的请升级或更换编译器。
首先,在 https://github.com/lvgl/lvgl 下载或克隆整个工程。LVGL 的最新版本是 LVGL 8.2 ,注意 LVGL 7 已经不再更新,LVGL 7 和 8 之间库结构发生较大改变,编写出的代码并不很兼容,并且 LVGL 7 的示例代码和模拟器似乎已经在 GitHub 上下架了。本教程以 LVGL 8 为例,移植 LVGL 7 的话可以参考,但一些细节需要注意调整。建议使用最新版本,否则无法得到完整的工具链支持。
使用 Keil 的开发者请注意,LVGL 8 似乎不能在 ARM CC v5 下编译成功,请更新编译器版本为 ARM CC v6 。
使用 STM32 的开发者还需要注意,STM32 标准库无法使用 ARM CC v6 编译,请使用 HAL 库或更换编译工具链(如 LLVM-clang 或 GCC-none-eabi )
接下来自行准备一个单片机工程,在 User 或其它等效的目录中,然后新建目录 lvgl 并进入,从克隆得到的 LVGL 工程中复制以下文件或目录到其中:
demos
examples
src
lvgl.h
lv_conf_template.h
如果不需要使用官方提供的示例代码,可以不复制 demos 目录。
接下来,将 lv_conf_template.h 重命名为 lv_conf.h ,并移动到上一级目录中。
注意:LVGL 库的目录比较复杂,头文件引用相对混乱,在没有充分明白正在做什么之前,请不要随意修改文件夹名或变更文件位置。
回到上一级目录,打开 lv_conf.h ,将开头的 #if 0 条件编译取消,启用文件包含的配置:
/* clang-format off */
#if 1 /*Set it to "1" to enable content*/
该配置文件还有几处需要调整的地方,首先最前面(第 27 行)的一个宏定义表示显示屏的颜色深度,需要根据不同的显示屏做调整:
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
如果屏幕的颜色深度不一致,一定要修改该宏。LVGL 会根据该宏创建合适的颜色定义,如果与实际不一致会造成显示时颜色错乱。
如果设置为 8 ,代表使用 8 位的颜色,其中 RBG 色值各占 3 、3 、2 位;如果设置为 16 ,则 RBG 色值各占 5 、6 、5 位,这是许多 TFT 屏采用的颜色格式;32 则是 PC 机和移动设备都使用的带透明度的 32bit 位图,RGB 色值和透明度各占一个字节。
第 52 行中还有一处表示最大占用内存量的宏,可以根据实际单片机的情况自行修改,只要保证大于注释中写的 2kB 就行。
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
除此之外,在第 273 和 280 行还有这么两个宏定义,如果将它们设置为 1 ,那么可以在屏幕的左下角和右下角显示当前内存占用和帧率,非常适合性能分析:
#define LV_USE_PERF_MONITOR 0
#define LV_USE_MEM_MONITOR 0
其它设置可以对照注释和文档修改。
接下来开始导入工程文件,这一步需要将 lvgl/src 中除了 draw 目录中的所有文件全部导入,而 draw 目录中除了根目录的 .c 文件外,只导入 sw 目录中的源文件。LVGL 8 的目录深度较大,请耐心添加,细心检查,不要遗漏文件。
使用 STM32 单片机的话还需要注意在启动文件中修改堆、栈大小,至少各设置 8kB 空间:
Stack_Size EQU 0x00002000
Heap_Size EQU 0x00002000
全部添加完成之后,尝试编译整个工程,应该是可以零 error 通过了。
使用 ARM CC v6 可能会发生
__aeabi_assert符号未定义的问题,可以在整个项目管理中提前定义宏NDEBUG禁用该符号。
LVGL 只提供了绘图的算法,其它内容需要自行编写。LVGL 提供的接口在 lvgl/examples/porting 目录中,该目录有如下文件:
lv_port_disp :显示设备接口lv_port_indev :输入设备接口lv_port_fs :文件系统接口将各个文件名结尾的 template 去除。接下来先编写显示设备的接口,至少确保能显示一些东西来。
在 lv_port_disp.c 及其头文件中,首先需要去除条件编译,启用这部分内容:
/*Copy this file as "lv_port_disp.h" and set this value to "1" to enable content*/
#if 1
由于之前重命名过头文件,因此在源文件中也需要修改对应的名称:
#include "lv_port_disp.h"
源文件在宏定义区域中有两个宏定义,需要修改为实际的显示屏尺寸。改过了之后记得把 #warning 预处理语句去除了:
#ifndef MY_DISP_HOR_RES
//#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
#define MY_DISP_HOR_RES 320
#endif
/* ... same as above ... */
lv_port_disp_init() 是一个最顶层的初始化显示设备的函数,在主函数中需要调用它一次性初始化显示设备的功能。该函数的修改方式注释里已经写的较为清楚了,接下来提供一个修改示例。
首先将 91~102 行的两个提供显示缓存的语句全部注释或删除,只保留 /* Example for 1) */ 。然后修改 114~115 行的两个数值为实际的屏幕清晰度。
/* 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*/
/*Set the resolution of the display*/
disp_drv.hor_res = 320;
disp_drv.ver_res = 240;
该文件内还有两个函数 disp_init() 和 disp_flush() ,需要提供实际显示设备的接口。
disp_init() 中,需要提供屏幕的初始化代码,如果已经在外部初始化过可以忽略。
disp_flush() 中,需要在注释的位置根据提供的参数绘制一个像素点,。这一过程也可以使用填充函数获得更快的速度,甚至可以使用 GPU 等加速等方式完成,具体如何编写代码可以参考注释。例如,测试用的屏幕是这样逐个绘制像素点,从而填充一块区域的:
/* ... */
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
ILI9341_SetFrontColor(&ili9341, color_p->full);
ILI9341_DrawPixel(&ili9341, x, y);
color_p++;
}
}
/* ... */
至此,API 移植便结束了。接下来可以编写程序测试 LVGL 的效果了。
在使用 LVGL 前,需要调用以下两个函数完成 LVGL 库的初始化以及 LVGL 显示设备接口的初始化:
lv_init();
lv_port_disp_init();
然后就可以绘制图形了。这里提供了一段简单的代码,可以绘制一个按钮:
lv_obj_t* btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
绘制完之后,还需要在主循环中调用 lv_task_handler() 函数,这样绘制的内容才能实时更新到屏幕上:
while (1) {
/* ... */
lv_task_handler();
}
然后将编译得到的结果下载到单片机内,就可以在屏幕上看到一个按钮了:

上文介绍了如何移植显示设备。但是 LVGL 是一个用户界面库,光有显示设备,不能做一些用户交互的功能还是不太够,因此就需要使用输入设备。
LVGL 支持 5 种类型的输入设备:
在移植时,不要搞错了输入设备的类型,否则 LVGL 无法对输入作出响应。
LVGL 对输入设备的接口全部存放在 lv_port_indev.c 及其头文件中。接下来以触摸屏为例介绍输入设备的移植,不同设备的 API 有一定区别,在移植时请以官方文档为主。
首先,需要去掉两个文件中的 #if 0 条件编译,启用两个文件。
在 lv_port_indev.c 中,包含了 5 种设备的 API ,但它们不可能都用到,因此需要裁剪无用的函数和定义。尤其是在初始化函数 lv_port_indev_init() 中,如果不去除无用设备的初始化语句,那么在调用时可能会出现问题。
源码在注释中已经着重强调了不同 API 的分区,只需要根据分区保留需要的代码即可。
根据代码的思路(精简后的源码不长,而且抽象程度较高,完全可以读懂),接下来实现三个函数的功能。
首先是 touchpad_init() ,在这里需要对输入设备做初始化,就像上文对触摸屏做初始化一样。
在 touchpad_is_pressed() 中,需要提供一个显示屏触摸函数,判断是否发生了触摸事件:
static bool touchpad_is_pressed(void) {
if (XPT2046_TouchDetect() == TOUCH_PRESSED)
return true;
return false;
}
如果发生了触摸事件,那么会进入 touchpad_get_xy() 函数中,获取触摸点坐标:
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y) {
static XPT2046_Coordinate coord = { -1, -1, -1, -1 };
XPT2046_Get_TouchedPoint(&xpt2046, &coord);
(*x) = coord.x;
(*y) = coord.y;
}
如果这几个函数都编写正确,那么理论上已经可以实现输入功能了。不过在此之前,还有一个关键的步骤:LVGL 使用一个 tick 系统管理全局事件,它就像 LVGL 的心跳一样,如果没有这个心跳就无法检测事件。
为了给 LVGL 提供心跳,需要不断调用 lv_tick_inc() 函数,该函数的参数为每次心跳的毫秒间隔:
while (1)
{
lv_tick_inc(1);
lv_task_handler();
delay_ms(1);
}
使用单片机时更推荐使用定时器完成该函数的调用,设置定时器溢出时间为 1 毫秒后在定时器中断函数内调用它。
接下来提供一个示例,可以检测输入设备是否能正常使用。首先在 main 函数的开头执行输入设备的初始化:
lv_port_indev_init();
然后编写如下函数:
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++;
lv_obj_t* label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
void lv_example(void) {
lv_obj_t* btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
}
在主函数中调用 lv_example() ,编译后下载到单片机内,可以得到一个和上一个示例相同的按钮,但是每次点击之后,按钮的文本都会发生变化:

其余内容将第一时间更新于:http://frozencandles.fun/archives/307
LVGL 是一个图形库,那么在绘制图形时就免不了需要对绘制结果做一些微调。那么每次微调都需要将程序下载到单片机去显然是麻烦的选择,不过幸好 LVGL 提供了模拟器,可以在 PC 端上直接生成可交互的界面,无需下载即可查看绘制效果。
LVGL 可以在各个平台上模拟,完整的模拟器使用指南可以参照 https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html 。接下来以 Windows 平台基于 Visual Studio 的模拟为例介绍通用的使用方法。
首先,在 https://github.com/lvgl/lv_port_win_visual_studio 中下载 Visual Studio 工程源码。注意,在 LVGL.Simulator 目录中包含 3 个外部的仓库,需要将它们一并下载并放在正确的位置。
然后,使用 Visual Studio 打开 LVGL.Simulator.sln 工程,点击编译即可得到 GUI 可执行文件。

需要注意的是,Visual Studio 提供的模拟器是使用 C++ 编写的,如果需要自定义函数,需要在头文件中使用
#ifdef __cplusplus
extern "C" {
#endif
/* ... function prototypes ... */
#ifdef __cplusplus
}
#endif
将函数原型包围起来,否则在使用 C 语言符号时会出错。
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
我是Ruby新手,并被要求在我们的新项目中使用它。我们还被要求使用Padrino(Sinatra)作为后端/框架。我们被要求使用Rspec进行测试。我一直在寻找可以指导在Padrino上使用RspecforRuby的教程。我得到的主要是引用RoR。但是,我需要RubyonPadrino。请在任何入门/指南/引用/讨论等方面指导我。如有不妥之处请指正。可能是我没有针对我的问题搜索正确的词/短语组合。我正在使用Ruby1.9.3和Padrinov.0.10.6。注意:我还提到了SOquestion,但它没有帮助。 最佳答案 我没用过Pa
我给自己买了一个新的8gigUSBkey,我正在寻找一个合适的解决方案来拥有一个可移植RoR环境来学习。我在谷歌上搜索了一下,发现了一些可能性,但我很想听听一些现实生活中的经历和意见。谢谢! 最佳答案 我喜欢InstantRails,非常容易使用,无需安装程序,也不会修改您的系统环境。 关于ruby-on-rails-可移植RubyonRails环境,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/q