LittleVGL 学习笔记
本文最后更新于:2020年11月5日 凌晨
版本: v7.7.2-dev
平台: windows 10
模拟平台: CodeBlocks on win10
移植
Tick 接口
LittlevGL 需要一个系统嘀嗒 (tick) 来了解动画和其他任务的运行时间。
您需要定期调用 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 ‘,如下所示:
1 |
|
API
以1毫秒的分辨率提供对系统刻度的访问
函数
uint32_t lv_tick_get(void)
- 获取自启动以来经过的毫秒数
- return
- 经过的毫秒数
uint32_t lv_tick_elaps(uint32_t prev_tick)
-获取自上一个时间戳以来经过的毫秒数
- return
- 自“ prev_tick”以来经过的毫秒数
- Parameters
- prev_tick:上一个时间戳(systick_get()的返回值)
概述
Object
ittleVGL是以对象(Object)为概念的,在LVGL中,用户界面的基本构建块是对象(Object),也称为控件。例如,按钮,标签,图像,列表,图表或文本区域。‘
在对象类型这里查看所有对象类型。
Attributes
基础属性
所有的控件对象都具有一些共同的属性,如下所示
- 位置(Position)
- 大小(Size)
- 父类(Parent)
- 是否可拖拽(Drag enable)
- 是否可点击(Click enable)等等
你可以通过 lv_obj_set_...
和 lv_obj_get_...
这样的API接口对来读写这些属,就比如下面这样:
1 |
|
要查看所有可用函数请访问基本对象的文档.
专属属性
对象类型也有本身的专有属性,比如,一个滑动条有:
- 最小最大值(Min. max. values)
- 当前值(Current value)
- 自定义样式(Custom styles)
对于这些属性,每个对象类型都有唯一的API函数。例如一个滑块:
1 |
|
这些对象类型的API已经描述在他们的 文档 中,但是你也可以在他们各自的头文件中核查 (例如 lv_objx/lv_slider.h)
对象工作机制
父子结构
父对象可以被看作是其子对象的容器,每个对象只有一个父对象(screen对象除外),父对象可以有无限数量的子对象。同时父对象的类型是没有限制,但是有一些典型的父对象(例如按钮)和典型的子对象(例如标签)。
父对象和子对象之间具有如下2点特性:
一起移动(Moving together)
如果父对象的位置更改,则子对象将随父对象一起移动,因此子对象的坐标位置是以父对象的左上角而言的,而不是以屏幕的左上角
(0;0) 坐标表示对象的位置会保持在他们各自父对象的左上角
1 |
|
Modify the position of the parent:
1 |
|
(为简单演示,例子中没有给出调整对象颜色的代码)
子对象只可显示在父对象上
如果子对象的一部分在父对象的外面,那么子对象的这一部分将不会被显示出来
1 |
|
创建-删除对象
在littleVGL中,对象可以在运行时被创建和删除,这意味着仅当前创建的对象消耗RAM。例如,如果需要图表(chart),则可以在需要时创建它,并在不可见或不必要时将其删除。
每一种对象都有其专属的 create 创建函数,他需要2个参数
- 指向父对象的指针。要创建屏幕(screen),请以NULL作为父级。
- (可选)用于复制具有相同类型的对象的指针。该复制对象可以为NULL,以避免复制操作。
所有对象使用 lv_obj_t
指针(C语言)作为句柄进行引用。以后可以使用该指针设置或获取对象的属性。
创建函数看起来如下面这样:
1 |
|
所有对象类型都有一个通用的删除函数。它删除对象及其所有子对象。
1 |
|
lv_obj_del will delete the object immediately. If for any reason you can’t delete the object immediately you can use lv_obj_del_async(obj). It is useful e.g. if you want to delete the parent of an object in the child’s LV_EVENT_DELETE signal.
lv_obj_del
会立即删除对象。如果你由于某种原因不想立即删除的话,你可以使用lv_obj_del_async(obj)
接口来异步删除,删除动作会在下一个lv_task_handler
调用时被执行。这很有用,例如,如果要在子对象的LV_EVENT_DELETE信号删除对象的父对象。
另外你可以通过 lv_obj_clean(obj)
接口来清除某个obj对象下的所有子对象(但不能删除对象本身):
1 |
|
Screens
确保不要混淆显示器和屏幕:
- 显示(Displays)是绘制像素的物理硬件。
- 屏幕(Screens )是与特定显示器关联的高级根对象。一个显示器可以有多个与之关联的屏幕,但反之亦然。
创建 Screens
屏幕对象是一个特殊的对象,因为他自己没有父对象,所以它以这样的方式来创建:
1 |
|
屏幕对象可以被创建为任何对象类型。例如,一个 基本对象或一个用一个图片作为壁纸
获取活跃的screen
默认情况下,littleVGL会为显示器创建一个lv_obj类型的基础对象来作为它的屏幕,即最顶层的父类,可以通过lv_scr_act()接口来获取当前活跃的屏幕对象,通过lv_scr_load()接口来设置一个新的活跃屏幕对象
每一次显示中始终有一个活动屏幕。默认情况下,littleVGL为每个显示创建并加载一个“基础对象(Base object)”作为屏幕。要获取当前活动的屏幕,请使用函数 lv_scr_act()
来获取活跃屏幕。
加载screens
要加载新屏幕,如以下函数原型 lv_scr_load(scr1)
.
动画加载screens
可以使用动画加载新屏幕(类似PPT切换页面的动画,如:淡入淡出等)
1 |
|
transition_type存在以下转换类型:
- LV_SCR_LOAD_ANIM_NONE: 延时 time 毫秒后立即切换
- LV_SCR_LOAD_ANIM_OVER_LEFT/RIGHT/TOP/BOTTOM:将新屏幕移到给定方向上
- LV_SCR_LOAD_ANIM_MOVE_LEFT/RIGHT/TOP/BOTTOM:将旧屏幕和新屏幕都移至给定方向
- LV_SCR_LOAD_ANIM_FADE_ON:使新屏幕淡入覆盖屏幕
Setting auto_del
to true
会在动画结束时自动删除旧屏幕.
在延迟time之后开始动画播放时,新屏幕将变为活动状态((由 lv_scr_act()返回创建)。
处理多个显示器 displays
屏幕在当前选择的默认显示上创建。默认显示是使用 lv_disp_drv_register
注册的最后一个显示屏,也可以使用lv_disp_set_default(disp)
显式地选择一个新的默认显示。
lv_scr_act()
,lv_scr_load()
和lv_scr_load_anim()
在默认屏幕上进行操作。
查看 Multi-display support 了解更多
Parts
控件( widgets)可以包含多个零部件。例如,按钮button仅具有主要部分,而滑块Slider则由背景,指示器和旋钮组成。
各零部件的名称构造类似 LV_ + <TYPE> _PART_ <NAME>
。例如 LV_BTN_PART_MAIN
or LV_SLIDER_PART_KNOB
。通常在将 样式styles 添加到对象时使用零部件。可以将不同的样式分配给对象的不同零部件。
要了解有关零部件的更多信息,请阅读“样式”概述的相关部分。
States
对象可以处于以下状态的组合
- LV_STATE_DEFAULT:正常,已发布
- LV_STATE_CHECKED:切换或选中 Toggled or checked
- LV_STATE_FOCUSED:通过键盘或编码器聚焦或通过触摸板/鼠标单击Focused via keypad or encoder or clicked via touchpad/mouse
- LV_STATE_EDITED 由编码器编辑Edit by an encoder
- LV_STATE_HOVERED 鼠标悬停(现在不支持)Hovered by mouse (not supported now)
- LV_STATE_PRESSED 已按下Pressed
- LV_STATE_DISABLED 禁用或不活动Disabled or inactive
当用户按下、释放、聚焦等对象时,状态通常由库自动更改。但是,状态也可以手动更改。要完全覆盖当前状态,请使用 lv_obj_set_state(obj, part, LV_STATE...)
。要设置或清除给定状态(但保持其他状态不变)使用 lv_obj_add/clear_state(obj, part, LV_STATE_...)
,在两种情况下都可以使用 或| 状态值来组合。例如lv_obj_set_state(obj, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED)
。
要了解有关零部件的更多信息,请阅读“样式”概述的相关部分。
Layers 层
创建顺序
默认情况下,LVGL在背景上绘制旧对象,在前景上绘制新对象。
例如,假设我们向一个名为button1的父对象添加了一个按钮,然后向另一个名为button2的按钮添加了一个按钮。然后button1(及其子对象)将在背景中,并且可以被button2及其子对象覆盖。
1 |
|
成为前景
有几种方法可以将对象置于前景:Cai Xuefeng
- 使用
lv_obj_set_top(obj,true)
。如果对象或它的任何子对象被点击,那么LVGL将自动将该对象带到前景。它的工作原理类似于PC上的典型GUI。当点击背景中的一个窗口时,它会自动来到前景 - 使用
lv_obj_move_foreground(obj)
显式地告诉库将一个对象放到前景。类似地,使用lv_obj_move_background(obj)
移动到背景 - 当
lv_obj_set_parent(obj,new_parent)
被使用时,对象将在new_parent父对象的前景
顶层和系统层
LVGL使用两个特殊的层,分别名为 layer_top
和 layer_sys
。两者在显示器的所有屏幕上都是可见的和常见的。但是,它们不能在多个物理显示器之间共享。layer_top
总是位于默认屏幕的顶部(lv_scr_act()
),而 layer_sys
位于 layer_top
的顶部(不可更改,适合做 一些弹窗)
用户可以使用 layer_top
创建一些随处可见的内容。例如,一个菜单栏,一个弹出窗口等等。如果单击click属性是启用的,那么 layer_top
将吸收所有用户的单击,并处理。
1 |
|
layer_sys也用于类似的目的在LVGL上。例如,它将鼠标光标放在那里,以确保它总是可见的
Events 事件
当发生用户可能感兴趣的事情时,事件将在LVGL中触发。例如对象:
- 被点击
- 被拖动
- 其值已被更改,等等。
用户可以将回调函数分配给对象以查看这些事件。实际上,它看起来像这样:
1 |
|
注意:多个对象可以使用同一回调函数。
Event类型
存在以下事件类型:
一般事件
所有对象(例如Buttons/Labels/Sliders等)都将接收这些通用事件,而不管它们的类型如何。
相关的输入设备
当用户按下/释放对象时发送这些消息。它们不仅用于鼠标指针,还可以用于键盘,编码器和按钮输入设备。访问输入设备概述部分以了解有关它们的更多信息。
- LV_EVENT_PRESSED 该对象已被按下
- LV_EVENT_PRESSING 对象被按下(在按下时连续发送)
- LV_EVENT_PRESS_LOST 仍在按下输入设备,但不再在对象上
- LV_EVENT_SHORT_CLICKED 在
LV_INDEV_LONG_PRESS_TIME
时间之前释放按键。如果拖动则不调用。 - LV_EVENT_LONG_PRESSED 持续按下
LV_INDEV_LONG_PRESS_TIME
时间。如果拖动则不调用。 - LV_EVENT_LONG_PRESSED_REPEAT 在
LV_INDEV_LONG_PRESS_TIME
事件之后每LV_INDEV_LONG_PRESS_REP_TIME
毫秒调用一次。如果拖动则不调用。 - LV_EVENT_CLICKED 释放时调用(无论长按),如果拖动则不调用。
- LV_EVENT_RELEASED 无论什么情况下,只要对象被释放时都会被调用,即使是拖动。如果在按下后从对象外部释放时,则不会调用。在这种情况下,
LV_EVENT_PRESS_LOST
会被触发。
相关指针
这些事件仅由类似指针的输入设备(例如鼠标或触摸板)发送
- LV_EVENT_DRAG_BEGIN 开始拖动对象
- LV_EVENT_DRAG_END 拖动完成(包括拖动并甩出(拖拽))
- LV_EVENT_DRAG_THROW_BEGIN 开始拖拽(拖动后释放“动量”)(控件会因为惯性仍会移动一段距离)
键盘和编码器
这些事件由键盘和编码器输入设备发送。在 [overview/indev](Input devices) 了解更多。
- LV_EVENT_KEY 将按键发送到对象。通常在按下它或在长按之后重复时。可以通过以下方式检索按键
uint32_t*key=lv_event_get_data()
- LV_EVENT_FOCUSED 对象集中在其组中
- LV_EVENT_DEFOCUSED 对象在其组中散焦
一般事件库
发送的其他一般事件。
- LV_EVENT_DELETE 正在删除对象。释放相关的用户分配数据。
特别事件
这些事件特定于特定的对象类型。
- LV_EVENT_VALUE_CHANGED 对象值已更改(例如,对于Slider)
- LV_EVENT_INSERT 某物被添加到对象中。(通常到文本区域)
- LV_EVENT_APPLY 单击“确定”,“应用”或类似的特定按钮。(通常来自键盘对象)LV_EVENT_CANCEL 单击“关闭”,“取消”或类似的特定按钮。(通常来自键盘对象)LV_EVENT_REFRESH 查询以刷新对象。永远不会由库发送,但可以由用户发送。
请访问enter description here的文档,以了解对象类型使用哪些事件。
自定义的数据
一些事件可能包含自定义数据。例如,LV_EVENT_VALUE_CHANGED
在某些情况下会告诉新值。有关更多信息,请访问enter description here的文档。要在事件回调中获取自定义数据,请使用 lv_event_get_data()
。
自定义数据的类型取决于发送对象,但如果是
- single number (数据)则是 uint32_t* 或 int32_t*
- 文字则是char* 或 constchar*
手动发送事件
任意事件
要将事件手动发送到对象,请使用 lv_event_send(obj,LV_EVENT_...,&custom_data)
例如,它可以通过模拟按钮按下来手动关闭消息框(尽管有更简单的方法):
1 |
|
刷新事件
LV_EVENT_REFRESH
是一个特殊事件,因为它旨在供用户用来通知对象刷新自身。一些例子:
- 通知标签根据一个或多个变量(例如当前时间)刷新其文本
- 语言更改时刷新标签
- 如果满足某些条件,则启用按钮(例如,输入正确的PIN)
- 如果超出限制,则将样式添加到对象/从对象删除样式,等等。
处理类似情况的最简单方法是使用以下函数。
lv_event_send_refresh(obj)
只是 lv_event_send(obj,LV_EVENT_REFRESH,NULL)
一个包装。因此,它只向对象发送一个 LV_EVENT_REFRESH
.
lv_event_send_refresh_recursive(obj)
向一个对象及其所有子对象发送LV_EVENT_REFRESH
事件。如果将NULL
作为参数传递,则所有显示的所有对象都将刷新。
LVGL的样式
样式用于设置对象的外观。lvgl中的样式受CSS启发很大。简而言之,概念如下:
- 样式是一个
lv_style_t
变量, 可以保存属性,例如边框宽度,文本颜色等。与classCSS类似。 - 并非必须指定所有属性。未指定的属性将使用默认值。
- 可以将样式分配给对象以更改其外观。
- 样式可以被任意数量的对象使用。
- 样式可以级联,这意味着可以将多个样式分配给一个对象(一个对象由多个零部件,会用到不同的样式),并且每种样式可以具有不同的属性。例如,
style_btn
可能会是导致默认的灰色按钮,但style_btn_red
只需添加一个background-color=red
就可以覆盖背景色。 - 以后添加的样式具有更高的优先级。这意味着,如果以两种样式指定属性,则将使用后面添加的样式。
- 如果未在对象中指定,则某些属性(例如,文本颜色)可以从父级继承。
- 对象可以具有比“普通”样式更高优先级的局部样式local styles。
- 与CSS(伪类描述不同的状态,例如:hover)不同,在lvgl中,将属性分配给给定的状态。(即“类”与状态无关,但是每个属性都有一个状态)
- 当对象更改状态时可以应用过渡Transitions 。
States
对象可以处于以下状态:
- LV_STATE_DEFAULT(0x00):正常,已释放
- LV_STATE_CHECKED(0x01):切换或选中
- LV_STATE_FOCUSED(0x02):通过键盘或编码器聚焦或通过触摸板/鼠标单击LV_STATE_EDITED(0x04):由编码器编辑
- LV_STATE_HOVERED(0x08):鼠标悬停(现在不支持)
- LV_STATE_PRESSED(0x10):已按下
- LV_STATE_DISABLED(0x20):禁用或不禁用,
- 状态的组合也是可能的。
LV_STATE_FOCUSED|LV_STATE_PRESSED
可以在每种状态和状态组合中定义样式属性。例如,为默认和按下状态设置不同的背景颜色。如果未在状态中定义属性,则将使用最佳匹配状态的属性。通常,它表示带有 LV_STATE_DEFAULT
状态的属性。 ̨如果即使对于默认状态也未设置该属性,则将使用默认值。(请参阅稍后)
但是“最佳匹配状态的属性”到底意味着什么?States的值代表着显示的的优先级(请参见上面的列表)。值越高,优先级越高。为了确定要使用哪个状态state的属性,我们举一个例子。让我们来看看背景色是这样定义的:
- LV_STATE_DEFAULT:白色
- LV_STATE_PRESSED:灰色
- LV_STATE_FOCUSED:红色
- 默认情况下,对象处于默认状态,因此很简单:该属性在对象的当前状态中完美定义为白色
- 按下对象时,有2个相关属性:默认为白色(默认与每个状态有关)和按下为灰色。按下状态的优先级为0x10,高于默认状态的0x00优先级,因此将使用灰色。
- 当物体聚焦时,会发生与按下状态相同的事情,并且将使用红色。(焦点状态的优先级高于默认状态)。
- 聚焦并按下对象时,灰色和红色都可以使用,但是按下状态的优先级高于聚焦,因此将使用灰色。
- 可以为设置例如玫瑰色通过组合
LV_STATE_PRESSED | LV_STATE_FOCUSED
。在这种情况下,此组合状态的优先级为0x02+0x10=0x12,该优先级高于按下状态的优先级,因此将使用玫瑰色。 - 选中对象后,没有属性可以设置此状态的背景色。因此,在缺少更好的选择的情况下,对象在默认状态的属性中仍为白色。
一些实用说明:
- 如果要为所有状态设置属性(例如红色背景色),只需将其设置为默认状态即可。如果对象找不到其当前状态的属性,它将回退到默认状态的属性。
- 使用
或|
来描述复杂情况的属性。(例如,按+选中+集中) - 对不同的状态使用不同的样式元素可能是一个好主意。例如,很难找到 释放,按下,选中+按下,聚焦,聚焦+按下,聚焦+按下+选中等状态的背景颜色。相反,例如,将背景色用于按下和选中状态,并使用不同的边框颜色指示聚焦状态
级联样式
不需要将所有属性设置为一种样式。可以向对象添加更多样式,然后让后来添加的样式修改或扩展其他样式的属性。例如,创建常规的灰色按钮样式,并为仅设置新的背景色的红色按钮创建新的样式。
当在CSS中列出所有使用的类时,这是相同的概念。<divclass=".btn.btn-red">
较晚添加的样式优先于较早的样式。因此,在上面的灰色/红色按钮示例中,应首先添加常规按钮样式,然后再添加红色样式。但是,仍然考虑来自状态State的优先级。因此,让我们研究以下情况:
- 基本的按钮样式定义了默认状态为深灰色和按下状态为浅灰色
- 红色按钮样式仅在默认状态下将背景色定义为红色
- 在这种情况下,释放按钮时(它处于默认状态)它将是红色的,因为在最后添加的样式(红色样式)中找到了完美的匹配。按下按钮时,浅灰色是更好的搭配,因为它完美地描述了当前状态,因此按钮将是浅灰色的。
继承
某些属性(通常与文本相关)可以从父对象的样式继承。仅当未以对象的样式(即使在默认状态下)设置给定属性时,才应用继承。在这种情况下,如果该属性是可继承的,则该属性的值也将在父级中搜索,直到一部分可以告诉该属性的值为止。父母会用自己的状态来说明价值。按下按钮后,文字颜色就从这里来,将使用按下的文字颜色。
小部件
对象可以具有可以具有自己样式的小部件。例如,页面包含四个部分:
- 背景
- 可卷动
- 滚动条
- 边缘闪光灯
- 有三种类型的对象部分的主体,虚拟和现实。
主要零部件通常是对象的背景和最大的部分。某些对象只有一个主要部分。例如,一个按钮只有一个背景。
虚拟零部件是实时绘制到主体零部件的其他零部件。它们后面没有“真实”对象。例如,页面的滚动条不是真实的对象,仅在绘制页面背景时才绘制。虚拟零部件始终具有与主要零部件相同的状态。如果可以继承该属性,则在转到父级之前还将考虑主体部分。
真实零部件是由主对象创建和管理的真实对象。例如,页面的可滚动部分是真实对象。实际零部件的状态可能与主要零部件的状态不同。要查看对象具有哪些部分,请访问其文档页面
初始化样式并设置/获取属性
样式存储在 lv_style_t
变量中。样式变量应为static
,全局或动态分配。换句话说,它们不能是函数中的局部变量,在函数存在时销毁。在使用样式之前,应使用进行初始化lv_style_init(&my_style)
。初始化后,可以设置样式属性。属性集函数格式如下所示:lv_style_set_<property_name>(&style,<state>,<value>);
。例如,上面提到的示例如下所示:
1 |
|
可以使用lv_style_copy(&style_destination,&style_source)
复制样式。复制后,仍然可以自由添加属性。
要删除属性,请使用:
1 |
|
要从给定状态的样式中获取值,可以使用以下原型的功能:_lv_style_get_color/int/opa/ptr(&style, <prop>, <result buf>);.
。将选择最匹配的属性,并返回其优先级。如果找不到该属性,则将返回-1
函数(…color/int/opa/ptr)的形式应根据的类型使用<prop>。例如:
1 |
|
要重置样式(释放所有数据),请使用Cai Xuefeng
1 |
|
管理样式列表
样式本身没有那么有用。应该将其分配给对象才能生效。对象的每个部分都存储一个样式列表,该列表是已分配样式的列表。
要将样式添加到对象,请使用lv_obj_add_style(obj,<part>,&style)
,例如:
1 |
|
可以使用以下方式重置对象样式列表lv_obj_reset_style_list(obj,<part>)
如果已经分配给对象的样式发生更改(即,其属性之一设置为新值),则应通过以下方式通知使用该样式的对象:lv_obj_refresh_style(obj)
要获得属性的最终值,包括级联,继承,局部样式和过渡(请参见下文),可以使用以下类似的get函数:lv_obj_get_style_<property_name>(obj, <part>)
。这些函数使用对象的当前状态,如果没有更好的候选者,则返回默认值。例如:
1 |
|
本地风格
在对象的样式列表中,也可以存储所谓的局部属性。与CSS的概念相同。局部样式与普通样式相同,但是它仅属于给定的对象,不能与其他对象共享。要设置本地属性,请使用lv_obj_set_style_local_<property_name>(obj, <part>, <state>, <value>);
例如以下:
1 |
|
转换
默认情况下,当对象更改状态(例如,按下状态)时,会立即设置新状态下的新属性。但是,通过过渡,可以在状态改变时播放动画。例如,在按下按钮后,可以在300毫秒内将其背景色设置为所按下的颜色。
过渡的参数存储在样式中。可以设置
- 过渡时间
- 开始过渡之前的延迟
- 动画path(也称为计时功能)
- 要设置动画的属性
可以为每个状态定义过渡属性。例如,将500ms过渡时间设置为默认状态将意味着当对象进入默认状态时,将应用500ms过渡时间。在按下状态下设置100ms过渡时间将意味着在进入按下状态时100ms过渡时间。因此,此示例配置将导致快速进入工作状态而缓慢回到默认状态。
属性
样式中可以使用以下属性。
混合属性
- radius(lv_style_int_t):设置背景的半径。0:无半径,LV_RADIUS_CIRCLE:最大半径。默认值:0。
- clip_corner(bool):true:可以将溢出的内容剪切到圆角(半径>0)上。默认值:false。
- size(lv_style_int_t):小部件内部元素的大小。是否使用此属性,请参见窗口小部件的文档。默认值:LV_DPI / 20。
- transform_width(lv_style_int_t):使用此值使对象在两侧更宽。默认值:0
- transform_height(lv_style_int_t)使用此值使对象在两侧都较高。默认值:0
- transform_angle(lv_style_int_t):旋转类似图像的对象。它的uinit为0.1度,对于45度使用450。默认值:0。
- transform_zoom(lv_style_int_t)缩放类似图像的对象。LV_IMG_ZOOM_NONE正常大小为256(或),一半为128,一半为512,等等。默认值:LV_IMG_ZOOM_NONE
- opa_scale(lv_style_int_t):继承。按此比例缩小对象的所有不透明度值。由于继承了子对象,因此也会受到影响。默认值:LV_OPA_COVER。
填充和边距属性
填充可在边缘的内侧设置空间。意思是“我不要我的孩子们离我的身体太近,所以要保留这个空间”。
填充内部设置了孩子之间的“差距”。边距在边缘的外侧设置空间。意思是“我想要我周围的空间”。
如果启用了布局或自动调整,则这些属性通常由Container对象使用。但是,其他小部件也使用它们来设置间距。有关详细信息,请参见小部件的文档。
- pad_top(lv_style_int_t):在顶部设置填充。默认值:0。
- pad_bottom(lv_style_int_t):在底部设置填充。默认值:0。
- pad_left(lv_style_int_t):在左侧设置填充。默认值:0。
- pad_right(lv_style_int_t):在右侧设置填充。默认值:0。
- pad_inner(lv_style_int_t):设置子对象之间对象内部的填充。默认值:0。
- margin_top(lv_style_int_t):在顶部设置边距。默认值:0。
- margin_bottom(lv_style_int_t):在底部设置边距。默认值:0。
- margin_left(lv_style_int_t):在左边设置边距。默认值:0。
- margin_right(lv_style_int_t):在右边设置边距。默认值:0。
背景属性
背景是一个可以具有渐变和radius舍入的简单矩形。
- bg_color(lv_color_t)指定背景的颜色。默认值:LV_COLOR_WHITE。
- bg_opa(lv_opa_t)指定背景的不透明度。默认值:LV_OPA_TRANSP。
- bg_grad_color(lv_color_t)指定背景渐变的颜色。右侧或底部的颜色是
bg_grad_dir != LV_GRAD_DIR_NON
。默认值:LV_COLOR_WHITE。 - bg_main_stop(uint8_t):指定渐变应从何处开始。0:最左/最上位置,255:最右/最下位置。默认值:0。
- bg_grad_stop(uint8_t):指定渐变应在何处停止。0:最左/最上位置,255:最右/最下位置。预设值:255。
- bg_grad_dir(lv_grad_dir_t)指定渐变的方向。可以LV_GRAD_DIR_NONE/HOR/VER。默认值:LV_GRAD_DIR_NONE。
- bg_blend_mode(lv_blend_mode_t):将混合模式设置为背景。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
边框属性
边框绘制在背景上方。它具有radius舍入。
- border_color(lv_color_t)指定边框的颜色。默认值:LV_COLOR_BLACK
- border_opa(lv_opa_t)指定边框的不透明度。默认值:LV_OPA_COVER
- border_width(lv_style_int_t):设置边框的宽度。默认值:0。
- border_side(lv_border_side_t)指定要绘制边框的哪一侧。可以LV_BORDER_SIDE_NONE/LEFT/RIGHT/TOP/BOTTOM/FULL。ORed值也是可能的。默认值:LV_BORDER_SIDE_FULL。
- border_post(bool):如果true在绘制完所有子级之后绘制边框。默认值:false
- border_blend_mode(lv_blend_mode_t):设置边框的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
轮廓属性
轮廓类似于边框,但绘制在对象外部。
- outline_color(lv_color_t)指定轮廓的颜色。默认值:LV_COLOR_BLACK
- outline_opa(lv_opa_t)指定轮廓的不透明度。默认值:LV_OPA_COVER
- outline_width(lv_style_int_t):设置轮廓的宽度。默认值:0。
- outline_pad(lv_style_int_t)设置对象和轮廓之间的空间。默认值:0
- outline_blend_mode(lv_blend_mode_t):设置轮廓的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
阴影属性
阴影是对象下方的模糊区域。
- shadow_color(lv_color_t)指定阴影的颜色。默认值:LV_COLOR_BLACK
- shadow_opa(lv_opa_t)指定阴影的不透明度。默认值:LV_OPA_TRANSP
- shadow_width(lv_style_int_t):设置轮廓的宽度(模糊大小)。默认值:0
- shadow_ofs_x(lv_style_int_t):设置阴影的X偏移量。默认值:0
- shadow_ofs_y(lv_style_int_t):设置阴影的Y偏移量。默认值:0
- shadow_spread(lv_style_int_t):在每个方向上使阴影大于背景的值达到此值。默认值:0
- shadow_blend_mode(lv_blend_mode_t):设置阴影的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include "../../lv_examples.h"
/**
* Using the Shadow style properties
*/
void lv_ex_style_4(void)
{
static lv_style_t style;
lv_style_init(&style);
/*Set a background color and a radius*/
lv_style_set_radius(&style, LV_STATE_DEFAULT, 5);
lv_style_set_bg_opa(&style, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style, LV_STATE_DEFAULT, LV_COLOR_SILVER);
/*Add a shadow*/
lv_style_set_shadow_width(&style, LV_STATE_DEFAULT, 8);
lv_style_set_shadow_color(&style, LV_STATE_DEFAULT, LV_COLOR_BLUE);
lv_style_set_shadow_ofs_x(&style, LV_STATE_DEFAULT, 10);
lv_style_set_shadow_ofs_y(&style, LV_STATE_DEFAULT, 20);
/*Create an object with the new style*/
lv_obj_t * obj = lv_obj_create(lv_scr_act(), NULL);
lv_obj_add_style(obj, LV_OBJ_PART_MAIN, &style);
lv_obj_align(obj, NULL, LV_ALIGN_CENTER, 0, 0);
}图案属性
图案是在背景中间绘制或重复以填充整个背景的图像(或符号)。
- pattern_image():指向变量的指针,图像文件或符号的path。默认值:。constvoid*lv_img_dsc_tNULL
- pattern_opa(lv_opa_t):指定图案的不透明度。默认值:LV_OPA_COVER
- pattern_recolor(lv_color_t):将此颜色混合到图案图像中。如果是符号(文本),它将是文本颜色。默认值:LV_COLOR_BLACK。
- pattern_recolor_opa(lv_opa_t):重着色的强度。默认值:(LV_OPA_TRANSP不重新着色)
- pattern_repeat(bool)::true图案将作为马赛克重复。false:将图案放置在背景中间。默认值:false。
- pattern_blend_mode(lv_blend_mode_t):设置图案的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
数值属性
值是绘制到背景的任意文本。它可以是创建标签对象的轻量级替代。
- value_str():指向要显示的文本的指针。仅保存指针!(不要将局部变量与lv_style_set_value_str一起使用,而应使用静态,全局或动态分配的数据)。默认值:NULL
- value_color(lv_color_t):文本的颜色。默认值:LV_COLOR_BLACK。
- value_opa(lv_opa_t):文本的不透明度。默认值:LV_OPA_COVER。
- value_font():指向文本字体的指针。默认值:NULL
- value_letter_space(lv_style_int_t):文本的字母空间。默认值:0
- value_line_space(lv_style_int_t):文本的行距。默认值:0。
- value_align(lv_align_t):文本的对齐方式。可以LV_ALIGN_…。默认值:LV_ALIGN_CENTER。
- value_ofs_x(lv_style_int_t):与路线原始位置的X偏移量。默认值:0。
- value_ofs_y(lv_style_int_t):从路线的原始位置偏移Y。默认值:0。
- value_blend_mode(lv_blend_mode_t):设置文本的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
文字属性
文本对象的属性。
- text_color(lv_color_t):文本的颜色。默认值:LV_COLOR_BLACK。
- text_opa(lv_opa_t):文本的不透明度。默认值:LV_OPA_COVER。
- text_font():指向文本字体的指针。默认值constlv_font_t*NULL
- text_letter_space(lv_style_int_t):文本的字母空间。默认值:0
- text_line_space(lv_style_int_t):文本的行距。默认值:0。
- text_decor(lv_text_decor_t):添加文字修饰。可以LV_TEXT_DECOR_NONE/UNDERLINE/STRIKETHROUGH。默认值:LV_TEXT_DECOR_NONE。
- text_sel_color(lv_color_t):设置文本选择的背景色。默认值:LV_COLOR_BLACK
- text_blend_mode(lv_blend_mode_t):设置文本的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
线属性
线的属性。
- line_color(lv_color_t):线条的颜色。默认值:LV_COLOR_BLACK
- line_opa(lv_opa_t):直线的不透明度。默认值:LV_OPA_COVER
- line_width(lv_style_int_t):线的宽度。默认值:0。
- line_dash_width(lv_style_int_t):破折号的宽度。仅对水平或垂直线绘制虚线。0:禁用破折号。默认值:0。
- line_dash_gap(lv_style_int_t):两条虚线之间的间隙。仅对水平或垂直线绘制虚线。0:禁用破折号。默认值:0。
- line_rounded(bool)::true绘制圆角的线尾。默认值:false。
- line_blend_mode(lv_blend_mode_t):设置线条的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
图片属性
图像的属性。
- image_recolor(lv_color_t):将此颜色混合到图案图像中。如果是符号(文本),它将是文本颜色。默认值:LV_COLOR_BLACK
- image_recolor_opa(lv_opa_t):重新着色的强度。默认值:(LV_OPA_TRANSP不重新着色)。默认值:LV_OPA_TRANSP
- image_opa(lv_opa_t):图像的不透明度。默认值:LV_OPA_COVER
- image_blend_mode(lv_blend_mode_t):设置图像的混合模式。可以LV_BLEND_MODE_NORMAL/ADDITIVE/SUBTRACTIVE)。默认值:LV_BLEND_MODE_NORMAL。
1 |
|
转换特性
用于描述状态更改动画的属性。
- transition_time(lv_style_int_t):过渡时间。默认值:0。
- transition_delay(lv_style_int_t):转换前的延迟。默认值:0。
- transition_prop_1(propertyname):应在其上应用过渡的属性。将属性名称
LV_STYLE_
与大写字母一起使用,例如LV_STYLE_BG_COLOR
。默认值:0(无)。 - transition_prop_2(propertyname):与transition_1相同,只是另一个属性。默认值:0(无)。
- transition_prop_3(propertyname):与transition_1相同,只是另一个属性。默认值:0(无)。
- transition_prop_4(propertyname):与transition_1相同,只是另一个属性。默认值:0(无)。
- transition_prop_5(propertyname):与transition_1相同,只是另一个属性。默认值:0(无)。
- transition_prop_6(propertyname):与transition_1相同,只是另一个属性。默认值:0(无)。
- transition_path(lv_anim_path_t):过渡的动画path。(需要为静态或全局变量,因为仅保存了其指针)。默认值:(lv_anim_path_def线性path)。
1 |
|
比例属性
鳞片状元素的辅助属性。体重秤具有正常区域和末端区域。顾名思义,结束区域是标度的结束,可以是临界值或void值。正常区域在结束区域之前。两个区域可能具有不同的属性
- scale_grad_color(lv_color_t):在正常区域中,在比例尺线上对该颜色进行渐变。默认值:LV_COLOR_BLACK。
- scale_end_color(lv_color_t):结束区域中刻度线的颜色。默认值:LV_COLOR_BLACK。
- scale_width(lv_style_int_t):比例尺的宽度。默认值:。默认值:。LV_DPI/8LV_DPI/8
- scale_border_width(lv_style_int_t):在标准区域的比例尺外侧绘制的边框的宽度。默认值:0。
- scale_end_border_width(lv_style_int_t):在结束区域的刻度外侧上绘制边框的宽度。默认值:0。
- scale_end_line_width(lv_style_int_t):结束区域中比例线的宽度。默认值:0。
1 |
|
在小部件的文档中,您将看到诸如“小部件使用典型的背景属性”之类的句子。“典型背景”属性是:
- 背景
- 边境
- 大纲
- 阴影
- 模式
- 值
主题
主题是样式的集合。始终有一个活动主题,在创建对象时会自动应用其样式。它为UI提供了默认外观,可以通过添加其他样式来对其进行修改。
默认的主题被设定在lv_conf.h
与LV_THEME_...
定义。每个主题都具有以下属性
- 原色primary color
- 二次色secondary color
- 小字体
- 普通字体
- 字幕字体
- 标题字体
- 标志(特定于给定主题)
如何使用这些属性取决于主题。
有3个内置主题:
- empty空:未添加默认样式
- material材质:令人印象深刻的现代主题-单声道:用于黑白显示的简单黑白主题
- template模板:一个非常简单的主题,可以将其复制以创建自定义主题
扩展主题
内置主题可以通过自定义主题进行扩展。如果创建了自定义主题,则可以选择“基本主题”。基本主题的样式将添加到自定义主题之前。可以链接任何数量的主题。例如,材料主题->自定义主题->黑暗主题。这是有关如何基于当前活动的内置主题创建自定义主题的示例
1 |
|
Example
1 |
|
动画
您可以使用动画在开始值和结束值之间自动更改变量的值。动画将通过定期调用带有相应value参数的“animator”函数来发生。
该动画功能具有以下原型:
1 |
|
该原型与LVGL的大多数设置功能兼容。例如lv_obj_set_x(obj, value) or lv_obj_set_width(obj, value)
创建动画
要创建动画,必须初始化变量 lv_anim_t并使用lv_anim_set_…()功能对其进行配置。
1 |
|
您可以同时在同一变量上应用多个不同的动画。例如,使用lv_obj_set_x和lv_obj_set_y设置x和y坐标的动画。但是,只有一个动画可以存在给定的变量和函数对。因此lv_anim_start()将删除已经存在的可变功能动画。
动画path
您可以确定动画的path。在最简单的情况下,它是线性的,这意味着开始和结束之间的当前值线性变化。path主要是其计算基于所述动画的当前状态中的下一个值集的函数。当前,有以下内置path功能:
- lv_anim_path_linear线性动画
- lv_anim_path_step最后一步更改
- lv_anim_path_ease_in开头缓慢
- lv_anim_path_ease_out最后慢
- lv_anim_path_ease_in_out在开始和结束时也很慢
- lv_anim_path_overshoot超出最终值
- lv_anim_path_bounce从最终值反弹一点(就像撞墙一样)
可以这样初始化path:
1
2
3
4
5
6
7 lv_anim_path_t path;
lv_anim_path_init(&path);
lv_anim_path_set_cb(&path, lv_anim_path_overshoot);
lv_anim_path_set_user_data(&path, &foo); /*Optional for custom functions*/
/*Set the path in an animation*/
lv_anim_set_path(&a, &path);
通过CodeBlocks模拟运行LittlevGL
其实官方有使用教程:lvgl/lv_sim_codeblocks_win
1 |
|
下面就跟着官方教程走。
- 下载安装 git:1.5 起步 - 安装 Git
比较简单,跟着git官方教程走就行了。这是为了后边使用命令下载 LVGL 仿真文件。 - 下载安装 CodeBlocks:CodeBlocks下载地址
选择带编译器的版本:codeblocks-20.03mingw-setup。
安装一路默认即可,可更改安装路径 - 打开 cmd,输入命令: 文件默认会下载到目录
1
git clone https://github.com/lvgl/lv_sim_codeblocks_win.git
C:/Users/username
。如果自己指定目录,请提前进入指定目录下,再输入上述命令下载 - 使用命令 回车进入该文件目录下,并输入命令:
1
cd lv_sim_codeblocks_win
回车,将自动下载lvgl, lv_examples and lv_drivers 文件内容1
git submodule update --init --recursive
- 打开 codeblocks,开始界面点击
Open an existing project
,进入我们刚下载的lv_sim_codeblocks_win
文件夹并选择 ittlevGL.cbp` ,确认打开。 - 单击
Click on Build
图标或者按F9
编译运行工程。
如果编译时提示找不到编译器,出现无法编译的情况,则需对程序的编译环境进行重新配置
这里先点击Auto-detect
自动检测看自己电脑上有没有,如果没有的话, 则需要单独下载mingw:mingw-w64。再设置mingw路径。
- 开始使用仿真。
这里下载仿真的 LVGL 版本比较老,7.6.0 版本,不过这与最新版差别并不大,新版主要时修复了一些bug。
关于最新版的仿真,目前并无方法。
参考资料
- LVGL:Win7基于CodeBlocks的PC模拟仿真
- 【LittleVGL】Windows环境下利用CodeBlocks搭建LittleVGL的PC模拟器环境配置问题
- AN0018_LittlevGL_on_AT32_MCU_ZH_V1.0.3
使用PlatformIO运行LittlevGL
该方案不可行,不可行!不可行!原因未知。
已在 windows 和 linux 都测试过,均不可用,源码编译有错误。
该演示应帮助您使用出色的PlatformIO IDE组织项目。
它会自动安装所有内容-只需在vscode中打开此项目的文件夹,并同意安装它提供的所有功能
它包含可运行的LittlevGL演示,可在PC上运行(通过SDL驱动程序)。
它有示例,说明如何创建多个构建目标。例如:
native 快速在PC上建立原型接口
其他,为裸机构建固件
如何安装和使用演示
重要!native 构建(带有SDL2驱动程序的模拟器)仅在Linux上进行了测试!但是您仍然可以构建MCU目标。如果有人有兴趣改善其他OS支持(Windows,macOS)-欢迎PR。
Install Visual Studio Code
https://code.visualstudio.com/
Install SDL drivers
Windows
先安装 MSYS2,记住软件安装路径,后面用到。
之后打开 MSY32,只命令行输入下面语句:
国内网速安装较慢,搭梯子请设置全局梯子,再重新打开 MSY32 并输入下面语句。
1 |
|
将 Mingw-w64 安装路径下的 bin 文件夹(默认为 C:\msys64\mingw64\bin) 加载到 Windows 系统的 PATH 环境变量中
- 在win10搜索框输入
设置
- 输入
环境变量
,选中编辑账户的环境变量
- 再弹窗中 变量 栏中找到
Path
并单击选中,单击编辑
按钮,弹窗中 单击新建
,紧接着单击浏览
,找到 Mingw-w64 安装路径下的 bin 文件夹 - 一步步单击
确定
推出弹窗保存设置,之后重新打开 MSY32 - 在MSY32中分别输入命令 如果看不到预期的输出或g ++或gdb无法识别的命令,请检查安装(Windows控制面板>程序),并确保PATH条目与Mingw-w64 bin文件夹位置匹配
1
2g++ --version
gdb --version
构建/运行
打开 vscode,File -> Open Folder 打开下载的文件
- 如果你是第一次使用platformio,如果 vscode 弹窗提醒你要安装 PlatformIO 插件,请点击
同意
。然后等待PlatformIO安装。
构建/执行:
- 仿真: Terminal(终端) -> Run Task…() -> PlatformIO: Execute (native)
Bare metal: Terminal -> Run Build Task… -> PlatformIO: Build (stm32f429_disco)
参考文件
Run LittlevGL via PlatformIO
Using GCC with MinGW
LittlevGL基础控件学习
LittlevGL在AT32上的移植说明
STM32F4 SPI DMA显示
GUuiLite-MCU SPI屏也能跑这么炫酷的特效?来,移植起来秀一秀
STM32—SPI通信的总结(库函数操作)
【STM32】使用DMA+SPI传输数据
【LVGL学习之旅 01】移植LVGL到STM32
ScarsFun/lvgl_STM32F103_encoder_rtx5
(三)stm32之串口通信DMA传输完成中断
使用 spi dma 传输和键盘时绘图区域错误?
weefnn/pandora
lvgl/lv_port_stm32f746_disco
两天移植littlevgl
lvgl/lv_port_stm32f429_disco
LVGL 优化帧率技巧
f429 discovery开发版 LVGL移植(带操作系统)
【STM32】HAL库 STM32CubeMX教程十一—DMA (串口DMA发送接收)
STM32 DMA传输笔记(HAL库版)
STM32f103c8cb HAL library with SPI over DMA
STM32 cubeMX: triggering SPI DMA interrupt using interrupt
HAL_SPI_TransmitReceive_DMA() transmit interrupt always triggered after receive interrupt
STM32CubeMx. SPI and DMA usage example for STM32 MCU.
LVGL 移植
文件移植与修改
跟随官方教程移植:v7.11.0-dev
1 |
|
- 下载 LVGL 最新发布版 Release v7.11.0。这里并不下载 master 仓库的。并将文件夹重命名为
lvgl
- 将 lvgl 文件夹复制到工程目录下,并在IDE中将
lvgl
文件夹添加到编译路径中 - 将lvgl文件夹中的lv_conf_template.h复制并重命名为lv_conf,与lvgl同级目录
- 打开lv_conf.h,将#if 0改为 # if 1,设置屏幕的宽高像素,以及颜色模式,如下代码所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#if 1 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
/* clang-format off */
#include <stdint.h>
/*====================
Graphical settings
*====================*/
/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX (240) // 水平像素
#define LV_VER_RES_MAX (320) // 垂直像素
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB332
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 16 // 这里选择 RGB565模式 - 在你需要用到的littlevGL的文件添加#include “lvgl.h”
- 调用lv_init();函数
在一个定时任务中调用lv_tick_inc(x)
每x毫秒(x应在1到10之间)。 LVGL的内部时间需要。或者配置lv_tick_custom(请参阅LV_CONF.H)直接使用库函数自动调用lv_tick_inc(x)
。 - 调用 lv_init() 初始化
- 设置显示缓冲区。LVGL首先将在此缓存图像数据,并将渲染图像作为显示器进行。缓冲区大小可以自由设置,但1/10屏幕尺寸是一个很好的起点。
1
2
3static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * LV_VER_RES_MAX / 10]; /*Declare a buffer for 1/10 screen size*/
lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX / 10); /*Initialize the display buffer*/ - 实现并注册可以将渲染图像复制到显示区域的函数: 其中的set_pixel就是LCD顶层驱动的画点函数,这里直接替换成自己的画点函数即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.flush_cb = my_disp_flush; /*Set your driver function*/
disp_drv.buffer = &disp_buf; /*Assign the buffer to the display*/
lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
void my_disp_flush(lv_disp_drv_t * disp, 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++) {
set_pixel(x, y, *color_p); /* Put a pixel to the display.*/
color_p++;
}
}
lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/
} - 如果需要触摸输入就进行输入函数注册和实现,不需要就算了这里
1
2
3
4
5
6
7
8
9
10
11
12
13lv_indev_drv_t indev_drv; /*Descriptor of a input device driver*/
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_touchpad_read; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
{
data->state = touchpad_is_pressed() ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
if(data->state == LV_INDEV_STATE_PR) touchpad_get_xy(&data->point.x, &data->point.y);
return false; /*Return `false` because we are not buffering and no more data to read*/
}touchpad_get_xy
和touchpad_is_pressed()
也需要我们自己实现,目的就是将触摸坐标赋值给data->point.x, &data->point.y,data->state
。因此这里可以自由发挥,只要把坐标值给到这俩变量就行了。例如1
2
3
4
5
6
7bool my_touchpad_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; /*现在没有缓冲,所以没有更多的数据读取*/
} - 定期调用lv_task_handler()在主机中断或操作系统任务中每隔几毫秒。如果需要,它将重绘屏幕,处理输入设备等。
For a more detailed guide go to the Porting section.
以上的 9-10 步其实官方已经有例程了,我们不用再自己编写,只需要修改即可。例程在 lvgl\examples\porting
,这里面存放的就是对外接口适配例程。用哪一个,就更改那个名字,并在相应文件中修改 #if 0
启用。
- lv_port_disp:显示相关。
- lv_port_indev:输入相关。
- lv_port_fs:文件系统相关。
这里我们只使用 lv_port_disp
其它保持不动。并将 lv_port_disp.c/h
文件复制到 lvgl 同级的 lvgl_porting
目录下。 然后删除其它无关文件夹,最终文件结构如下:
这里我们按照上面的 8-10 步骤修改 lv_port_disp.c/h 文件
.c/h 文件中的
#if 0
改为# if 1
, .c 文件的头文件包含改为#include "lv_port_disp.h"
1
2
3
4
5
6
7/*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_disp.h"缓存数组已经在
lv_port_disp_init(void)
定义好了,这里我们仅使用一级缓存,将其他数组定去注释掉,不然会占用RAM,导致RAM不够用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/* Example for 1) */
static lv_disp_buf_t draw_buf_dsc_1;
static lv_color_t draw_buf_1[LV_HOR_RES_MAX * 10]; /*A buffer for 10 rows*/
lv_disp_buf_init(&draw_buf_dsc_1, draw_buf_1, NULL, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/
/* Example for 2) */
// static lv_disp_buf_t draw_buf_dsc_2;
// static lv_color_t draw_buf_2_1[LV_HOR_RES_MAX * 10]; /*A buffer for 10 rows*/
// static lv_color_t draw_buf_2_2[LV_HOR_RES_MAX * 10]; /*An other buffer for 10 rows*/
// lv_disp_buf_init(&draw_buf_dsc_2, draw_buf_2_1, draw_buf_2_2, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/
/* Example for 3) */
// static lv_disp_buf_t draw_buf_dsc_3;
// static lv_color_t draw_buf_3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX]; /*A screen sized buffer*/
// static lv_color_t draw_buf_3_2[LV_HOR_RES_MAX * LV_VER_RES_MAX]; /*An other screen sized buffer*/
// lv_disp_buf_init(&draw_buf_dsc_3, draw_buf_3_1, draw_buf_3_2, LV_HOR_RES_MAX * LV_VER_RES_MAX); /*Initialize the display buffer*/同时注释掉下面屏幕尺寸赋值语句。之前我们已经在 lv_conf.h 中设置过了,下面语句的优先级最高即会覆盖掉 lv_conf.h 中的设置。但为了保持入口一致,我们统一在lv_conf.h中设置,这里的注释掉。
1
2
3/*Set the resolution of the display*/
//disp_drv.hor_res = 240;
//disp_drv.ver_res = 320;修改
disp_flush()
。LCD_Fast_DrawPoint
就是我们之前编写的画点函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include "lcd.h"
static void disp_flush(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;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/* Put a pixel to the display. For example: */
/* put_px(x, y, *color_p)*/
LCD_Fast_DrawPoint(x,y,color_p->full);
color_p++;
}
}
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}触摸接口在
lv_port_indev
文件中,这里没用到,不添加。添加定时器,设置为1ms定时。在中断函数中调用
lv_tick_inc(1)
。注意这里的 “1” 必须和中断事件移植,中断间隔几ms,这个数字就是几。且只能取 1-10。1
2
3
4
5
6#include "lvgl.h"
/* 中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
lv_tick_inc(1);//lvgl的1ms心跳
}编译,检查错误。主要就是头文件包含错误。注意在STM32CubeIDE中一定要将GUI文件夹设置源文件夹
其余头文件包含如下:
编写主函数,驱动例程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31void test_screen(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL); /*Add a button the current screen*/
lv_obj_set_pos(btn, 10, 10); /*Set its position*/
lv_obj_set_size(btn, 120, 50); /*Set its size*/
lv_obj_t * label = lv_label_create(btn, NULL); /*Add a label to the button*/
lv_label_set_text(label, "Button"); /*Set the labels text*/
}
void setup()
{
delay_init(84); /* 延时函数初始化 */
__HAL_SPI_ENABLE(&hspi1);
HAL_TIM_Base_Start_IT(&htim10); /* 使能定时器 */
LCD_Init();//LCD初始化
delay_ms(100);
lv_init(); //lvgl系统初始化, 放在 LCD_Init() 后面
lv_port_disp_init(); //lvgl显示接口初始化,放在lv_init()的后面
test_screen(); /* 初始化界面后立即会显示 */
run_times = millis();
}
void loop()
{
lv_task_handler();
}test_screen
函数作用是创建一个带字符的按钮控件,主函数通过调用lv_task_handler()
显示创建的按钮控件。
loop 循环中一定不能有阻塞(延时函数),必须保证lv_task_handler()
能够及时调用,或者放在中断中定时调用。
LVGL快速入门
LVGL应用
Screen
Screen transitions and display background
Multiple screen question #394
The most efficient way to structure a multi screen LittlevGL project
button to change screen #93
LittlevGL 切换界面的演示
零知增强板-TFT扩展板 3.5寸 UI界面示例(lvgl)
LVGL优化
该优化测试实在 240*320 的屏幕测试的,测试显示按钮+屏幕背景刷新
1 |
|
不同配置下显示该同一内容画面所消耗的时间、RAM、ROM对比。
DMA+中断+单buf
1 |
|
- 耗时:67ms
- RAM占用 73.55%
- FLASH占用 65.12%
LV_HOR_RES_MAX * 10
改为 LV_HOR_RES_MAX * 20
1 |
|
- 耗时:55ms
- RAM占用 80.87%
- FLASH占用 65.12%
双buf
1 |
|
- 耗时:48ms
- RAM占用 80.87%
- FLASH占用 65.12%
LV_HOR_RES_MAX * 10
改为 LV_HOR_RES_MAX * 20
1 |
|
- 耗时:42ms
- RAM占用 95.52%
- FLASH占用 65.12%
减小RAM占用
修改以下内容对于显示时间几乎无影响,但可以大幅减小RAM暂用
上述测试都是在下属设置中(lv_conf.h)
1 |
|
下述为dma 单buffer的显示参数
1 |
|
- 耗时:67ms
- RAM占用 73.55%
- FLASH占用 65.12%
修改 LV_MEM_SIZE 大小
1 |
|
- 耗时:66ms
- RAM占用 36.05%
- FLASH占用 65.12%
插件宏定义设为0
lv_conf.h设置不用的插件为0,速度无影响,内存增加
上述测试都是在下属设置中(lv_conf.h)
1 |
|
在上述配置下,下述为dma 单buffer的显示参数
1 |
|
- 耗时:67ms
- RAM占用 73.55%
- FLASH占用 65.12%
如果将上述宏定义全部改为1(lvgl默认全为1),显示参数为
- 耗时:67ms
- RAM占用 73.55%
- FLASH占用 72.38%
参考资料