芯课堂 | LVG免费开源GUI图形库

图片

概述

本文介绍目前LVGL的应用小知识,希望对采用MCU设计UI界面的用户有所启发,开发出界面更友好的消费品或者工业产品,造福大众。

01.LVGL系统架构

LVGL系统框架

应用程序创建GUI并处理特定任务的应用程序。

LVGL本身是一个图形库。我们的应用程序通过调用LVGL库来创建GUI。它包含一个HAL(硬件抽象层)接口,用于注册显示和输入设备驱动程序。

驱动程序除特定的驱动程序外,它还有其他的功能,可驱动显示器到GPU (可选)、读取触摸板或按钮的输入。

根据MCU,有两种典型的硬件设置。一个带有内置LCD/TFT驱动器的外围设备,而另一种是没有内置LCD/TFT驱动器的外围设备。在这两种情况下,都需要一个帧缓冲区来存储屏幕的当前图像。

1.集成了TFT/LCD驱动器的MCU如果MCU集成了TFT/LCD驱动器外围设备,则可以直接通过RGB接口连接显示器。在这种情况下,帧缓冲区可以位于内部RAM(如果MCU有足够的RAM)中,也可以位于外部RAM(如果MCU具有存储器接口)中。

2.如果MCU没有集成TFT/LCD驱动程序接口,则必须使用外部显示控制器(例如SSD1963、SSD1306、ILI9341 )。在这种情况下,MCU可以通过并行端口,SPI或通过I2C与显示控制器进行通信。帧缓冲区通常位于显示控制器中,从而为MCU节省了大量RAM。

02.建立一个LVGL项目

要在我们的项目中使用 lvgl ,我们起码需要获取到官方的这两个库:

lvgl(lvgl)核心图形库的官方 GitHub 仓库地址:https://github.com/lvgl/lvgl。

lvgl(lv_drivers)输入输出设备驱动官方 GitHub 仓库地址:https://github.com/lvgl/lv_drivers

我们可以克隆或下载这两个库的最新版本,将它们复制到我们的项目中,然后进行适配。

目录 lvgl 就是 lvgl 的官方图形库

目录 lv_drivers 是 lvgl 输入输出设备驱动官方示例配置

目录 lv_examples 是 lvgl 的官方demo(可选,但不要直接使用到实际项目中)

图片

配置文件

上面的三个库中有一个类似名为 lv_conf_template.h 的配置头文件(template就是模板的意思)。通过它可以设置库的基本行为,裁剪不需要模块和功能,在编译时调整内存缓冲区的大小等等。

  1. 将 lvgl/lv_conf_template.h 复制到 lvgl 同级目录下,并将其重命名为 lv_drv_conf.h 。打开文件并将开头的 #if 0 更改为 #if 1 以使能其内容。

  2. 将 lv_drivers/lv_drv_conf_template.h 复制到 lv_drivers 同级目录下,并将其重命名为 lv_conf.h 。打开文件并将开头的 #if 0 更改为 #if 1 以使能其内容。

  3. (可选)将 lv_examples/lv_ex_conf_template.h 复制到 lv_examples 同级目录下,并将其重命名为 lv_ex_conf.h 。打开文件并将开头的 #if 0 更改为 #if 1 以使能其内容。

图片

准备lvgl配置文件

图片

使能配置文件

lv_conf.h 也可以复制到其他位置,但是应该在编译器选项中添加 ``LV_CONF_INCLUDE_SIMPLE``定义(例如,对于gcc编译器为``-DLV_CONF_INCLUDE_SIMPLE`` ) 并手动设置包含路径。

在配置文件中,注释说明了各个选项的含义。我们在移植时至少要检查以下三个配置选项,其他配置根据具体的需要进行修改:

  • LV_HOR_RES_MAX 显示器的水平分辨率。

  • LV_VER_RES_MAX 显示器的垂直分辨率。

  • LV_COLOR_DEPTH 颜色深度,其可以是:

8 - RG332

16 - RGB565

32 - (RGB888和ARGB8888)

初始化LVGL

准备好这三个库:lvgl、lv_drivers、lv_examples 后,我们就要开始使用lvgl带给我们的功能了。使用 lvgl 图形库之前,我们还必须初始化 lvlg 以及相关其他组件。初始化的顺序为:

  1. 调用 lv_init() 初始化 lvgl 库;

  2. 初始化驱动程序;

  3. 在 LVGL 中注册显示和输入设备驱动程序;

  4. 在中断中每隔 x毫秒 调用 lv_tick_inc(x) 用以告知 lvgl 经过的时间;

  5. 每隔 x毫秒 定期调用 lv_task_handler() 用以处理与 LVGL相关的任务。

03.显示接口

要设置显示,必须初始化 lv_disp_buf_t 和 lv_disp_drv_t 变量。

  • lv_disp_buf_t 保存显示缓冲区信息的结构体

  • lv_disp_drv_t HAL要注册的显示驱动程序、与显示交互并处理与图形相关的结构体、回调函数。

显示缓存区

关于缓冲区大小,有 3 种情况:

一个缓冲区 LVGL将屏幕的内保存到缓冲区中并将其发送到显示器。缓冲区可以小于屏幕。在这种情况下,较大的区域将被重画成多个部分。如果只有很小的区域发生变化(例如按下按钮),则只会刷新该部分的区域。

两个非屏幕大小的缓冲区 具有两个缓冲区的 LVGL 可以将其中一个作为显示缓冲区,而另一缓冲区的内容发送到后台显示。应该使用 DMA 或其他硬件将数据传输到显示器,以让CPU同时绘图。这样,渲染和刷新并行处理。与 一个缓冲区 的情况类似,如果缓冲区小于要刷新的区域,LVGL将按块绘制显示内容

两个屏幕大小的缓冲区 与两个非屏幕大小的缓冲区相反,LVGL将始终提供整个屏幕的内容,而不仅仅是块。这样,驱动程序可以简单地将帧缓冲区的地址更改为从 LVGL 接收的缓冲区。因此,当MCU具有 LCD/TFT 接口且帧缓冲区只是 RAM 中的一个位置时,这种方法的效果很好。

显示驱动器

一旦缓冲区初始化准备就绪,就需要初始化显示驱动程序。在最简单的情况下,仅需要设置 lv_disp_drv_t 的以下两个字段:

buffer 指向已初始化的 lv_disp_buf_t 变量的指针。

flush_cb 回调函数,用于将缓冲区的内容复制到显示的特定区域。刷新准备就绪后,需要调用lv_disp_flush_ready()。LVGL可能会以多个块呈现屏幕,因此多次调用flush_cb。使用 lv_disp_flush_is_last() 可以查看哪块是最后渲染的。

其中,有一些可选的数据字段:

hor_res 显示器的水平分辨率。(默认为 lv_conf.h 中的 LV_HOR_RES_MAX )

ver_res 显示器的垂直分辨率。(默认为 lv_conf.h 中的 LV_VER_RES_MAX )

color_chroma_key 在 chrome 键控图像上将被绘制为透明的颜色。(默认为 lv_conf.h 中的 LV_COLOR_TRANSP )

user_data 驱动程序的自定义用户数据。可以在 lv_conf.h 中修改其类型。

anti-aliasing 使用抗锯齿(anti-aliasing)(边缘平滑)。缺省情况下默认为 lv_conf.h 中的 LV_ANTIALIAS 。

rotated 如果 1 交换 hor_res 和 ver_res 。两种情况下 LVGL 的绘制方向相同(从上到下的线条),因此还需要重新配置驱动程序以更改显示器的填充方向。

screen_transp 如果为 1 ,则屏幕可以具有透明或不透明的样式。需要在      lv_conf.h 中启用 LV_COLOR_SCREEN_TRANSP 。

要使用GPU,可以使用以下回调:

gpu_fill_cb 用颜色填充内存中的区域。

gpu_blend_cb 使用不透明度混合两个内存缓冲区。

gpu_wait_cb 如果在 GPU 仍在运行 LVGL 的情况下返回了任何 GPU 函数,则在需要确保GPU渲染就绪时将使用此函数。

注意,这些功能需要绘制到内存(RAM)中,而不是直接显示在屏幕上。

其他一些可选的回调,使单色、灰度或其他非标准RGB显示一起使用时更轻松、优化:

rounder_cb 四舍五入要重绘的区域的坐标。例如。2x2像素可以转换为2x8。如果显示控制器只能刷新特定高度或宽度的区域(对于单色显示器,通常为8 px高),则可以使用它。

set_px_cb 编写显示缓冲区的自定义函数。如果显示器具有特殊的颜色格式,则可用于更紧凑地存储像素。(例如1位单色,2位灰度等)。这样,lv_disp_buf_t中使用的缓冲区可以较小,以仅保留给定区域大小所需的位数。set_px_cb不能与两个屏幕大小的缓冲区一起显示缓冲区配置。

monitor_cb 回调函数告诉在多少时间内刷新了多少像素。

clean_dcache_cb 清除与显示相关的所有缓存的回调

要设置 lv_disp_drv_t 变量的字段,需要使用 lv_disp_drv_init(&disp_drv) 进行初始化。最后,要为 LVGL 注册显示设备,需要调用lv_disp_drv_register(&disp_drv)。

04.输入设备接口

(一)、输入设备的类型

要设置输入设备,必须初始化 lv_indev_drv_t 变量:

图片

类型 (indev_drv.type)可以是:

  1. LV_INDEV_TYPE_POINTER 触摸板或鼠标

  2. LV_INDEV_TYPE_KEYPAD 键盘或小键盘

  3. LV_INDEV_TYPE_ENCODER 带有左,右,推动选项的编码器

  4. LV_INDEV_TYPE_BUTTON 外部按钮按下屏幕

read_cb (indev_drv.read_cb)是一个函数指针,将定期调用该函数指针以报告输入设备的当前状态。它还可以缓冲数据并在没有更多数据要读取时返回 false ,或者在缓冲区不为空时返回 true 。

进一步了解有关 输入设备 的更多信息。

(二)、触摸板,鼠标或任何指针

可以单击屏幕点的输入设备属于此类别。

即使状态为 LV_INDEV_STATE_REL ,触摸板驱动程序也必须返回最后的 X/Y 坐标。

要设置鼠标光标,请使用 lv_indev_set_cursor(my_indev,&img_cursor) 。( my_indev 是 lv_indev_drv_register 的返回值)键盘或键盘

(三)、触摸板或键盘

带有所有字母的完整键盘或带有一些导航按钮的简单键盘均属于此处。

要使用键盘/触摸板:

  • 注册具有 LV_INDEV_TYPE_KEYPAD 类型的 read_cb 函数。

  • 在 lv_conf.h 中启用 LV_USE_GROUP

  • 必须创建一个对象组:lv_group_t * g = lv_group_create(),并且必须使用 lv_group_add_obj(g,obj) 向其中添加对象

  • 必须将创建的组分配给输入设备:lv_indev_set_group(my_indev,g)(      my_indev 是 lv_indev_drv_register 的返回值)

  • 使用 LV_KEY _… 在组中的对象之间导航。有关可用的密钥,请参见      lv_core/lv_group.h。

(四)、编码器

可以通过下面四种方式使用编码器:

  1. 按下按钮

  2. 长按其按钮

  3. 转左

  4. 右转

简而言之,编码器输入设备的工作方式如下:

  • 通过旋转编码器,可以专注于下一个/上一个对象。

  • 在简单对象(如按钮)上按下编码器时,将单击它。

  • 如果将编码器按在复杂的对象(如列表,消息框等)上,则该对象将进入编辑模式,从而转动编码器即可在对象内部导航。

  • 长按按钮,退出编辑模式。

要使用编码器(类似于键盘),应将对象添加到组中。

(五)、使用带有编码器逻辑的按钮

除了标准的编码器行为外,您还可以利用其逻辑来使用按钮导航(聚焦)和编辑小部件。如果只有几个按钮可用,或者除编码器滚轮外还想使用其他按钮,这将特别方便。

需要有3个可用的按钮:

LV_KEY_ENTER 将模拟按下或推动编码器按钮

LV_KEY_LEFT 将向左模拟转向编码器

LV_KEY_RIGHT 将正确模拟转向编码器

其他键将传递给焦点小部件

如果按住这些键,它将模拟indev_drv.long_press_rep_time中指定的时间段内的编码器单击。

(六)、按键

按钮是指屏幕旁边的外部“硬件”按钮,它们被分配给屏幕的特定坐标。如果按下按钮,它将模拟在指定坐标上的按下。(类似于触摸板)

使用 lv_indev_set_button_points(my_indev, points_array) 将按钮分配给坐标。points_array应该看起来像const lv_point_t points_array [] = {{12,30},{60,90},…}

points_array不能超出范围。将其声明为全局变量或函数内部的静态变量。

(七)、其它功能

除了 read_cb 之外,还可以在 lv_indev_drv_t 中指定 feedback_cb 回调。输入设备发送任何类型的事件时,都会调用feedback_cb。(独立于其类型)。它允许为用户提供反馈,例如在LV_EVENT_CLICK上播放声音。

可以在lv_conf.h中设置以下参数的默认值,但可以在lv_indev_drv_t中覆盖默认值:

  • 拖拽限制(drag_limit) 实际拖动对象之前要滑动的像素数 drag_throw 拖曳速度降低[%]。更高的价值意味着更快的减速

  • (drag_throw) 拖曳速度降低[%]。更高的价值意味着更快的减速

  • (long_press_time) 按下时间发送 LV_EVENT_LONG_PRESSED (以毫秒为单位)

  • (long_press_rep_time) 发送 LV_EVENT_LONG_PRESSED_REPEAT 的时间间隔(以毫秒为单位)

  • (read_task) 指向读取输入设备的lv_task的指针。可以通过 lv_task_...() 函数更改其参数

每个输入设备都与一个显示器关联。默认情况下,新的输入设备将添加到最后创建的或显式选择的显示设备(使用lv_disp_set_default())。相关的显示已存储,并且可以在驱动程序的显示字段中更改。

(八)、心跳

LVGL 需要系统滴答声才能知道动画和其他任务的经过时间。

为此我们需要定期调用 lv_tick_inc(tick_period) 函数,并以毫秒为单位告知调用周期。例如, lv_tick_inc(1) 用于每毫秒调用一次。

为了精确地知道经过的毫秒数,lv_tick_inc 应该在比 lv_task_handler() 更高优先级的例程中被调用(例如在中断中),即使 lv_task_handler 的执行花费较长时间。

(九)、任务处理器(Task Handler)

要处理 LVGL 的任务,我们需要定期通过以下方式之一调用 lv_task_handler() :

  • mian 函数中设置 while(1) 调用

  • 定期定时中断(低优先级然后是 lv_tick_inc()) 中调用

  • 定期执行的 OS 任务中调用

计时并不严格,但应保持大约5毫秒以保持系统响应。

05.日志记录

LVGL 内置有日志模块,用于记录用户库中正在发生的事情。

(一)、日志级别

要启用日志记录,需要在 lv_conf.h 中将 LV_USE_LOG 设置为 1 ,并将 LV_LOG_LEVEL 设置为以下值之一:

  • LV_LOG_LEVEL_TRACE 记录所有信息

  • LV_LOG_LEVEL_INFO 记录重要事件

  • LV_LOG_LEVEL_WARN 记录是否发生了警告事件

  • LV_LOG_LEVEL_ERROR 记录错误信息,当系统可能发生故障时或致命错误

  • LV_LOG_LEVEL_NONE 不要记录任何东西

级别高于设置的日志级别的事件也将被记录。例如。如果使用 LV_LOG_LEVEL_WARN ,也会记录错误。

(二)、使用printf记录

如果您的系统支持printf,则只需在 lv_conf.h 中启用 **LV_LOG_PRINTF **即可发送带有 printf 的日志。

(三)、自定义日志功能

如果不能使用 printf 或想要使用自定义函数进行日志记录,可以使用 lv_log_register_print_cb() 注册 “logger” 回调。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/293220.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RFID技术在3C家电中的全方位应用

RFID技术在3C家电中的全方位应用 一、RFID技术简述 射频识别(RFID)技术是一种无线通信技术,已经在各行各业得到广泛应用。在3C家电领域,RFID技术的应用正在逐渐增加,为产品追溯、库存管理、防伪验证等方面提供了许多…

运维:电脑技巧:Win10常见的网络端口大全

目录 一、什么是网络端口? 二、网络传输协议 三、常见的 TCP 和 UDP 默认端口 一、什么是网络端口? 在计算机网络中,端口是通信端点。通常,端口标识分配给它们的特定网络服务。在操作系统中,端口号的主要用途协助是…

Python从入门到网络爬虫(内置函数详解)

前言 Python 内置了许多的函数和类型,比如print(),input()等,我们可以直接在程序中使用它们,非常方便,并且它们是Python解释器的底层实现的,所以效率是比一般的自定义函数更有效率。目前共有71个内置函数&…

Python爬取解放号外包需求案例,利用post参数多页爬取

代码展示: import requests import csv f open(外包数据.csv,modea,encodingutf-8,newline) csv_writer csv.writer(f) csv_writer.writerow([标题,编号,开始时间,结束时间,价格,状态,类型,投标人数,详情页]) def down_load(page): for page in range(1,page…

​电脑技巧:​笔记本电脑电流声的原因和解决方案

目录 一、音频设备接口接触不良 二、笔记本电源问题 三、笔记本电脑驱动程序问题 四、音频硬件问题 五、操作系统内部电磁干扰 六、最后总结 大家在日常生活当中,笔记本电脑已经成为我们工作、学习和娱乐的重要工具。但有时我们在使用过程中可能会遇到一个令人…

React组件之间的8种通讯方式

在 React 社区,遇到最多的其中一个问题是“不同组件之间如何相互通讯”。 在网上搜索了一些答案之后,早晚会有人提到 Flux,随后问题来了:“怎么用Flux解决通讯问题?Flux是必须的吗?”。 有时候 Flux 能解…

C++完成Query执行sql语句的接口封装和测试

1、在LXMysql.h 创建Query执行函数 //封装 执行sql语句 if sqllen 0 strlen获取字符长度bool Query(const char*sql,unsigned long sqllen0); 2、在LXMysql.cpp编写函数 bool LXMysql::Query(const char* sql, unsigned long sqllen){if (!mysql)//如果mysql没有初始化好{c…

C/C++ BM4 合并两个排序的链表

文章目录 前言题目1. 解决方案一1.1 思路概述1.2 源码 2. 解决方案二2.1 思路阐述2.2 源码 总结 前言 这道题采用两种方式,一种是直接插入法,还有一种就是递归调用。 题目 输入两个递增的链表,单个链表的长度为n,合并这两个链表…

imgaug库指南(四):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里,数据是模型训练的基石,其质量与数量直接影响着模型的性能。然而,获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此,数据增强技术应运而生,成为了解决这一问题的…

AntDB内存管理之内存上下文

1. 主题说明 AntDB的内存管理在开发时,使用了内存上下文机制来实现内存管理。本文就从AntDB的内存上下文机制出发,解析内存上下文的实现原理。AntDB的代码中,涉及到内存的处理时,经常会看到下面这样的代码。 图1:切换…

SpringBean的生命周期

SpringBean Bean的生命周期 1、首先需要明确bean对象与普通对象的区别: 对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。 而 Spring 中的对象是 bean,…

Gin 路由注册与请求参数获取

Gin 路由注册与请求参数获取 文章目录 Gin 路由注册与请求参数获取一、Web应用开发的两种模式1.前后端不分离模式2.前后端分离模式 二、RESTful介绍三、API接口3.1 RESTful API设计指南3.2 API与用户的通信协议3.3 RestFul API接口设计规范3.3.1 api接口3.3.2 接口文档&#xf…

C++_模板

目录 1、函数模板 1.2 模板原理 2、多个模板参数 3、模板的显示实例化 4、模板的匹配 5、类模板 结语: 前言: 在C中,模板分为函数模板和类模板,而模板的作用就是避免了重复的工作,把原本是程序员要做的重复工作…

内网DNS隐蔽隧道搭建之iodine工具

iodine iodine是基于C语言开发的,分为服务端和客户端。iodine支持转发模式和中继模式。其原理是:通过TAP虚拟网卡,在服务端建立一个局域网;在客户端,通过TAP建立一个虚拟网卡;两者通过DNS隧道连接&#xf…

YACS(上海计算机学会竞赛平台)2023年12月月赛——移动复位

移动复位 内存限制: 256 Mb时间限制: 1000 ms 题目描述 二维平面上有一个点。该点最初所在的位置称之为起点。接下来,该点接受了一串命令,每个命令可以用一个大写字母表示: R 表示该点沿 X 轴坐标正方向移动了一个单位;L 表示…

AI实景无人直播创业项目:开启自动直播新时代,一部手机即可实现增长

在当今社会,直播已经成为了人们日常生活中不可或缺的一部分。无论是商家推广产品、明星互动粉丝还是普通人分享生活,直播已经渗透到了各行各业。然而,传统直播方式存在着一些不足之处,如需现场主持人操作、高昂的费用等。近年来&a…

CentOs 环境下使用 Docker 部署 Ruoyi-Vue

CentOs 环境下使用 Docker 部署 Ruoyi-Vue RuoYi-Vue 项目下载地址 RuoYi-Vue: 🎉 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本 (gitee.com) Docker 部…

x-cmd pkg | tig - git 文本模式界面

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 tig 由 Jonas Fonseca 于 2006 年使用 C 语言创建的 git 交互式文本命令行工具。旨在开启交互模式快速浏览 git 存储库的信息以及 git 命令的运行。 首次用户 使用 x tig 即可自动下载并使用 在终端运行 eval "…

NeurIPS上新 | 从扩散模型、脑电表征,到AI for Science,微软亚洲研究院精选论文

编者按:欢迎阅读“科研上新”栏目!“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里,你可以快速浏览研究院的亮点资讯,保持对前沿领域的敏锐嗅觉,同时也能找到先进实用的开源工具。 本期“科研上新…

项目框架构建之6:编写通用主机基础类

本文是“项目框架构建”系列之6,本文介绍如何编写通用主机基础类。 1.为了构建通用主机,我们先创建主机接口IAppHost接口 接口需要有配置项,我们定义为HostConfiguration,比如我们希望用户可以设定他的工作目录,就可…