lvgl
目录
lvgl
Lvgl移植到STM32
-- 1、下载LVGL源码
-- 2、将必要文件复制到工程目录
-- 3、修改配置文件
将lvgl与底层屏幕结合到一块
-- lvgl也需要有定时器,专门给自己做了一个函数,告诉lvgl经过了多长时间(ms(毫秒)级别)
编写代码
lvgl的中文教程手册网站
lvgl的基础知识
-- 重点是lvgl基础控件的使用
-- 如何用lvgl去制作软件
更改错误
-- 8、只要使用汉字,都按照下面这种方式修改
屏幕显示调用
如何实现界面刷新(界面中的数据如何变化)
总结
- 主要用于界面设计
-- 我们在做的项目是空气质量检测仪,下面这些是项目在开发阶段分的模块
- 分为项目硬件,项目软件,项目结构(产品外壳),UI(界面设计),APP,测试
-- 对于界面开发,有很多,如qt,但是qt开发出来的程序比较大,占用内存比较多,所以选择了lvgl
-- GUI:LVGL,emwin,qt,touchgfx,minigui,simplegui(嵌入式开发阶段的界面开发工具)
-- LVGL(轻巧而多功能的图形库)是一个免费的开放源代码图形库,它提供创建具有易于使用的图形元素,精美的视觉效果和低内存占用的嵌入式GUI所需的一切。
- Lvgl官网:LVGL — Light and Versatile Embedded Graphics Library
- Lvgl官方文档:LVGL: Light and Versatile Graphics Library — LVGL documentation
- Lvgl源码网址:https://github.com/lvgl/lvgl
-- lvgl官方网站
-- 硬件要求:
基本上,每个现代控制器(肯定必须要能够驱动显示器)都适合运行LVGL。LVGL的最低运行要求很低: 16、32或64位微控制器或处理器
最低 16 MHz 时钟频率
Flash/ROM::对于非常重要的组件要求 >64 kB(建议 > 180 kB)
-- Lvgl系统框架
-- 应用程序创建 GUI 并处理特定任务的应用程序。 -- LVGL 本身是一个图形库。我们的应用程序通过调用 LVGL 库来创建 GUI 。它包含一个 HAL (硬件抽象层)接口,用于注册显示和输入设备驱动程序。 驱动程序除特定的驱动程序外,它还有其他的功能,可驱动显示器到 GPU (可选)、读取触摸板或按钮的输入。
Lvgl移植到STM32
-- 1、下载LVGL源码
- 我们使用的是Lvgl7.11版本,因此在Github上找到对应版本源码下载:https://github.com/lvgl/lvgl/tree/v7.11.0
-- 其中lvgl/src文件夹内存放的是LVGL的核心源码,lvgl/examples/porting文件夹内存放的是lvgl与底层的接口函数,这些函数需要我们根据自己的项目进行修改。
-- 源码在文件夹中可以找到
-- 2、将必要文件复制到工程目录
-- 1、在个人的工程目录下创建一个名为Lvgl的文件夹,并将lvgl/src目录复制到Lvgl目录下,将lvgl/examples/porting文件夹复制到Lvgl目录下,同时将lvgl/lvgl.h文件以及lvgl/lv_conf_template.h文件复制到Lvgl目录下。如下图所示:
-- 2、将lv_conf_template.h文件更名为lv_conf.h,如下图所示:
-- 3、修改port目录下所需要的文件名字,我们只使用了屏幕的显示功能,因此我们只修改显示接口的文件名字,将lv_port_disp_template.c/.h更名为lv_port_disp.c/.h如下图所示:
-- 4、在Lvgl目录下再创建一个app目录,用于存放我们后期自己写的应用层界面代码,具体操作如下图所示:
-- 5、打开工程,在工程目录下新建三个分组,分别为Lvgl/app、Lvgl/porting、Lvgl/src三个目录,具体操作如下图所示:
-- 6、添加文件到工程目录中,porting目录下只添加lv_port_disp.c,以及Lvgl目录下的lv_conf.h文件,这两个文件后面需要修改。
在src目录下,添加Lvgl/src目录下除去gpu文件夹外的所有文件夹内的c文件。
-- 7、配置头文件路径,把Lvgl文件夹下所有包含h文件的路径,在工程属性中进行配置,具体操作如下图:
-- 3、修改配置文件
-- 1、打开lv_port_disp.c/.h文件,修改如下内容
-- 2、修改lv_conf.h文件如下图所示,修改后编译代码,这个时候代码就没有错误了。
-- 3、接下来适配屏幕接口到lvgl上,先修改lv_conf.h内的宏定义,通过它可以设置库的基本行为,裁剪不需要模块和功能,在编译时调整内存缓冲区的大小等等,我们先修改一些必须修改的定义,后期的功能我们在具体项目中再做裁剪。
-- 注:
-- 竖屏和横屏可以自己选择
-- 更改单片机分给lvgl的空间大小
-- 是否要适配v6版本,我们这里选择v7版本,就不适配v6了,将宏定义改为0
将lvgl与底层屏幕结合到一块
-- 更改初始化函数,进入初始化函数中
-- 将lcd初始化加进来,以后就不用lcd初始化了,直接调用lvgl的初始化函数即可
-- 绘制缓冲区,屏蔽其他两种方法
-- 更改参数
-- 找到lcd中的指定区域内填充指定颜色快的函数
-- 并在.h中声明
-- 将lv_port_disp.c中的函数进行修改
-- lvgl也需要有定时器,专门给自己做了一个函数,告诉lvgl经过了多长时间(ms(毫秒)级别)
-
先将操作系统中的宏定义改变
-
我们要写一个时钟节拍钩子函数
-
参考我们之前写的任务栈溢出函数
编写代码
-- 创建任务句柄
TaskHandle_t xLvglTaskHandle = NULL;
-- 创建任务主题函数
void vLvglTaskFunction( void * pvParameters )
{
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS( 5 );//5ms的延时
xLastWakeTime = xTaskGetTickCount();
for(;;)
{
lv_task_handler();//在裸机里放在while循环中即可
vTaskDelayUntil( &xLastWakeTime, xPeriod );//绝对延时
}
}
-- 创建任务(放在main函数中)
xTaskCreate(vLvglTaskFunction, "lvgl_task", 512, NULL, 2, &xLvglTaskHandle);
-- 启动任务
if(xReturn == pdPASS)
vTaskStartScheduler();//任务调度器 //当程序执行完这句话之后,正常情况下下面的代码不会再运行,只会运行任务主题函数
-- 上述内容我们就已经成功配置了lvgl的环境,下面我们写入一个测试代码,看看是否能够正常显示。写在初始化函数后面即可。
-- 完整代码
#include "lvgl.h"
#include "lv_port_disp.h"
TaskHandle_t xLvglTaskHandle = NULL;
void vLvglTaskFunction( void * pvParameters )
{
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS( 5 );//5ms的延时
xLastWakeTime = xTaskGetTickCount();
for(;;)
{
lv_task_handler();//在裸机里放在while循环中即可
vTaskDelayUntil( &xLastWakeTime, xPeriod );//绝对延时
}
}
int main(void)
{
lv_init();
lv_port_disp_init();
//测试代码--显示一个label
lv_obj_t * label1 = lv_label_create(lv_scr_act(), NULL);
lv_label_set_long_mode(label1, LV_LABEL_LONG_BREAK);
lv_label_set_recolor(label1, true);
lv_label_set_align(label1, LV_LABEL_ALIGN_CENTER);
lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label "
"and wrap long text automatically.");
lv_obj_set_width(label1, 150);
lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, -30);
BaseType_t xReturn = pdPASS;
xTaskCreate(vLvglTaskFunction, "lvgl_task", 512, NULL, 2, &xLvglTaskHandle);
//xTaskCreate(arc_loader, "lvgl_task", 512, NULL, 2, &xLvglTaskHandle);
if(xReturn == pdPASS)
vTaskStartScheduler();//任务调度器 //当程序执行完这句话之后,正常情况下下面的代码不会再运行,只会运行任务主题函数
}
-- 注意:这里用了lvgl的初始化函数,不用再用lcd的初始化了,前面已经把lcd初始化函数写在了lvgl的初始化函数里面了。
-- 测试效果图
lvgl的中文教程手册网站
-- 该网站包含各种事例代码和图形库
-- 英文 Layers — LVGL documentation
-- 中文 基础对象(lv_obj) — 百问网LVGL中文教程手册文档 1.0 文档
lvgl的基础知识
-- lvgl是一个图形库
-- 那么什么叫做图形库呢?
-- 这些就是一个一个的图形
-- 每个图形又可以称为对象
-- 对象的属性:假如要绘制一个仪表盘,那么他的大小设置多大,位置放在哪里。能不能拖动呢?是否被拖动?
-- 对象的层级:父子,小介面基于大介面的基础上创建出来的,例如在大介面上点击一个按钮导致小介面生成。
-- 事件:点击,拖动,长按,松开,滚动等等
-- 这个事件怎么处理呢?
-
当事件发生时,lvgl会跟根据提前定义好的回调函数进行处理,回调函数可以自己定义,也可以使用lvgl提供的回调函数。
-
不同的对象所对应的事件也不同
-- 还有特殊的事件,键盘或者事件编码
-- 输入设备:触摸屏,键盘,鼠标,编码器等等
-- 显示:
-- 字体:LVGL 支持 UTF-8 编码的 Unicode 字符。需要配置的编辑器,以将的代码/文本保存为 UTF-8 (通常是默认值)
-- 在lvgl里面,只要显示汉字,一定要在UTF-8格式下写汉字
-- lvgl是有自己的一个操作系统的,只是目前我们还没有使用
-- 重点是lvgl基础控件的使用
-- 例子:圆弧展示进度条
-- 首先将代码复制到咱们的程序里面
static void arc_loader(lv_task_t * t)
{
static int16_t a = 270;
a+=5;
lv_arc_set_end_angle(t->user_data, a);
if(a >= 270 + 360) {
lv_task_del(t);
return;
}
}
int main()
{
lv_init();
lv_port_disp_init();
//显示一个圆弧(进度条)
/*Create an Arc*/
lv_obj_t * arc = lv_arc_create(lv_scr_act(), NULL);
lv_arc_set_bg_angles(arc, 0, 360);
lv_arc_set_angles(arc, 270, 270);
// lv_obj_align(arc, NULL, LV_ALIGN_CENTER, 0, 0);//改变了对象的位置
lv_obj_set_pos(arc,20,60);//设置起点位置
lv_obj_set_size(arc,200,200);//设置对象大小
/* Create an `lv_task` to update the arc.
* Store the `arc` in the user data*/
lv_task_create(arc_loader, 20, LV_TASK_PRIO_LOWEST, arc);
}
-- 结果图太小了,需要找个能改变自身大小的
-
相关api
-
设置位置
lv_obj_set_pos(arc,20,60);//设置起点位置
- 设置对象大小
lv_obj_set_size(arc,200,200);//设置对象大小
-- 至此就可以添加一个圆弧图形了,但是这个圆弧是静态的,需要添加一个定时器,让圆弧动起来。所以前面写的时钟钩子函数就可以派上用场了。
-- 如何用lvgl去制作软件
-- 安装软件
-- 1、先安装Java环境(注意一定要先安装java环境)
-- 2、安装软件
-- 3、打开软件,点击创建,选择v7
-- 4、工程的名字和路径都不能出现中文,屏幕的类型如果选项中没有的话,可以自己定义
-- 5、创建成功后,会跳转新的界面,将右上角英文切换成中文,界面的颜色也可以选择,可以改为深蓝色或者其他颜色
-- 6、可以先选择一个图片当作背景,将图片拖拽过来,可以通过更改参数让它显示在全屏
-- 7、新增界面
-- 8、可以更改背景的颜色
-- 9、也可以更改图片的透明度,255是不透明,0是完全透明
-- 10、设置文本
-- 11、将表格拖进界面
-- 12、有些是不能用的
-- 13、将图形界面设计完之后,点击编译,编译成功后就会将配置好的界面显示出来
-- 14、注意:这个编译默认编译的是第一个界面,如果向编译其他界面。
-- 都编译成功后,开始将代码放到工程中
-- 15、先找到工程所在地方
-- 16、
-- 17、了解这些文件的作用
-- 18、在工程中新建文件夹
-- 19、添加头文件路径
-- 20、之后编译,出现错误
更改错误
-- 1、修改界面文件
-- 2、更改图模文件
-- 3、更改字模文件
-- 4、修改系统自带文件
-- 5、之后编译,然后有的电脑还会出现错误,有的没有
-- 6、添加一个定义
-- 7、修改错误,一般就是中文汉字编码的错误
- 只要有汉字,就会报两个错
-- 8、只要使用汉字,都按照下面这种方式修改
-- 解决方案一:显示的字符串,最后不能以汉字结束。在字符串结束,加一个空格
- 点击错误跳转到出错的地方,将字符串最后加一个空格
- 其中这个摄氏度就是中文的。
-- 解决方案二:将keil的编码格式修改为utf-8,然后在这种编码格式下重新输入汉字
-- 然后在这个格式下重新输入汉字,为了保险起见,后面也加上空格
屏幕显示调用
-- 将代码复制到main函数中
-- 加上头文件
-- 这两个代码分别是创建和显示
-- 只有创建函数的话,只会创建,创建完之后不会显示
-- 要想在屏幕中显示,必须要调用显示函数
-- 此时显示的界面是screen1,如果想要显示其他界面,如screen,只需要将screen1改为screen即可
-- 但是如果两个显示函数都调用的话,只会显示后面调用的那个
-- 那么如何实现切换界面
- 1、将创建函数写在main函数中
- 2、然后在按键任务中调用显示函数
- 3、加一个标志位,防止多次点击同一按键,导致界面反复切换同一个界面无意义
- 3、代码
uint8_t GUI_flag = 0;
//2
void KETTaskCreat(void *pvParameters)
{
uint8_t keyflag = 0;
uint8_t buff[10]={0};
while(1)
{
keyflag = get_key();
switch(keyflag)
{
case 1:
if(GUI_flag!=2)
{
lv_scr_load(guider_ui.screen); //显示
GUI_flag=2;
}
break;
case 2:
if(GUI_flag!=1)
{
GUI_flag =1;
lv_scr_load(guider_ui.screen_1); //显示
}
break;
}
vTaskDelay(50); //在任务周期时可以释放cpu
}
}
如何实现界面刷新(界面中的数据如何变化)
-- 1、实现文本数据刷新
-- 要想实现数据更新,必须要找到对应的对象,然后调用对应的赋值函数
-- 例如界面一的温度数值,先找到界面一对应的赋值函数,复制
-- 将函数复制到lcd显示任务函数中,直接将数值更改为变量,就可以实现动态更新数值了。
void LCDTaskCreat(void *pvParameters)//函数名称不固定,参数格式是固定的(任务一定是一个while循环,每个任务一定要有自己的任务周期)//设置任务周期的目的就是可以让任务可以主动释放cpu(指的是任务能够自动释放cpu)
{
//BaseType_t xReturn = pdPASS;//有没有获取成功,再显示LCD
EventBits_t r_event;
while(1)
{
r_event = xEventGroupWaitBits(Event_Handle, /* 事件对象句柄 */
0x07,/* 接收任务感兴趣的事件 */
pdTRUE,/* 退出时清除事件位 */
pdFALSE,/* 满足感兴趣的所有事件 *///逻辑或
500);/* 指定超时事件,一直等 */
//dht
if(r_event &(0x1<<0))//判断这个数中的位0是否为真
{
sprintf(D_wen, "tem: %.2d鈩?",(int)dht.tem);
//实现数据更新
//找到对应对象的赋值函数
lv_label_set_text(guider_ui.screen_1_label_1, D_wen);
}
}
}
-- 2、实现表格数据刷新(折线图)
-- 找到对应的赋值函数
-- 在lcd函数中调用
将值改为变量
-- 效果图
总结
-- 1、进行界面设计,注意先考虑好布局(做几个界面,每个界面显示什么内容,如何规划)
-- 2、保证每个界面在屏幕上都能显示出来
-- 3、保证界面相互切换不会卡死
-- 4、界面刷新