草庐IT

OpenHarmony轻量化系统的LVGL使用(一)

X丶昕雪 2023-03-28 原文

想了解更多关于开源的内容,请访问:

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

概括

LVGL(Light and Versatile Graphics Library)是一个开源轻量级显示框架,支持多类型显示设备&&输入设备,作为一个开源图形库,自带了三十多种小组件供开发者使用。兼容低配置的嵌入式开发,可以以极低的配置要求完成丝滑动画。LVGL其强大的功能,使得它在物联网设备中应用比比皆是。

前一阵子开发了润和hispark_pegasus Hi3861开发板上的SSD1306,其提供的开发库开发起来还是较为困难,于是想尝试将LVGL移植到OpenHarmony轻量化系统上,便于后续OpenHarmony物联网设备的屏幕开发,大大降低屏幕开发的时间成本。

本文章将简单介绍LVGL的主要开发流程,OpenHarmony->LVGL的具体移植期待后续文章。

初始化

概括

LVGL的初始化主要流程为以下步骤:

  1. 调用lv_init()。
  2. 初始化驱动程序
  3. 初始化显示驱动和输入设备驱动程序。
  4. 设置一个定时器,每隔n(1-10)毫秒调用lv_tick_inc(n)以告知lvgl经过时间,该线程需要高于第五步的优先级。
  5. 主程序中每隔数毫秒(5ms)调用lv_timer_handler()处理LVGL界面(绘制显示界面,读取输入设备信息,展示动画等)。

初始化显示驱动

初始化lv_disp_draw_buf_t && lv_disp_drv_t这两个结构体然后对应初始化绘制缓冲区的内部图形缓冲区,显示驱动程序。

初始化缓存区

对于lv_disp_draw_buf_t:

/*一个静态或全局变量来存储缓冲区*/
static lv_disp_draw_buf_t disp_buf;

/*静态或全局缓冲区(s)。第二个缓冲区是可选的*/
static lv_color_t buf_1[MY_DISP_HOR_RES * 10];
static lv_color_t buf_2[MY_DISP_HOR_RES * 10];

/*初始化函数,第三变量可以为NULL */
lv_disp_draw_buf_init(&disp_buf, buf_1, buf_2, MY_DISP_HOR_RES*10);
更大的缓冲区能带来更高的性能,但超过1/10屏幕大小的缓冲区之后性能没有显著提升,因而建议稍微大于1/10屏幕大小的缓冲区即可.

只使用一个缓冲区,那么需要等待LVGL刷新了之后才能渲染下一帧.而使用到两个缓冲区后,LVGL可以同时进行刷新和渲染。

初始化显示驱动程序

​对于lv_disp_drv_t:

初始化中可以启用 ​full_refresh 以强制 LVGL 始终重绘整个屏幕。这适用于单/双缓冲模式。

如果启用该模式并提供 2 个屏幕大小的绘制缓冲区,LVGL 的显示处理就像“传统”双缓冲一样工作。

这意味着在 flush_cb​ 中只有帧缓冲区的地址需要更改为提供的指针(color_p 参数)。 如果 MCU 具有 LCD 控制器外围设备而不是外部显示控制器(例如 ILI9341 或 SSD1963),则应使用此配置。

初始化流程
对于上面的缓冲区初始化完成之后,可以初始化显示驱动结构体lv_disp_drv_t。

  1. 用lv_disp_drv_init(&disp_drv)初始化。
  2. 以下的变量必须初始化。
  1. 指向上一章显示缓存的指针的 draw_buf 。
  2. hor_res 显示器的水平分辨率(以像素为单位)。
  3. ver_res 显示器的垂直分辨率(以像素为单位)。
  4. flush_cb 一个回调函数,用于将缓冲区的内容复制到显示器的特定区域。
  1. 需要在LVGL中用lv_disp_drv_register(&disp_drv)注册。
lv_disp_flush_ready(&disp_drv) 需要在刷新准备好时调用。 LVGL 可能会以多个块呈现屏幕,因此多次调用 flush_cb。要查看当前是否是渲染的最后一个块,请使用 lv_disp_flush_is_last(&disp_drv)。

结构体其余成员
对于lv_disp_drv_t对象而言,还有一些可选择的成员可以修改:

  • color_chroma_key 将在镀铬键控图像上绘制为透明的颜色。从lv_conf.h默认设置为LV_COLOR_CHROMA_KEY。
  • anti_aliasing 使用抗锯齿(边缘平滑)。如果在 lv_conf.h 中将 LV_COLOR_DEPTH 设置为至少 16,则默认启用。
  • rotated 和 sw_rotate 硬件旋转屏幕和软件旋转屏幕.sw_rotate 置为1时执行旋转.
  • screen_transp 如果 1 屏幕本身也可以具有透明度。 LV_COLOR_SCREEN_TRANSP 需要在 lv_conf.h 中启用并且需要 LV_COLOR_DEPTH 32。
  • user_data 驱动程序的自定义void 用户数据…
  • rounder_cb 四舍五入要重绘的区域的坐标。例如。 2x2 px 可以转换为 2x8。 如果显示控制器只能刷新具有特定高度或宽度的区域(单色显示器通常为 8 像素高度),则可以使用它。
  • set_px_cb 一个自定义函数来写入绘制缓冲区。如果显示器具有特殊的颜色格式,它可用于将像素更紧凑地存储在绘图缓冲区中。 (例如 1 位单色、2 位灰度等) 这样,lv_disp_draw_buf_t 中使用的缓冲区可以更小,以仅容纳给定区域大小所需的位数。请注意,使用set_px_cb 渲染比普通渲染慢。
  • monitor_cb 一个回调函数,告诉我们在多长时间内刷新了多少像素。当最后一个块被渲染并发送到显示器时调用。
  • clean_dcache_cb 用于清理与显示相关的任何缓存的回调。
样例
static lv_disp_drv_t disp_drv; //静态的一个显示驱动结构体
lv_disp_drv_init(&disp_drv); //初始化结构体
disp_drv.draw_buf = &disp_buf; //指向上一章显示缓存的指针
disp_drv.flush_cb = my_flush_cb; //回调函数,将缓冲区的内容复制到显示器的特定区域
disp_drv.hor_res = 320; //水平像素
disp_drv.ver_res = 240; //垂直像素

lv_disp_t * disp; //保存返回值
disp = lv_disp_drv_register(&disp_drv); //注册结构体

//回调函数
void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
//最简单的情况下(但也最慢)
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++;
}
}
//通知LVGL准备刷新
lv_disp_flush_ready(disp_drv);
}

初始化输入设备驱动

对于输入设备,首先要区分是何种类型输入设备,不同输入设备都有不同的初始化驱动API

LVGL将输入设备分成了四种类型:

  • LV_INDEV_TYPE_POINTER 触摸板或鼠标。
  • LV_INDEV_TYPE_KEYPAD 键盘。
  • LV_INDEV_TYPE_ENCODER 编码器,带有左/右转和推动选项。
  • LV_INDEV_TYPE_BUTTON 外部按钮。

初始化输入设备结构体lv_indev_drv_t

lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); //初始化结构体
indev_drv.type =LV_INDEV_TYPE_POINTER; //输入设备类型,当前为鼠标
indev_drv.read_cb =... //回调函数,用于定期(几乎实时)获取输入设备的数据
lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv); //注册输入设备

触摸板,鼠标

对于触摸板,鼠标等指针类的输入设备,他们的回调函数可以如下设置。

void my_input_read(lv_indev_drv_t * drv, lv_indev_data_t*data)
{
if(touchpad_pressed) {
data->point.x = touchpad_x;
data->point.y = touchpad_y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
若是需要显示光标,则如下:

lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv);
//图像声明
LV_IMG_DECLARE(mouse_cursor_icon);
//创建光标对象
lv_obj_t * cursor_obj = lv_img_create(lv_scr_act(), NULL);
//设置光标图像的来源
lv_img_set_src(cursor_obj, &mouse_cursor_icon);
//链接显示驱动
lv_indev_set_cursor(mouse_indev, cursor_obj);
//设置光标点击
lv_obj_set_click(cursor_obj, false);

编码器

使用编码器可以做到以下事件:

  1. 按下按钮
  2. 长按按钮
  3. 左转
  4. 右转
  • 注册一个带有 LV_INDEV_TYPE_ENCODER 类型的 read_cb 函数。
  • 必须创建一个对象组: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 的返回值)。
void 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_PRESSED;
else data->state = LV_INDEV_STATE_RELEASED;
}

睡眠管理

由于屏幕驱动会占用系统的大量资源,所以在不需要屏幕驱动时,可以选择睡眠LVGL子系统,减少对资源的占用时,可以参考以下代码:

while(1) {
//一秒内无操作
if(lv_disp_get_inactive_time(NULL) < 1000) {
//LVGL处理相关任务
lv_task_handler();
}
/*Sleep after 1 sec inactivity*/
else {
timer_stop(); //定时器暂停
sleep(); //休眠线程
}
my_delay_ms(5);
}
当需要唤醒时,对输入设备进行操作以唤醒LVGL。

将下面代码加入输入设备读取功能里面。

lv_tick_inc(LV_DISP_DEF_REFR_PERIOD); //唤醒任务
timer_start(); //重启定时器
lv_task_handler(); //启动LVGL处理相关任务
想了解更多关于开源的内容,请访问:

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

有关OpenHarmony轻量化系统的LVGL使用(一)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby-on-rails - Ruby on Rails : . 常量化 : wrong constant name error? - 2

    我正在使用这个:4.times{|i|assert_not_equal("content#{i+2}".constantize,object.first_content)}我之前声明过局部变量content1content2content3content4content5我得到的错误NameError:wrongconstantnamecontent2这个错误是什么意思?我很确定我想要content2=\ 最佳答案 你必须用一个大字母来调用ruby​​常量:Content2而不是content2。Aconstantnamestart

随机推荐