目录

系统总览

设置项目

获取图书馆

配置文件

初始化

显示界面

显示缓冲区

显示驱动程序

回转

应用程序接口

输入设备接口

输入设备的类型

触摸板、鼠标或任何指针

键盘或键盘

编码器

按钮

其他特性

应用程序接口

时钟接口

应用程序接口

任务处理程序

睡眠管理

操作系统和中断

中断

日志记录

日志级别

使用 printf 记录

自定义日志功能

添加日志

任务和线程


LVGL文档中文网站

系统总览

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

LVGL 图形库本身。您的应用程序可以与库通信以创建 GUI。它包含一个 HAL(硬件抽象层)接口来注册您的显示和输入设备驱动程序。

驱动程序 除了您的特定驱动程序之外,它还包含驱动您的显示器的功能,可选择驱动到 GPU 并读取触摸板或按钮。


根据 MCU 的不同,有两种典型的硬件设置。一个带有内置 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。

    设置项目

    获取图书馆

    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 编译器)并手动设置包含路径。

    在配置文件的注释中解释了选项的含义。至少检查这三个配置选项并根据您的硬件进行修改:

    1. LV_HOR_RES_MAX显示器的水平分辨率。

    2. LV_VER_RES_MAX显示器的垂直分辨率。

    3. LV_COLOR_DEPTH 8 表示(RG332),16 表示(RGB565)或 32 表示(RGB888 和 ARGB8888)。

    初始化

    要使用图形库,您必须初始化它和其他组件。初始化的顺序是:

    1. 调用lv_init()

    2. 初始化您的驱动程序。

    3. 在 LVGL 中注册显示和输入设备驱动程序。有关显示和输入设备注册的更多信息。

    4. lv_tick_inc(x)x中断中每毫秒调用一次以告诉经过的时间。了解更多。

    5. lv_task_handler()每隔几毫秒定期调用以处理 LVGL 相关任务。了解更多。

      显示界面

      要设置显示的lv_disp_buf_tlv_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 种可能的配置:

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

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

      3. 两个屏幕大小的缓冲区。与两个非屏幕大小的缓冲区相比,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_NONELV_DISP_ROT_90LV_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_tlv_disp_drv_t

      HAL 要注册的显示驱动程序结构

      类型定义结构_disp_tlv_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 -尺寸buf1buf2像素数。

      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_tlv_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 )

      检查驱动程序的配置,如果是双缓冲(包括buf1buf2设置)

      参数

      disp -- 指向要检查的显示的指针

      退货

      真:双缓冲;false:不是双缓冲

      bool lv_disp_is_true_double_buf( lv_disp_t * disp )

      检查驱动程序的配置,如果是真正的双缓冲(两者buf1buf2设置,并size为屏幕大小)

      参数

      disp -- 指向要检查的显示的指针

      退货

      真:双缓冲;false:不是双缓冲

      结构lv_disp_buf_t

      #include <lv_hal_disp.h>

      用于保存显示缓冲区信息的结构。

      公众会员

      无效*buf1

      第一个显示缓冲区。

      无效*buf2

      第二个显示缓冲区。

      无效*buf_act

      uint32_tsize

      lv_area_tarea

      整数flushing

      整数flushing_last

      uint32_tlast_area

      uint32_tlast_part

      结构_disp_drv_t

      #include <lv_hal_disp.h>

      HAL 要注册的显示驱动程序结构

      公众会员

      lv_coord_thor_res

      水平分辨率。

      lv_coord_tver_res

      垂直分辨率。

      lv_disp_buf_t *buffer

      指向用 初始化的缓冲区的指针lv_disp_buf_init()。LVGL 将使用这个缓冲区来绘制屏幕内容

      uint32_tantialiasing

      1:在此显示器上启用抗锯齿。

      uint32_trotated

      1:将显示屏旋转 90 度。

      警告

      不为你更新坐标!

      uint32_tscreen_transp

      如果屏幕没有纯色 (opa == LV_OPA_COVER) 背景,则处理。仅在需要时使用,因为它较慢。

      uint32_tdpi

      显示器的 DPI(每英寸点数)。默认设置为LV_DPIfrom lv_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_tcolor_chroma_key

      在 CHROMA_KEYED 图像上,此颜色将是透明的。LV_COLOR_TRANSP默认。(lv_conf.h)

      lv_disp_drv_user_data_tuser_data

      自定义显示驱动程序用户数据

      结构_disp_t

      #include <lv_hal_disp.h>

      显示结构。

      笔记

      lv_disp_drv_t 应该是结构的第一个成员。

      公众会员

      lv_disp_drv_tdriver

      < 显示驱动程序 定期检查脏区并刷新它们的任务

      lv_task_t *refr_task

      lv_ll_tscr_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_tdel_prev

      1:当屏幕加载动画准备好时自动删除上一屏

      lv_color_tbg_color

      屏幕透明时的默认显示颜色

      常量无效*bg_img

      显示为墙纸的图像源

      lv_opa_tbg_opa

      背景颜色或墙纸的不透明度

      lv_area_t inv_areasLV_INV_BUF_SIZE]

      无效(标记为重绘)区域

      uint8_t inv_area_joinedLV_INV_BUF_SIZE]

      uint32_tinv_p

      uint32_tlast_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_indevlv_indev_drv_register

      键盘或键盘

      带有所有字母的全键盘或带有几个导航按钮的简单键盘都属于这里。

      要使用键盘/小键盘:

      • read_cbLV_INDEV_TYPE_KEYPAD类型注册一个函数。

      • LV_USE_GROUPlv_conf.h 中启用

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

      • 所创建的组必须被分配给一个输入设备:(是的返回值)lv_indev_set_group(my_indev, g)my_indevlv_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 件事:

      1. 按下它的按钮

      2. 长按它的按钮

      3. 转左

      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_arrayconst 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_tlv_indev_type_t

      类型定义uint8_tlv_indev_state_t

      类型定义uint8_tlv_drag_dir_t

      类型定义uint8_tlv_gesture_dir_t

      类型定义结构_lv_indev_drv_tlv_indev_drv_t

      由用户初始化并由 'lv_indev_add()' 注册

      类型定义结构_lv_indev_proc_tlv_indev_proc_t

      输入设备的运行时数据 由库内部使用,您不需要触摸它。

      类型定义结构_lv_indev_tlv_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_tpoint

      对于 LV_INDEV_TYPE_POINTER 当前按下的点

      uint32_tkey

      对于 LV_INDEV_TYPE_KEYPAD 当前按下的键

      uint32_tbtn_id

      对于 LV_INDEV_TYPE_BUTTON 当前按下的按钮

      int16_tenc_diff

      对于 LV_INDEV_TYPE_ENCODER 自上次读取以来的步数

      lv_indev_state_tstate

      LV_INDEV_STATE_REL 或 LV_INDEV_STATE_PR

      结构_lv_indev_drv_t

      #include <lv_hal_indev.h>

      由用户初始化并由 'lv_indev_add()' 注册

      公众会员

      lv_indev_type_ttype

      < 输入设备类型 读取输入设备数据的函数指针。如果有更多数据要读取(缓冲),则返回“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_tuser_data

      结构_disp_t *disp

      < 指向分配的显示任务的指针,以读取周期性读取输入设备

      lv_task_t *read_task

      在实际拖动对象之前要滑动的像素数

      uint8_tdrag_limit

      拖投减速 [%]。更大的价值意味着更快的减速

      uint8_tdrag_throw

      至少这种差异应该在两点之间评估为手势

      uint8_tgesture_min_velocity

      至少这个区别应该是发送手势

      uint8_tgesture_limit

      长按时间(毫秒)

      uint16_tlong_press_time

      长按重复触发周期 [ms]

      uint16_tlong_press_rep_time

      结构_lv_indev_proc_t

      #include <lv_hal_indev.h>

      输入设备的运行时数据 由库内部使用,您不需要触摸它。

      公众会员

      lv_indev_state_tstate

      输入设备的当前状态。

      lv_point_tact_point

      输入设备的当前点。

      lv_point_tlast_point

      输入设备的最后一点。

      lv_point_tvect

      act_point和之间的区别last_point

      lv_point_tdrag_sum

      lv_point_tdrag_throw_vect

      结构_lv_obj_t *act_obj

      结构_lv_obj_t *last_obj

      结构_lv_obj_t *last_pressed

      lv_gesture_dir_tgesture_dir

      lv_point_tgesture_sum

      uint8_tdrag_limit_out

      uint8_tdrag_in_prog

      lv_drag_dir_tdrag_dir

      uint8_tgesture_sent

      结构_lv_indev_proc_t :: [匿名] :: [匿名]pointer

      lv_indev_state_tlast_state

      uint32_tlast_key

      结构_lv_indev_proc_t :: [匿名] :: [匿名]keypad

      联盟_lv_indev_proc_t :: [匿名]types

      uint32_tpr_timestamp

      按下的时间戳

      uint32_tlongpr_rep_timestamp

      长按重复时间戳

      uint8_tlong_pr_sent

      uint8_treset_query

      uint8_tdisabled

      uint8_twait_until_release

      结构_lv_indev_t

      #include <lv_hal_indev.h>

      带有驱动程序、运行时数据 ('proc') 和一些附加信息的主要输入设备描述符

      公众会员

      lv_indev_drv_tdriver

      lv_indev_proc_tproc

      结构_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需要更长的时间。

      使用 FreeRTOSlv_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 毫秒以保持系统响应。

    1. 例子:

      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  1LV_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_PRINTFlv_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移植之中文文档相关推荐

  1. 基于arduino的ESP32 学习笔记(六)LVGL文件系统移植,中文字库和图片显示

    前言 本文的目的是为了给将要制作的ESP32手环做技术储备 记录移植LVGL文件系统,制作LVGL中文字库,将图片和字库放在SD卡里,通过移植好的LVGL文件系统,读取SD卡中的中文字库和图片,并显示 ...

  2. LVGL v7移植到ARM平台

    本次移植的是LVGL v7.110 硬件平台:野火IMX6ULL 参考博客: 实践分享 | 基于framebuffer的lvgl的移植使用_嵌入式大杂烩-CSDN博客 ⭐建立一个lvgl项目 - 百问 ...

  3. HLK-W801开发-LVGL图像库移植,并驱动GC9A01圆形屏幕

    移植后的效果如上图所示,采用的LVGL版本是8.2,接下来是详细的移植过程. 项目来源简单介绍 某天在某宝瞎逛时,突然发现一款单片机,最小系统板只有9.9,这不正好戳中老夫穷逼的心,想到就现在就一块F ...

  4. LVGL|lvgl教程之修改lvgl tabview部件顶部框(选项卡)的默认样式

    效果预览 前言 lvgl提供了非常多的部件(30多个)给用户使用,这些部件的所有样式都是可以修改的.它们都有默认的样式,但是当我们觉得默认的样式不合适自己项目的时候可以就需要进行修改了,本文来教大家怎 ...

  5. React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念

    React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念 React 入门系列教程导航 React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念 Rea ...

  6. BeautifulSoup4 模块中文文档

    原文出处 -> Beautiful Soup Documentation 目录 一.前言 1.1 寻求帮助 二.快速开始 三.安装 BeautifulSoup 3.1 安装完成后的问题 3.2 ...

  7. OpenGIS中文文档

    OpenGIS中文文档 OpenGIS(Open Geodata Interoperation Specification,开放地理数据互操作规范) 2.1. 前言 o2.1.1. 什么是开放GIS ...

  8. Bootstrap 一篇就够 快速入门使用(中文文档)

    目录 一.Bootstrap 简介 什么是 Bootstrap? 历史 为什么使用 Bootstrap? Bootstrap 包的内容 在线实例 Bootstrap 实例 更多实例 Bootstrap ...

  9. Tomcat7中文文档

    2019独角兽企业重金招聘Python工程师标准>>>   Tomcat7中文文档   注:本文根据tomcat官网(tomcat.apache.org)tomcat7.0官方文档整 ...

最新文章

  1. POJ-1753 Flip Game 枚举 状态压缩
  2. 插件制作教程 php,typecho插件编写教程(二):写一个新插件
  3. shell编程【分发系统】
  4. 用python画xy散点图-python画时间序列散点图
  5. SAP RFC通信模式
  6. qq地区采集_用户诉QQ浏览器违法收集个人隐私,法院裁定腾讯立即停止相关行为...
  7. 数学家眼里的相同与不同
  8. 2008-2011大股东增持专题
  9. 图像识别算法_图像识别—MobileNets算法详解
  10. 虚拟光驱下载安装和使用,Windows系统如何直接打开iso文件
  11. 步道乐跑(最新版本)
  12. Latex 对号和叉号的
  13. C语言获取执行程序所在的目录路径
  14. ROC曲线和PR曲线
  15. 微信从原版到现在所有界面图片_微信这4张登录界面图,你见过几张?微信老用户都不一定能认全!...
  16. Linux网络连接命令
  17. 从零开始学Python爬虫系列:Matplotlib FuncAnimation这1个功能,加1个更新函数,画出最简单的动图,让你爬取的数据动起来。(附:图片上加弹幕功能)
  18. Something went wrong while dowloading dependencies could not open caffe-builder-config.cmake
  19. 面向对象,类,属性,方法,创建调用属性方法,有参,无参,变量代码示例
  20. 这14种嵌入式实时系统,你用过哪些?

热门文章

  1. QList使用注意(浅拷贝 深拷贝)
  2. 大数据Hadoop课程进度
  3. 腾讯游戏扫码登录源码
  4. VS发生RC1107错误的原因
  5. 一道笔试题(求质数乘积)
  6. brew install mysql
  7. 深圳云计算培训:新手学习云计算的规划
  8. Codeforces Round #644 (Div. 3) E.Polygon
  9. zz:Android APP Monkey信息自动收集脚本
  10. 人工智能:通俗易懂理解深度学习与神经网络