- 嵌入式
- 物联网
- 智能硬件
- 嵌入式GUI
- 硬件开发
【免费下载链接】HoloCubic
带网络功能的伪全息透明显示桌面站
导读
LVGL(本仓库内为 v7.6.1)并不强制要求触摸屏才能操作界面——通过键盘(Keypad)或旋转编码器(Encoder)这两类输入设备,同样可以完成按钮、下拉框、滚轮、滑条、开关、文本框等全部常用控件的选择、进入与编辑。本文以仓库中 lv_demo_keypad_encoder 演示的 README 为主体,结合其 源码实现,完整讲解该演示的功能范围、启用步骤、分组导航机制、编辑模式与虚拟键盘工作流,并给出在 HoloCubic 这类带编码器硬件的嵌入式项目中的落地参考。读完本文,你将掌握“如何让一个不含触摸屏的 LVGL 界面支持完整导航与文本输入”的整套方案。
Demo 概览:无触摸屏的控件导航
原文档开篇即点明了该演示的核心价值:LVGL 允许在不使用触摸板(touchpad)的情况下,通过键盘和/或编码器来控制控件。演示的目的就是展示在没有触摸屏时,如何操作以下控件类型:
- 按钮(Button)
- 复选框(Checkbox)
- 滑条(Slider)
- 开关(Switch)
- 数字微调框(Spinbox)
- 下拉列表(Drop-down list)
- 滚轮选择器(Roller)
- 列表(List)
- 文本框/文本输入(Text area / text input)
上图即仓库中该演示的运行界面截图(800×480),画面由两个 Tab 页组成:左侧 "Selectors" 页集中展示了上述各类选择型控件,右侧 "Text input" 页则演示无触摸屏下的文本输入。
如何运行该演示
原文档给出了两步运行指引,下面结合仓库实际配置展开为完整的启用流程。
第一步:在lv_ex_conf.h中开启演示宏
找到 lv_ex_conf.h,将演示开关从 0 改为 1:
/*Demonstrate the usage of encoder and keyboard*/ #define LV_USE_DEMO_KEYPAD_AND_ENCODER 1需要说明的是,仓库中默认值为 0(在 lv_ex_conf_template.h 中同样默认为 0),即该演示在 HoloCubic 固件中默认未启用;如需验证,可临时打开再编译。
同时需要注意两个可选辅助开关,它们决定了演示是否会注册对应的模拟输入设备:
| 宏 | 默认值 | 作用 |
|---|---|---|
LV_EX_KEYBOARD | 0 | 为部分示例添加 PC 键盘支持(需要lv_drivers仓库,即模拟器中的键盘驱动) |
LV_EX_MOUSEWHEEL | 0 | 为部分示例添加“编码器”(鼠标滚轮模拟)支持(同样依赖lv_drivers) |
这两个宏在 lv_ex_conf.h 中定义。开启后,演示代码会在初始化时通过lv_drivers提供的keyboard_read/mousewheel_read注册 Keypad 与 Encoder 类型的输入设备(见下文源码解析)。
第二步:在初始化完成后调用演示入口
在lv_init()与各驱动初始化完成之后,调用:
lv_demo_keypad_encoder();该函数声明位于 lv_demo_keypad_encoder.h,整个示例被#if LV_USE_DEMO_KEYPAD_AND_ENCODER包裹,宏未开启时函数体不参与编译。需要注意的是,演示内部会调用lv_tabview_create等在“当前活动屏幕”上建控件,因此调用前必须确保显示驱动已经初始化且存在默认显示。
源码结构剖析:演示是怎么搭起来的
演示由三个文件组成,位于 lv_demo_keypad_encoder 目录:
README.md—— 本文主体所依据的说明文档;lv_demo_keypad_encoder.h—— 对外仅暴露lv_demo_keypad_encoder()入口;lv_demo_keypad_encoder.c—— 全部界面与交互逻辑(约 300 行)。
初始化流程
入口函数lv_demo_keypad_encoder()(lv_demo_keypad_encoder.c)按顺序完成五件事:
- 创建输入分组并设置焦点回调:
g = lv_group_create(); lv_group_set_focus_cb(g, focus_cb);分组(lv_group_t)是 LVGL 无触摸导航的核心对象,后面会专门讲解。
- 条件注册键盘输入设备(当
LV_EX_KEYBOARD开启时):
lv_indev_drv_t kb_drv; lv_indev_drv_init(&kb_drv); kb_drv.type = LV_INDEV_TYPE_KEYPAD; kb_drv.read_cb = keyboard_read; lv_indev_t * kb_indev = lv_indev_drv_register(&kb_drv); lv_indev_set_group(kb_indev, g);- 条件注册编码器输入设备(当
LV_EX_MOUSEWHEEL开启时,用鼠标滚轮模拟):
lv_indev_drv_t enc_drv; lv_indev_drv_init(&enc_drv); enc_drv.type = LV_INDEV_TYPE_ENCODER; enc_drv.read_cb = mousewheel_read; lv_indev_t * enc_indev = lv_indev_drv_register(&enc_drv); lv_indev_set_group(enc_indev, g);创建 Tabview 与两个 Tab 页:
t1 = "Selectors"、t2 = "Text input",并把tv加入分组g。填充各页内容:调用
selectors_create(t1)、text_input_create(t2),最后弹出启动消息框msgbox_create()。
Selectors 页:八大选择型控件
selectors_create() 在页面上按“列居中”布局依次创建:
lv_btn_create—— 按钮,标签为 "Button";lv_checkbox_create—— 复选框;lv_slider_create—— 滑条,范围 0~10;lv_switch_create—— 开关;lv_spinbox_create—— 数字微调框;lv_dropdown_create—— 下拉列表,并注册了dd_enc事件回调(对LV_EVENT_VALUE_CHANGED做出响应);lv_roller_create—— 滚轮选择器;lv_list_create—— 列表,高度超过页面时自动收缩,并添加了 "Apply / Close / Show / Hide / Delete / Copy / Paste" 七个带LV_SYMBOL_*图标的按钮项。
这八类控件正好覆盖了 LVGL 中“可编辑(editable)”与“不可编辑”两类交互形态,是验证分组导航能力最齐全的集合。
Text input 页:无触摸屏的文本输入
text_input_create() 创建两个单行文本框ta1、ta2(后者继承前者样式),初始隐藏光标并设置占位文本 "Type something"。真正有趣的是ta_event_cb:当文本框获得焦点且分组处于编辑模式时,会动态创建一个虚拟键盘lv_keyboard_create,把键盘与文本框绑定,并将焦点移交到键盘——这就是“无触摸屏输入文字”的完整闭环,详见后文事件回调一节。
启动消息框
msgbox_create() 在顶层图层弹出欢迎消息框 "Welcome to the keyboard and encoder demo",含 "Ok" / "Cancel" 两个按钮,并将消息框加入分组后直接聚焦它(lv_group_focus_obj(mbox))。如果启用了滚轮模拟,还会预先lv_group_set_editing(g, true)让分组进入编辑状态。顶层图层被设置为半透明灰色且可点击,用于在消息框显示期间屏蔽背景操作。
关键机制一:输入设备类型与数据约定
演示注册的两种设备类型定义在 lv_hal_indev.h:
LV_INDEV_TYPE_KEYPAD, /**< Keypad or keyboard*/ LV_INDEV_TYPE_ENCODER, /**< Encoder with only Left, Right turn and a Button*/两者的读取数据约定也由同一个头文件定义:
- Keypad 设备通过
data->key上报当前按键(lv_hal_indev.h); - Encoder 设备通过
data->enc_diff上报“自上次读取以来的步数”,通过data->state上报LV_INDEV_STATE_PR/LV_INDEV_STATE_REL(lv_hal_indev.h)。
在底层 lv_indev.c 的indev_encoder_proc()中可以看到:编码器按下(LV_INDEV_STATE_PR)且键值为LV_KEY_ENTER时触发“确认/进入”;LV_KEY_LEFT/LV_KEY_RIGHT被转换为enc_diff的增减,从而模拟编码器左右旋转;长按LV_KEY_ENTER则会切换分组的编辑模式(对可编辑控件生效)。这说明编码器本质上被抽象成了“左/右转 + 一个按钮”,与触摸无关。
关键机制二:分组(Group)导航与编辑模式
无触摸导航的全部秘密都藏在lv_group中。演示在 lv_group.c 的基础上使用了以下 API:
| API | 行号 | 作用 |
|---|---|---|
lv_group_create()/lv_group_add_obj(g, obj) | L110 | 创建分组并把控件加入分组(同一对象不可重复加入) |
lv_group_set_focus_cb(g, cb) | L310 | 设置焦点变化回调,用于滚动页面让焦点控件可见 |
lv_group_focus_obj(obj) | L212 | 将焦点移动到指定对象(会先解除当前焦点并发出LV_EVENT_DEFOCUSED) |
lv_group_set_editing(g, edit) | L320 | 切换/退出编辑模式 |
演示里的focus_cb(lv_demo_keypad_encoder.c)是理解“焦点如何在页面内滚动”的样板:每当焦点对象改变,它读取当前 Tab 页编号,调用lv_page_focus(t1/t2, obj, LV_ANIM_ON)把对应页面滚动到焦点控件位置,从而保证焦点永远可见。
而tv_event_cb(lv_demo_keypad_encoder.c)处理 Tab 切换:当 Tab 页改变或收到LV_EVENT_REFRESH时,先清空分组lv_group_remove_all_objs(g),再依据当前 Tab 把该页的控件结构体数组(selector_objs或textinput_objs)整体重新加入分组。这是“不同页共享一个分组、焦点随页重建”的经典写法。
关键机制三:虚拟键盘与事件驱动流程
ta_event_cb(lv_demo_keypad_encoder.c)完整展示了文本框在无触摸屏下的输入流程:
- 文本框获得焦点(
LV_EVENT_FOCUSED)时显示光标; - 若分组处于编辑模式,则创建虚拟键盘控件
lv_keyboard_create,加入分组并绑定当前文本框lv_keyboard_set_textarea(kb, ta); - 把焦点交给键盘,并通过
lv_page_focus让页面滚动到文本标签处; - 文本框失焦(
LV_EVENT_DEFOCUSED)时隐藏光标。
kb_event_cb(lv_demo_keypad_encoder.c)则负责收尾:先调用lv_keyboard_def_event_cb(kb, e)走默认键盘逻辑,当收到LV_EVENT_APPLY(确认)或LV_EVENT_CANCEL(取消)时,把焦点交还给文本框、删除虚拟键盘并把 Tabview 高度恢复为整屏。
模拟器验证:鼠标滚轮模拟编码器
在没有真实编码器硬件时,仓库自带的 Windows 模拟器工程 LvglSimulator 提供了现成的验证途径——lv_drivers/indev中的 mousewheel.c 把鼠标滚轮映射为编码器:
SDL_MOUSEWHEEL事件:滚轮 y 增量取反后写入enc_diff(即“向下滚 = 正向旋转”);SDL_BUTTON_MIDDLE(鼠标中键)按下/释放映射为编码器的按下/释放状态;mousewheel_read()每次读取后把enc_diff清零,返回false表示无缓冲数据。
对应的 keyboard.c 则把 SDL 键码转换为 LVGL 的LV_KEY_RIGHT / LV_KEY_LEFT / LV_KEY_UP / LV_KEY_DOWN / LV_KEY_ENTER / LV_KEY_ESC等标准键。这意味着在模拟器里用滚轮 + 鼠标中键,或在真实硬件上用键盘方向键 + 回车,就能完整走一遍该演示。
在 HoloCubic 等嵌入式项目中的落地参考
HoloCubic 固件工程位于 2.Firmware/HoloCubic-fw,其中 lv_port_indev.c 给出了真实设备上注册编码器驱动的标准模板:
lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_ENCODER; indev_drv.read_cb = encoder_read; indev_encoder = lv_indev_drv_register(&indev_drv);真实硬件场景下,只需在encoder_read()中填充data->enc_diff(累计旋转步数)与data->state(按下/释放),并在中断服务函数encoder_handler()里累加步数、记录按键状态——这正是本演示在模拟器中所做事情的硬件版。从源码结构可以推断,该演示与平台无关:它只依赖lv_drivers提供的read_cb接口,任何能输出“左右步数 + 按钮状态”的硬件(旋转编码器、游戏手柄拨盘、滚轮等)都能无缝接入。
常见问题与注意事项
- 宏不生效:
LV_USE_DEMO_KEYPAD_AND_ENCODER必须在编译期置 1,且需要把lv_demo_keypad_encoder.c纳入编译(可通过lv_examples.h统一包含头文件,见 lv_examples.h);本仓库中该演示与 HoloCubic 主固件默认是关闭状态。 - 必须先有显示驱动:
lv_indev_drv_register内部依赖默认显示器(见 lv_hal_indev.c),务必在lv_init()与显示、输入驱动初始化之后再调用演示入口。 - 编辑模式与长按:对文本框、微调框等可编辑控件,编码器需通过长按 Enter(代码中为
lv_group_set_editing的切换逻辑)进入编辑模式后,旋转才用于修改数值/选择字符,短按 Enter 仍是“确认/按下”,这是初次上手最易困惑的交互差异。 - 模拟器依赖:若要在 Windows 模拟器中体验键盘/滚轮控制,需要同时打开
LV_EX_KEYBOARD与LV_EX_MOUSEWHEEL两个辅助宏,并链接lv_drivers工程中的键盘与鼠标滚轮驱动。
以上内容全部以当前仓库实际源码与配置为准(LVGL v7.6.1),读者可直接打开 演示源码 逐行对照,或参考 HoloCubic 固件的输入设备移植模板 将这套交互范式落地到自己的编码器硬件上。
- 嵌入式
- 物联网
- 智能硬件
- 嵌入式GUI
- 硬件开发
【免费下载链接】HoloCubic
带网络功能的伪全息透明显示桌面站
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考