LVGL | LVGL移植之中文文档
目录
系统总览
设置项目
获取图书馆
配置文件
初始化
显示界面
显示缓冲区
显示驱动程序
回转
应用程序接口
输入设备接口
输入设备的类型
触摸板、鼠标或任何指针
键盘或键盘
编码器
按钮
其他特性
应用程序接口
时钟接口
应用程序接口
任务处理程序
睡眠管理
操作系统和中断
中断
日志记录
日志级别
使用 printf 记录
自定义日志功能
添加日志
任务和线程
LVGL文档中文网站
系统总览
应用 程序 创建 GUI 并处理特定任务的应用程序。
LVGL 图形库本身。您的应用程序可以与库通信以创建 GUI。它包含一个 HAL(硬件抽象层)接口来注册您的显示和输入设备驱动程序。
驱动程序 除了您的特定驱动程序之外,它还包含驱动您的显示器的功能,可选择驱动到 GPU 并读取触摸板或按钮。
根据 MCU 的不同,有两种典型的硬件设置。一个带有内置 LCD/TFT 驱动器外围,另一个没有它。在这两种情况下,都需要一个帧缓冲区来存储屏幕的当前图像。
带TFT/LCD 驱动 的MCU 如果您的MCU 有TFT/LCD 驱动外围,那么您可以通过RGB 接口直接连接显示器。在这种情况下,帧缓冲区可以位于内部 RAM(如果 MCU 有足够的 RAM)或外部 RAM(如果 MCU 有存储器接口)中。
外部显示控制器 如果 MCU 没有 TFT/LCD 驱动器接口,则必须使用外部显示控制器(例如 SSD1963、SSD1306、ILI9341)。在这种情况下,MCU 可以通过并行端口、SPI 或 I2C 与显示控制器通信。帧缓冲区通常位于显示控制器中,可为 MCU 节省大量 RAM。
设置项目
获取图书馆
LVGL 图形库可在 GitHub 上获得:https : //github.com/lvgl/lvgl。
您可以克隆它或从 GitHub 下载最新版本的库。
图形库是lvgl目录,应复制到您的项目中。
配置文件
有一个名为lv_conf.h 的 LVGL配置头文件。它设置库的基本行为,禁用未使用的模块和功能,在编译时调整内存缓冲区的大小等。
将lvgl/lv_conf_template.h复制到lvgl目录旁边,并将其重命名为lv_conf.h。打开文件并将开头更改为以启用其内容。
#if 0
#if 1
lv_conf.h也可以复制到其他地方,但是您应该将
LV_CONF_INCLUDE_SIMPLE
定义添加到您的编译器选项(例如-DLV_CONF_INCLUDE_SIMPLE
对于 gcc 编译器)并手动设置包含路径。在配置文件的注释中解释了选项的含义。至少检查这三个配置选项并根据您的硬件进行修改:
LV_HOR_RES_MAX显示器的水平分辨率。
LV_VER_RES_MAX显示器的垂直分辨率。
LV_COLOR_DEPTH 8 表示(RG332),16 表示(RGB565)或 32 表示(RGB888 和 ARGB8888)。
初始化
要使用图形库,您必须初始化它和其他组件。初始化的顺序是:
调用lv_init()。
初始化您的驱动程序。
在 LVGL 中注册显示和输入设备驱动程序。有关显示和输入设备注册的更多信息。
lv_tick_inc(x)
在x
中断中每毫秒调用一次以告诉经过的时间。了解更多。lv_task_handler()
每隔几毫秒定期调用以处理 LVGL 相关任务。了解更多。显示界面
要设置显示的
lv_disp_buf_t
和lv_disp_drv_t
变量的初始化。lv_disp_buf_t包含内部图形缓冲区。
lv_disp_drv_t包含回调函数来与显示交互和操作绘图相关的东西。
显示缓冲区
lv_disp_buf_t
可以这样初始化:/*A static or global variable to store the buffers*/static lv_disp_buf_t disp_buf;/*Static or global buffer(s). The second buffer is optional*/static lv_color_t buf_1[MY_DISP_HOR_RES * 10];static lv_color_t buf_2[MY_DISP_HOR_RES * 10];/*Initialize `disp_buf` with the buffer(s) */lv_disp_buf_init(&disp_buf, buf_1, buf_2, MY_DISP_HOR_RES*10);
关于缓冲区大小,有 3 种可能的配置:
一个缓冲区LVGL 将屏幕内容绘制到一个缓冲区中并将其发送到显示器。缓冲区可以小于屏幕。在这种情况下,较大的区域将在多个部分中重新绘制。如果只有小区域发生变化(例如按下按钮),则只会刷新这些区域。
具有两个缓冲区的两个非屏幕大小的缓冲区LVGL 可以将其绘制到一个缓冲区中,而将另一个缓冲区的内容发送到后台显示。应该使用DMA或其他硬件将数据传输到显示器,让CPU同时绘制。这样,显示的渲染和刷新变得并行。与One buffer类似,如果缓冲区小于要刷新的区域,LVGL 将分块绘制显示内容。
两个屏幕大小的缓冲区。与两个非屏幕大小的缓冲区相比,LVGL 将始终提供整个屏幕的内容,而不仅仅是块。通过这种方式,驱动程序可以简单地将帧缓冲区的地址更改为从 LVGL 接收到的缓冲区。因此,当 MCU 具有 LCD/TFT 接口且帧缓冲区只是 RAM 中的一个位置时,此方法效果最佳。
您可以使用基准示例来衡量显示配置的性能。
显示驱动程序
一旦缓冲区初始化准备就绪,就需要初始化显示驱动程序。在最简单的情况下,只
lv_disp_drv_t
需要设置以下两个字段:指向初始化
lv_disp_buf_t
变量的缓冲区指针。flush_cb一个回调函数,用于将缓冲区的内容复制到显示器的特定区域。
lv_disp_flush_ready()
需要在冲洗准备好时调用。LVGL 可能会以多个块呈现屏幕,因此会flush_cb
多次调用。要查看哪个是渲染的最后一块,请使用lv_disp_flush_is_last()
.
有一些可选的数据字段:
hor_res 显示器的水平分辨率。(
LV_HOR_RES_MAX
默认来自lv_conf.h)。ver_res 显示器的垂直分辨率。(
LV_VER_RES_MAX
默认来自lv_conf.h)。color_chroma_key将在镀铬键控图像上绘制为透明的颜色。
LV_COLOR_TRANSP
默认情况下来自lv_conf.h)。user_data驱动程序的自定义用户数据。它的类型可以在 lv_conf.h 中修改。
抗锯齿使用抗锯齿(边缘平滑)。
LV_ANTIALIAS
默认来自lv_conf.h。旋转和sw_rotate请参阅下面的旋转部分。
screen_transp如果
1
屏幕可以具有透明或不透明样式。LV_COLOR_SCREEN_TRANSP
需要在lv_conf.h 中启用。
要使用 GPU,可以使用以下回调:
gpu_fill_cb用颜色填充内存区域。
gpu_blend_cb使用不透明度混合两个内存缓冲区。
gpu_wait_cb如果有任何 GPU 函数返回,当 GPU 仍在 LVGL 工作时,将在需要时使用此函数,确保 GPU 渲染已准备就绪。
请注意,这些函数需要直接绘制到内存 (RAM) 而不是您的显示器。
一些其他可选的回调,使处理单色、灰度或其他非标准 RGB 显示器更容易、更优化:
rounder_cb四舍五入要重绘的区域的坐标。例如,2x2 像素可以转换为 2x8。如果显示控制器只能刷新具有特定高度或宽度的区域(单色显示器通常为 8 像素高度),则可以使用它。
set_px_cb一个自定义函数来写入显示缓冲区。如果显示器具有特殊的颜色格式,它可用于更紧凑地存储像素。(例如 1 位单色、2 位灰度等)这样使用的缓冲区
lv_disp_buf_t
可以更小,以仅容纳给定区域大小所需的位数。set_px_cb
不适用于显示缓冲区配置。Two screen-sized buffers
monitor_cb一个回调函数告诉多少像素在多少时间内被刷新。
clean_dcache_cb用于清除与显示相关的任何缓存的回调
要设置lv_disp_drv_t变量的字段,它需要用
lv_disp_drv_init(&disp_drv)
. 最后lv_disp_drv_register(&disp_drv)
需要调用为 LVGL 注册一个显示。放在一起看起来像这样:
lv_disp_drv_t disp_drv; /*A variable to hold the drivers. Can be local variable*/lv_disp_drv_init(&disp_drv); /*Basic initialization*/disp_drv.buffer = &disp_buf; /*Set an initialized buffer*/disp_drv.flush_cb = my_flush_cb; /*Set a flush callback to draw to the display*/lv_disp_t * disp;disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/
这里有一些简单的回调示例:
void my_flush_cb(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, y;for(y = area->y1; y <= area->y2; y++) {for(x = area->x1; x <= area->x2; x++) {put_px(x, y, *color_p)color_p++;}}/* IMPORTANT!!!* Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv); }void my_gpu_fill_cb(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, const lv_area_t * dest_area, const lv_area_t * fill_area, lv_color_t color); {/*It's an example code which should be done by your GPU*/uint32_t x, y;dest_buf += dest_width * fill_area->y1; /*Go to the first line*/for(y = fill_area->y1; y < fill_area->y2; y++) {for(x = fill_area->x1; x < fill_area->x2; x++) {dest_buf[x] = color;}dest_buf+=dest_width; /*Go to the next line*/} }void my_gpu_blend_cb(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa) {/*It's an example code which should be done by your GPU*/uint32_t i;for(i = 0; i < length; i++) {dest[i] = lv_color_mix(dest[i], src[i], opa);} }void my_rounder_cb(lv_disp_drv_t * disp_drv, lv_area_t * area) {/* Update the areas as needed. Can be only larger.* For example to always have lines 8 px height:*/area->y1 = area->y1 & 0x07;area->y2 = (area->y2 & 0x07) + 8; }void my_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) {/* Write to the buffer as required for the display.* Write only 1-bit for monochrome displays mapped vertically:*/buf += buf_w * (y >> 3) + x;if(lv_color_brightness(color) > 128) (*buf) |= (1 << (y % 8));else (*buf) &= ~(1 << (y % 8)); }void my_monitor_cb(lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px) {printf("%d px refreshed in %d ms\n", time, ms); }void my_clean_dcache_cb(lv_disp_drv_t * disp_drv, uint32) {/* Example for Cortex-M (CMSIS) */SCB_CleanInvalidateDCache(); }
回转
LVGL 支持以 90 度为增量旋转显示器。您可以选择是要软件轮换还是硬件轮换。
如果您选择软件旋转(
sw_rotate
标志设置为 1),LVGL 将为您执行旋转。您的驱动程序可以并且应该假设屏幕宽度和高度没有改变。只需像往常一样将像素刷新到显示器即可。软件轮换在您的flush_cb
回调中不需要额外的逻辑。在软件中执行轮换需要大量的开销,这就是硬件轮换也可用的原因。在这种模式下,LVGL 将绘制到缓冲区中,就好像您的屏幕现在具有反转的宽度和高度一样。您有责任自己旋转提供的像素。
初始化时显示的默认旋转可以使用
rotated
标志设置。可用选项为LV_DISP_ROT_NONE
,LV_DISP_ROT_90
,LV_DISP_ROT_180
,或LV_DISP_ROT_270
。旋转值与顺时针方向旋转物理显示器的方式有关。因此,LV_DISP_ROT_90
意味着您将硬件顺时针旋转 90 度,显示器逆时针旋转 90 度以进行补偿。(从 7.10.0 及更早版本升级的用户请注意:这些新的旋转枚举值与旧的 0/1 系统匹配,用于旋转 90 度,因此遗留代码应继续按预期工作。默认情况下,软件旋转也被禁用以实现兼容性.)
也可以在运行时使用API更改显示旋转。
lv_disp_set_rotation(disp, rot)
支持软件轮换是一项新功能,因此根据您的配置可能存在一些故障/错误。如果您遇到问题,请在GitHub 上打开一个问题。
应用程序接口
显示驱动 HAL 接口头文件
类型定义
类型定义结构_disp_drv_t
lv_disp_drv_t
HAL 要注册的显示驱动程序结构
类型定义结构_disp_t
lv_disp_t
显示结构。
笔记
lv_disp_drv_t
应该是结构的第一个成员。枚举
枚举
lv_disp_size_t
价值观:
枚举器
LV_DISP_SIZE_SMALL
枚举器
LV_DISP_SIZE_MEDIUM
枚举器
LV_DISP_SIZE_LARGE
枚举器
LV_DISP_SIZE_EXTRA_LARGE
职能
无效
lv_disp_drv_init
(lv_disp_drv_t *驱动程序)使用默认值初始化显示驱动程序。它用于在字段中具有已知值而不是内存中的垃圾。之后,您可以安全地只设置您需要的字段。
参数
驱动程序——指向要初始化的驱动程序变量的指针
void
lv_disp_buf_init
( lv_disp_buf_t * disp_buf , void * buf1 , void * buf2 , uint32_t size_in_px_cnt )初始化显示缓冲区
参数
disp_buf --
lv_disp_buf_t
要初始化的指针变量buf1 -- LVGL 用来绘制图像的缓冲区。始终必须指定且不能为 NULL。可以是用户分配的数组。例如或外部 SRAM 中的存储器地址
static lv_color_t disp_buf1[1024 * 10]
buf2 -- 可选地指定第二个缓冲区,以使图像渲染和图像刷新(发送到显示器)并行。在这种情况下,
disp_drv->flush
您应该使用 DMA 或类似的硬件将图像发送到后台的显示器。它允许 LVGL 在发送前一帧时将下一帧渲染到另一个缓冲区中。NULL
如果未使用,请设置为。size_in_px_cnt -尺寸
buf1
和buf2
像素数。
lv_disp_t *
lv_disp_drv_register
( lv_disp_drv_t *驱动程序)注册一个初始化的显示驱动程序。自动将第一个显示设置为活动。
参数
驱动程序——指向初始化的“lv_disp_drv_t”变量的指针(可以是局部变量)
退货
指向新显示的指针或出现错误时为 NULL
void
lv_disp_drv_update
( lv_disp_t * disp , lv_disp_drv_t * new_drv )在运行时更新驱动程序。
参数
disp -- 指向显示的指针。( 的返回值
lv_disp_drv_register
)new_drv -- 指向新驱动程序的指针
无效
lv_disp_remove
(lv_disp_t * disp)移除显示器
参数
disp -- 显示指针
无效
lv_disp_set_default
(lv_disp_t * disp)设置默认屏幕。默认情况下,将在其上创建新屏幕。
参数
disp -- 指向显示的指针
lv_disp_t *
lv_disp_get_default
(无效)获取默认显示
退货
指向默认显示的指针
lv_coord_t
lv_disp_get_hor_res
( lv_disp_t * disp )获取显示器的水平分辨率
参数
disp -- 指向显示的指针(NULL 使用默认显示)
退货
显示器的水平分辨率
lv_coord_t
lv_disp_get_ver_res
( lv_disp_t * disp )获取显示器的垂直分辨率
参数
disp -- 指向显示的指针(NULL 使用默认显示)
退货
显示器的垂直分辨率
bool
lv_disp_get_antialiasing
( lv_disp_t * disp )获取是否为显示器启用抗锯齿
参数
disp -- 指向显示的指针(NULL 使用默认显示)
退货
true:启用抗锯齿;假:禁用
lv_coord_t
lv_disp_get_dpi
( lv_disp_t * disp )获取显示器的 DPI
参数
disp -- 指向显示的指针(NULL 使用默认显示)
退货
显示器的 dpi
lv_disp_size_t
lv_disp_get_size_category
( lv_disp_t * disp )根据它的 hor 获取显示器的尺寸类别。资源 和 dpi。
参数
disp -- 指向显示的指针(NULL 使用默认显示)
退货
LV_DISP_SIZE_SMALL/MEDIUM/LARGE/EXTRA_LARGE
lv_disp_t *
lv_disp_get_next
( lv_disp_t * disp )获取下一个显示。
参数
disp -- 指向当前显示的指针。NULL 进行初始化。
退货
下一个显示或 NULL 如果没有更多。参数为NULL时给出第一个显示
lv_disp_buf_t *
lv_disp_get_buf
( lv_disp_t * disp )获取显示器的内部缓冲区
参数
disp -- 指向显示的指针
退货
指向内部缓冲区的指针
uint16_t
lv_disp_get_inv_buf_size
( lv_disp_t * disp )获取缓冲区中的区域数
退货
无效区域数
void
_lv_disp_pop_from_inv_buf
( lv_disp_t * disp , uint16_t num )从缓冲区弹出(删除)最后一个 'num' 无效区域
参数
num -- 要删除的区域数
bool
lv_disp_is_double_buf
( lv_disp_t * disp )检查驱动程序的配置,如果是双缓冲(包括
buf1
和buf2
设置)参数
disp -- 指向要检查的显示的指针
退货
真:双缓冲;false:不是双缓冲
bool
lv_disp_is_true_double_buf
( lv_disp_t * disp )检查驱动程序的配置,如果是真正的双缓冲(两者
buf1
和buf2
设置,并size
为屏幕大小)参数
disp -- 指向要检查的显示的指针
退货
真:双缓冲;false:不是双缓冲
结构
lv_disp_buf_t
#include <lv_hal_disp.h>
用于保存显示缓冲区信息的结构。
公众会员
无效*
buf1
第一个显示缓冲区。
无效*
buf2
第二个显示缓冲区。
无效*
buf_act
uint32_t
size
lv_area_t
area
整数
flushing
整数
flushing_last
uint32_t
last_area
uint32_t
last_part
结构
_disp_drv_t
#include <lv_hal_disp.h>
HAL 要注册的显示驱动程序结构
公众会员
lv_coord_t
hor_res
水平分辨率。
lv_coord_t
ver_res
垂直分辨率。
lv_disp_buf_t *
buffer
指向用 初始化的缓冲区的指针
lv_disp_buf_init()
。LVGL 将使用这个缓冲区来绘制屏幕内容uint32_t
antialiasing
1:在此显示器上启用抗锯齿。
uint32_t
rotated
1:将显示屏旋转 90 度。
警告
不为你更新坐标!
uint32_t
screen_transp
如果屏幕没有纯色 (opa == LV_OPA_COVER) 背景,则处理。仅在需要时使用,因为它较慢。
uint32_t
dpi
显示器的 DPI(每英寸点数)。默认设置为
LV_DPI
fromlv_Conf.h
。无效( *
flush_cb
) (结构_disp_drv_t * disp_drv ,常量lv_area_t *面积,lv_color_t * color_p )强制性:将内部缓冲区 (VDB) 写入显示器。完成后必须调用“lv_disp_flush_ready()”
无效( *
rounder_cb
) (结构_disp_drv_t * disp_drv , lv_area_t * area )可选:扩展无效区域以匹配
y
单色显示器上的显示驱动程序要求,例如舍入到 8、16 ..)无效( *
set_px_cb
) (结构_disp_drv_t * disp_drv , uint8_t * buf , lv_coord_t buf_w , lv_coord_t x , lv_coord_t y , lv_color_t color , lv_opa_t opa )可选:根据显示器的特殊要求在缓冲区中设置一个像素 可用于 LittelvGL 不支持的颜色格式。例如 2 位 -> 4 灰度
笔记
比使用支持的颜色格式绘制要慢得多。
无效( *
monitor_cb
) (结构_disp_drv_t * disp_drv , uint32_t时间, uint32_t px )可选:在每个刷新周期后调用以告知渲染和刷新时间 + 刷新像素数
无效( *
wait_cb
) (结构_disp_drv_t * disp_drv )可选:在 lvgl 等待操作完成时定期调用。例如刷新或 GPU 用户可以在这里执行非常简单的任务或产生任务
无效( *
clean_dcache_cb
) (结构_disp_drv_t * disp_drv )可选:当 lvgl 需要任何影响渲染的 CPU 缓存被清理时调用
无效( *
gpu_wait_cb
) (结构_disp_drv_t * disp_drv )可选:在 gpu 工作时调用等待
无效( *
gpu_blend_cb
) (结构_disp_drv_t * disp_drv , lv_color_t * dest ,常量lv_color_t * src , uint32_t长度, lv_opa_t opa )可选:使用不透明度混合两个内存(仅限 GPU)
无效( *
gpu_fill_cb
) (结构_disp_drv_t * disp_drv , lv_color_t * dest_buf , lv_coord_t dest_width ,常量lv_area_t * fill_area , lv_color_t颜色)可选:用颜色填充内存(仅限 GPU)
lv_color_t
color_chroma_key
在 CHROMA_KEYED 图像上,此颜色将是透明的。
LV_COLOR_TRANSP
默认。(lv_conf.h)lv_disp_drv_user_data_t
user_data
自定义显示驱动程序用户数据
结构
_disp_t
#include <lv_hal_disp.h>
显示结构。
笔记
lv_disp_drv_t
应该是结构的第一个成员。公众会员
lv_disp_drv_t
driver
< 显示驱动程序 定期检查脏区并刷新它们的任务
lv_task_t *
refr_task
lv_ll_t
scr_ll
显示器的屏幕
结构_lv_obj_t *
act_scr
此显示器上的当前活动屏幕
结构_lv_obj_t *
prev_scr
上一个画面。在屏幕动画期间使用
结构_lv_obj_t *
scr_to_load
lv_scr_load_anim 中准备加载的画面
结构_lv_obj_t *
top_layer
看
lv_disp_get_layer_top
结构_lv_obj_t *
sys_layer
看
lv_disp_get_layer_sys
uint8_t
del_prev
1:当屏幕加载动画准备好时自动删除上一屏
lv_color_t
bg_color
屏幕透明时的默认显示颜色
常量无效*
bg_img
显示为墙纸的图像源
lv_opa_t
bg_opa
背景颜色或墙纸的不透明度
lv_area_t
inv_areas
[LV_INV_BUF_SIZE
]无效(标记为重绘)区域
uint8_t
inv_area_joined
[LV_INV_BUF_SIZE
]uint32_t
inv_p
uint32_t
last_activity_time
上次在此显示器上有活动
下一个 以前的输入设备接口
输入设备的类型
要设置输入设备
lv_indev_drv_t
,必须初始化一个变量:lv_indev_drv_t indev_drv; lv_indev_drv_init(&indev_drv); /*Basic initialization*/ indev_drv.type =... /*See below.*/ indev_drv.read_cb =... /*See below.*/ /*Register the driver in LVGL and save the created input device object*/ lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
类型可以是
LV_INDEV_TYPE_POINTER触摸板或鼠标
LV_INDEV_TYPE_KEYPAD键盘或小键盘
LV_INDEV_TYPE_ENCODER编码器,带左、右、推选项
LV_INDEV_TYPE_BUTTON外部按键按下屏幕
read_cb是一个函数指针,它将被定期调用以报告输入设备的当前状态。它还可以缓冲数据并
false
在没有更多数据要读取或true
缓冲区不为空时返回。访问输入设备以了解有关一般输入设备的更多信息。
触摸板、鼠标或任何指针
可以点击屏幕点的输入设备属于这一类。
indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = my_input_read;...bool my_input_read(lv_indev_drv_t * drv, lv_indev_data_t*data) {data->point.x = touchpad_x;data->point.y = touchpad_y;data->state = LV_INDEV_STATE_PR or LV_INDEV_STATE_REL;return false; /*No buffering now so no more data read*/ }
重要的
即使状态为LV_INDEV_STATE_REL ,触摸板驱动程序也必须返回最后的 X/Y 坐标。
要设置鼠标光标,请使用. (是 的返回值)
lv_indev_set_cursor(my_indev, &img_cursor)
my_indev
lv_indev_drv_register
键盘或键盘
带有所有字母的全键盘或带有几个导航按钮的简单键盘都属于这里。
要使用键盘/小键盘:
read_cb
用LV_INDEV_TYPE_KEYPAD
类型注册一个函数。LV_USE_GROUP
在lv_conf.h 中启用必须创建一个对象组: 并且必须将对象添加到其中
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
可用的键。
indev_drv.type = LV_INDEV_TYPE_KEYPAD; indev_drv.read_cb = keyboard_read;...bool keyboard_read(lv_indev_drv_t * drv, lv_indev_data_t*data){data->key = last_key(); /*Get the last pressed or released key*/if(key_pressed()) data->state = LV_INDEV_STATE_PR;else data->state = LV_INDEV_STATE_REL;return false; /*No buffering now so no more data read*/ }
编码器
使用编码器,您可以做 4 件事:
按下它的按钮
长按它的按钮
转左
右转
简而言之,编码器输入设备的工作方式如下:
通过转动编码器,您可以专注于下一个/上一个对象。
当您在一个简单的对象(如按钮)上按下编码器时,它将被点击。
如果您按下复杂对象(如列表、消息框等)上的编码器,该对象将进入编辑模式,从而转动编码器您可以在对象内部导航。
要退出编辑模式,请长按按钮。
要使用编码器(类似于键盘),应将对象添加到组中。
indev_drv.type = LV_INDEV_TYPE_ENCODER; indev_drv.read_cb = encoder_read;...bool encoder_read(lv_indev_drv_t * drv, lv_indev_data_t*data){data->enc_diff = enc_get_new_moves();if(enc_pressed()) data->state = LV_INDEV_STATE_PR;else data->state = LV_INDEV_STATE_REL;return false; /*No buffering now so no more data read*/ }
使用带有编码器逻辑的按钮
除了标准编码器行为之外,您还可以利用其逻辑来使用按钮导航(聚焦)和编辑小部件。如果您只有几个按钮可用,或者您想使用除编码轮之外的其他按钮,这将特别方便。
您需要有 3 个按钮可用:
LV_KEY_ENTER将模拟按下或按下编码器按钮
LV_KEY_LEFT将模拟向左转动编码器
LV_KEY_RIGHT将模拟右转编码器
其他键将传递给聚焦的小部件
如果您按住这些键,它将模拟编码器单击,并在 中指定周期
indev_drv.long_press_rep_time
。indev_drv.type = LV_INDEV_TYPE_ENCODER; indev_drv.read_cb = encoder_with_keys_read;...bool encoder_with_keys_read(lv_indev_drv_t * drv, lv_indev_data_t*data){data->key = last_key(); /*Get the last pressed or released key*//* use LV_KEY_ENTER for encoder press */if(key_pressed()) data->state = LV_INDEV_STATE_PR;else {data->state = LV_INDEV_STATE_REL;/* Optionally you can also use enc_diff, if you have encoder*/data->enc_diff = enc_get_new_moves();}return false; /*No buffering now so no more data read*/ }
按钮
按钮是指屏幕旁边的外部“硬件”按钮,它们分配给屏幕的特定坐标。如果按下按钮,它将模拟按下指定坐标。(类似于触摸板)
要将按钮分配给坐标,请使用. 应该看起来像
lv_indev_set_button_points(my_indev, points_array)
points_array
const lv_point_t points_array[] = { {12,30},{60,90}, ...}
重要的
points_array 不能超出范围。将其声明为全局变量或函数内的静态变量。
indev_drv.type = LV_INDEV_TYPE_BUTTON; indev_drv.read_cb = button_read;...bool button_read(lv_indev_drv_t * drv, lv_indev_data_t*data){static uint32_t last_btn = 0; /*Store the last pressed button*/int btn_pr = my_btn_read(); /*Get the ID (0,1,2...) of the pressed button*/if(btn_pr >= 0) { /*Is there a button press? (E.g. -1 indicated no button was pressed)*/last_btn = btn_pr; /*Save the ID of the pressed button*/data->state = LV_INDEV_STATE_PR; /*Set the pressed state*/} else {data->state = LV_INDEV_STATE_REL; /*Set the released state*/}data->btn = last_btn; /*Save the last button*/return false; /*No buffering now so no more data read*/ }
其他特性
另外
read_cb
一个feedback_cb
回调中可以同时指定lv_indev_drv_t
。feedback_cb
当输入设备发送任何类型的事件时调用。(与其类型无关)。它允许为用户提供反馈,例如在 上播放声音LV_EVENT_CLICK
。以下参数的默认值可以在lv_conf.h 中设置,但默认值可以在
lv_indev_drv_t
.drag_limit在实际拖动对象之前要滑动的像素数
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()
)的显示中。相关的显示被存储并且可以disp
在驱动程序的字段中更改。应用程序接口
输入设备 HAL 接口层头文件
类型定义
类型定义uint8_t
lv_indev_type_t
类型定义uint8_t
lv_indev_state_t
类型定义uint8_t
lv_drag_dir_t
类型定义uint8_t
lv_gesture_dir_t
类型定义结构_lv_indev_drv_t
lv_indev_drv_t
由用户初始化并由 'lv_indev_add()' 注册
类型定义结构_lv_indev_proc_t
lv_indev_proc_t
输入设备的运行时数据 由库内部使用,您不需要触摸它。
类型定义结构_lv_indev_t
lv_indev_t
带有驱动程序、运行时数据 ('proc') 和一些附加信息的主要输入设备描述符
枚举
枚举[匿名的]
可能的输入设备类型
价值观:
枚举器
LV_INDEV_TYPE_NONE
未初始化状态
枚举器
LV_INDEV_TYPE_POINTER
触摸板、鼠标、外部按钮
枚举器
LV_INDEV_TYPE_KEYPAD
键盘或键盘
枚举器
LV_INDEV_TYPE_BUTTON
分配给屏幕特定点的外部(硬件按钮)
枚举器
LV_INDEV_TYPE_ENCODER
只有左转、右转和一个按钮的编码器
枚举[匿名的]
输入设备的状态
价值观:
枚举器
LV_INDEV_STATE_REL
枚举器
LV_INDEV_STATE_PR
枚举[匿名的]
价值观:
枚举器
LV_DRAG_DIR_HOR
对象可以水平拖动。
枚举器
LV_DRAG_DIR_VER
对象可以垂直拖动。
枚举器
LV_DRAG_DIR_BOTH
可以向各个方向拖动对象。
枚举器
LV_DRAG_DIR_ONE
对象只能拖动一个方向(第一次移动)。
枚举[匿名的]
价值观:
枚举器
LV_GESTURE_DIR_TOP
手势向上。
枚举器
LV_GESTURE_DIR_BOTTOM
手势向下。
枚举器
LV_GESTURE_DIR_LEFT
手势方向离开。
枚举器
LV_GESTURE_DIR_RIGHT
手势正确。
职能
无效
lv_indev_drv_init
(lv_indev_drv_t *驱动程序)使用默认值初始化输入设备驱动程序。它用于在字段 ant 中具有已知值而不是内存垃圾。之后,您可以设置字段。
参数
驱动程序——指向要初始化的驱动程序变量的指针
lv_indev_t *
lv_indev_drv_register
( lv_indev_drv_t *驱动程序)注册一个初始化的输入设备驱动程序。
参数
驱动程序——指向初始化的“lv_indev_drv_t”变量的指针(可以是局部变量)
退货
指向新输入设备的指针或出现错误时为 NULL
void
lv_indev_drv_update
( lv_indev_t * indev , lv_indev_drv_t * new_drv )在运行时更新驱动程序。
参数
indev -- 指向输入设备的指针。( 的返回值
lv_indev_drv_register
)new_drv -- 指向新驱动程序的指针
lv_indev_t *
lv_indev_get_next
( lv_indev_t * indev )获取下一个输入设备。
参数
indev -- 指向当前输入设备的指针。NULL 进行初始化。
退货
下一个输入设备或 NULL 如果没有更多。参数为NULL时给出第一个输入设备
bool
_lv_indev_read
( lv_indev_t * indev , lv_indev_data_t *数据)从输入设备读取数据。
参数
indev -- 指向输入设备的指针
data -- 输入设备将其数据写入这里
退货
false:没有更多数据;true:有更多数据要读取(缓冲)
结构
lv_indev_data_t
#include <lv_hal_indev.h>
传递给输入驱动程序以填充的数据结构
公众会员
lv_point_t
point
对于 LV_INDEV_TYPE_POINTER 当前按下的点
uint32_t
key
对于 LV_INDEV_TYPE_KEYPAD 当前按下的键
uint32_t
btn_id
对于 LV_INDEV_TYPE_BUTTON 当前按下的按钮
int16_t
enc_diff
对于 LV_INDEV_TYPE_ENCODER 自上次读取以来的步数
lv_indev_state_t
state
LV_INDEV_STATE_REL 或 LV_INDEV_STATE_PR
结构
_lv_indev_drv_t
#include <lv_hal_indev.h>
由用户初始化并由 'lv_indev_add()' 注册
公众会员
lv_indev_type_t
type
< 输入设备类型 读取输入设备数据的函数指针。如果有更多数据要读取(缓冲),则返回“true”。大多数驱动程序可以安全地返回“false”
布尔( *
read_cb
) (结构_lv_indev_drv_t * indev_drv , lv_indev_data_t *数据)无效( *
feedback_cb
) (结构_lv_indev_drv_t * , uint8_t )在输入设备上发生操作时调用。第二个参数是事件来自
lv_event_t
lv_indev_drv_user_data_t
user_data
结构_disp_t *
disp
< 指向分配的显示任务的指针,以读取周期性读取输入设备
lv_task_t *
read_task
在实际拖动对象之前要滑动的像素数
uint8_t
drag_limit
拖投减速 [%]。更大的价值意味着更快的减速
uint8_t
drag_throw
至少这种差异应该在两点之间评估为手势
uint8_t
gesture_min_velocity
至少这个区别应该是发送手势
uint8_t
gesture_limit
长按时间(毫秒)
uint16_t
long_press_time
长按重复触发周期 [ms]
uint16_t
long_press_rep_time
结构
_lv_indev_proc_t
#include <lv_hal_indev.h>
输入设备的运行时数据 由库内部使用,您不需要触摸它。
公众会员
lv_indev_state_t
state
输入设备的当前状态。
lv_point_t
act_point
输入设备的当前点。
lv_point_t
last_point
输入设备的最后一点。
lv_point_t
vect
act_point
和之间的区别last_point
。lv_point_t
drag_sum
lv_point_t
drag_throw_vect
结构_lv_obj_t *
act_obj
结构_lv_obj_t *
last_obj
结构_lv_obj_t *
last_pressed
lv_gesture_dir_t
gesture_dir
lv_point_t
gesture_sum
uint8_t
drag_limit_out
uint8_t
drag_in_prog
lv_drag_dir_t
drag_dir
uint8_t
gesture_sent
结构_lv_indev_proc_t :: [匿名] :: [匿名]
pointer
lv_indev_state_t
last_state
uint32_t
last_key
结构_lv_indev_proc_t :: [匿名] :: [匿名]
keypad
联盟_lv_indev_proc_t :: [匿名]
types
uint32_t
pr_timestamp
按下的时间戳
uint32_t
longpr_rep_timestamp
长按重复时间戳
uint8_t
long_pr_sent
uint8_t
reset_query
uint8_t
disabled
uint8_t
wait_until_release
结构
_lv_indev_t
#include <lv_hal_indev.h>
带有驱动程序、运行时数据 ('proc') 和一些附加信息的主要输入设备描述符
公众会员
lv_indev_drv_t
driver
lv_indev_proc_t
proc
结构_lv_obj_t *
cursor
LV_INPUT_TYPE_POINTER 的光标
结构_lv_group_t *
group
键盘目标组
常量lv_point_t *
btn_points
分配给按钮 () 屏幕的阵列点将在此处被按钮按下
下一个 以前的时钟接口
LVGL 需要一个系统滴答来了解动画和其他任务所用的时间。
您需要
lv_tick_inc(tick_period)
定期调用该函数并以毫秒为单位告诉调用周期。例如,lv_tick_inc(1)
每毫秒调用一次。lv_tick_inc
应该在比lv_task_handler()
(例如在中断中)更高优先级的例程中调用以精确知道经过的毫秒数,即使执行lv_task_handler
需要更长的时间。使用 FreeRTOS
lv_tick_inc
可以在vApplicationTickHook
.在基于 Linux 的操作系统上(例如在 Raspberry Pi 上)
lv_tick_inc
可以在线程中调用,如下所示:void * tick_thread (void *args) {while(1) {usleep(5*1000); /*Sleep for 5 millisecond*/lv_tick_inc(5); /*Tell LVGL that 5 milliseconds were elapsed*/} }
应用程序接口
以 1 毫秒的分辨率提供对系统滴答的访问
功能
uint32_t
lv_tick_get
(无效)获取自启动以来经过的毫秒数
返回
经过的毫秒数
uint32_t
lv_tick_elaps
( uint32_t prev_tick )获取自上一个时间戳以来经过的毫秒数
参数
prev_tick -- 前一个时间戳(lv_tick_get() 的返回值)
返回
自“prev_tick”以来经过的毫秒数
下一个 以前的任务处理程序
要处理 LVGL 的任务,您需要
lv_task_handler()
定期调用以下方法之一:
- while(1) of main() function
- timer interrupt periodically (low priority then
lv_tick_inc()
) - an OS task periodically
时间并不重要,但它应该是大约 5 毫秒以保持系统响应。
例子:
while(1) {lv_task_handler();my_delay_ms(5); }
要了解有关任务的更多信息,请访问任务部分。
睡眠管理
当没有用户输入发生时,MCU 可以进入睡眠状态。在这种情况下,主要内容
while(1)
应如下所示:while(1) {/*Normal operation (no sleep) in < 1 sec inactivity*/if(lv_disp_get_inactive_time(NULL) < 1000) {lv_task_handler();}/*Sleep after 1 sec inactivity*/else {timer_stop(); /*Stop the timer where lv_tick_inc() is called*/sleep(); /*Sleep the MCU*/}my_delay_ms(5); }
如果发生唤醒(按下、触摸或点击等),您还应该在输入设备读取功能中添加以下几行:
lv_tick_inc(LV_DISP_DEF_REFR_PERIOD); /*Force task execution on wake-up*/ timer_start(); /*Restart the timer where lv_tick_inc() is called*/ lv_task_handler(); /*Call `lv_task_handler()` manually to process the wake-up event*/
除此之外,
lv_disp_get_inactive_time()
您还可以检查lv_anim_count_running()
是否每个动画都完成了。操作系统和中断
默认情况下,LVGL不是线程安全的。
但是,在以下情况下调用 LVGL 相关函数是有效的:
在事件中。在活动中了解更多信息。
在lv_tasks 中。在任务中了解更多信息。
中断
尽量避免从中断中调用 LVGL 函数(除了
lv_tick_inc()
和lv_disp_flush_ready()
)。但是,如果您需要这样做,您必须禁用在lv_task_handler
运行时使用 LVGL 函数的中断。设置标志或某个值并定期在lv_task
.日志记录
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_LOG_PRINTF
在lv_conf.h 中启用以发送带有printf
.自定义日志功能
如果您无法使用
printf
或想使用自定义函数进行日志记录,您可以使用 .log 注册一个“记录器”回调lv_log_register_print_cb()
。例如:
void my_log_cb(lv_log_level_t level, const char * file, uint32_t line, const char * fn_name, const char * dsc) {/*Send the logs via serial port*/if(level == LV_LOG_LEVEL_ERROR) serial_send("ERROR: ");if(level == LV_LOG_LEVEL_WARN) serial_send("WARNING: ");if(level == LV_LOG_LEVEL_INFO) serial_send("INFO: ");if(level == LV_LOG_LEVEL_TRACE) serial_send("TRACE: ");serial_send("File: ");serial_send(file);char line_str[8];sprintf(line_str,"%d", line);serial_send("#");serial_send(line_str);serial_send(": ");serial_send(fn_name);serial_send(": ");serial_send(dsc);serial_send("\n"); }...lv_log_register_print_cb(my_log_cb);
添加日志
您还可以通过
LV_LOG_TRACE/INFO/WARN/ERROR(description)
函数使用日志模块。任务和线程
如果你需要使用真正的任务或线程,你需要一个互斥锁,它应该在调用之前调用
lv_task_handler
并在调用之后释放。此外,您必须在其他任务和线程中围绕每个 LVGL (lv_...
) 相关函数调用和代码使用相同的互斥锁。这样你就可以在真正的多任务环境中使用 LVGL。只需使用互斥锁来避免并发调用 LVGL 函数。
LVGL | LVGL移植之中文文档相关推荐
- 基于arduino的ESP32 学习笔记(六)LVGL文件系统移植,中文字库和图片显示
前言 本文的目的是为了给将要制作的ESP32手环做技术储备 记录移植LVGL文件系统,制作LVGL中文字库,将图片和字库放在SD卡里,通过移植好的LVGL文件系统,读取SD卡中的中文字库和图片,并显示 ...
- LVGL v7移植到ARM平台
本次移植的是LVGL v7.110 硬件平台:野火IMX6ULL 参考博客: 实践分享 | 基于framebuffer的lvgl的移植使用_嵌入式大杂烩-CSDN博客 ⭐建立一个lvgl项目 - 百问 ...
- HLK-W801开发-LVGL图像库移植,并驱动GC9A01圆形屏幕
移植后的效果如上图所示,采用的LVGL版本是8.2,接下来是详细的移植过程. 项目来源简单介绍 某天在某宝瞎逛时,突然发现一款单片机,最小系统板只有9.9,这不正好戳中老夫穷逼的心,想到就现在就一块F ...
- LVGL|lvgl教程之修改lvgl tabview部件顶部框(选项卡)的默认样式
效果预览 前言 lvgl提供了非常多的部件(30多个)给用户使用,这些部件的所有样式都是可以修改的.它们都有默认的样式,但是当我们觉得默认的样式不合适自己项目的时候可以就需要进行修改了,本文来教大家怎 ...
- React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念
React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念 React 入门系列教程导航 React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念 Rea ...
- BeautifulSoup4 模块中文文档
原文出处 -> Beautiful Soup Documentation 目录 一.前言 1.1 寻求帮助 二.快速开始 三.安装 BeautifulSoup 3.1 安装完成后的问题 3.2 ...
- OpenGIS中文文档
OpenGIS中文文档 OpenGIS(Open Geodata Interoperation Specification,开放地理数据互操作规范) 2.1. 前言 o2.1.1. 什么是开放GIS ...
- Bootstrap 一篇就够 快速入门使用(中文文档)
目录 一.Bootstrap 简介 什么是 Bootstrap? 历史 为什么使用 Bootstrap? Bootstrap 包的内容 在线实例 Bootstrap 实例 更多实例 Bootstrap ...
- Tomcat7中文文档
2019独角兽企业重金招聘Python工程师标准>>> Tomcat7中文文档 注:本文根据tomcat官网(tomcat.apache.org)tomcat7.0官方文档整 ...
最新文章
- POJ-1753 Flip Game 枚举 状态压缩
- 插件制作教程 php,typecho插件编写教程(二):写一个新插件
- shell编程【分发系统】
- 用python画xy散点图-python画时间序列散点图
- SAP RFC通信模式
- qq地区采集_用户诉QQ浏览器违法收集个人隐私,法院裁定腾讯立即停止相关行为...
- 数学家眼里的相同与不同
- 2008-2011大股东增持专题
- 图像识别算法_图像识别—MobileNets算法详解
- 虚拟光驱下载安装和使用,Windows系统如何直接打开iso文件
- 步道乐跑(最新版本)
- Latex 对号和叉号的
- C语言获取执行程序所在的目录路径
- ROC曲线和PR曲线
- 微信从原版到现在所有界面图片_微信这4张登录界面图,你见过几张?微信老用户都不一定能认全!...
- Linux网络连接命令
- 从零开始学Python爬虫系列:Matplotlib FuncAnimation这1个功能,加1个更新函数,画出最简单的动图,让你爬取的数据动起来。(附:图片上加弹幕功能)
- Something went wrong while dowloading dependencies could not open caffe-builder-config.cmake
- 面向对象,类,属性,方法,创建调用属性方法,有参,无参,变量代码示例
- 这14种嵌入式实时系统,你用过哪些?