- 嵌入式
- 物联网
- 智能硬件
- 嵌入式GUI
- 硬件开发
【免费下载链接】HoloCubic
带网络功能的伪全息透明显示桌面站
导读
本篇技术指南以 HoloCubic 仓库 LvglSimulator 工程中的lv_ex_btnmatrix示例文档为骨架,围绕 LVGL 的按钮矩阵(Button Matrix,lv_btnmatrix)控件,完整讲解其 map 字符串数组的编排规则、按钮宽度控制、可勾选(Checkable)状态位以及LV_EVENT_VALUE_CHANGED事件回调,并结合 LVGL 源码剖析控制位(ctrl bits)的存储与事件发送机制。读完本文,你将能在自己的 LVGL 界面(包括 HoloCubic 的伪全息桌面站 UI)中快速搭建数字键盘、菜单工具栏等密集型按钮布局,并理解其底层实现原理。
一、示例文档骨架:Simple Button matrix
该示例位于3.Software/LvglSimulator/vs2019_proj/lv_examples/src/lv_ex_widgets/lv_ex_btnmatrix/index.rst,主题为Simple Button matrix,通过.. lv_example::指令引用lv_ex_widgets/lv_ex_btnmatrix/lv_ex_btnmatrix_1(C 语言示例);文档同时保留了 MicroPython 章节,标注 "No examples yet."。仓库中实际同时提供 C 与 MicroPython 两个实现文件:
- C 语言实现
- MicroPython 实现
- 运行效果图(320x240,模拟器渲染结果)
说明:
index.rst中 MicroPython 章节标注 "No examples yet.",但仓库lv_ex_btnmatrix目录下实际已包含lv_ex_btnmatrix_1.py,两者可对照阅读。函数原型统一声明于 lv_ex_widgets.h(void lv_ex_btnmatrix_1(void);),由模拟器工程统一调用。
二、示例完整代码走读
C 版本完整代码如下(lv_ex_btnmatrix_1.c):
#include "../../../lv_examples.h" #include <stdio.h> #if LV_USE_BTNMATRIX static void event_handler(lv_obj_t * obj, lv_event_t event) { if(event == LV_EVENT_VALUE_CHANGED) { const char * txt = lv_btnmatrix_get_active_btn_text(obj); printf("%s was pressed\n", txt); } } static const char * btnm_map[] = {"1", "2", "3", "4", "5", "\n", "6", "7", "8", "9", "0", "\n", "Action1", "Action2", ""}; void lv_ex_btnmatrix_1(void) { lv_obj_t * btnm1 = lv_btnmatrix_create(lv_scr_act(), NULL); lv_btnmatrix_set_map(btnm1, btnm_map); lv_btnmatrix_set_btn_width(btnm1, 10, 2); /*Make "Action1" twice as wide as "Action2"*/ lv_btnmatrix_set_btn_ctrl(btnm1, 10, LV_BTNMATRIX_CTRL_CHECKABLE); lv_btnmatrix_set_btn_ctrl(btnm1, 11, LV_BTNMATRIX_CTRL_CHECK_STATE); lv_obj_align(btnm1, NULL, LV_ALIGN_CENTER, 0, 0); lv_obj_set_event_cb(btnm1, event_handler); } #endifMicroPython 版本(lv_ex_btnmatrix_1.py)与 C 版本一一对应:
def event_handler(obj, event): if event == lv.EVENT.VALUE_CHANGED: txt = obj.get_active_btn_text() print("%s was pressed" % txt) btnm_map = ["1", "2", "3", "4", "5", "\n", "6", "7", "8", "9", "0", "\n", "Action1", "Action2", ""] btnm1 = lv.btnm(lv.scr_act()) btnm1.set_map(btnm_map) btnm1.set_btn_width(10, 2) # Make "Action1" twice as wide as "Action2" btnm1.align(None, lv.ALIGN.CENTER, 0, 0) btnm1.set_event_cb(event_handler)示例演示了按钮矩阵的核心用法:创建对象、设置按钮 map、调整按钮相对宽度、为按钮设置可勾选状态,并监听数值变化事件。
三、map 字符串数组:按钮布局的“图纸”
按钮矩阵的核心是lv_btnmatrix_set_map()(C)或set_map()(Python),其入参是一个以""(空字符串)结尾的const char *数组。数组语义规则在 lv_btnmatrix.h 中有明确定义:
- 普通字符串:代表一个按钮,按钮文本即该字符串;
"\n":强制换行,其后的字符串进入下一行;"":数组结束标志,表示 map 终止(注意不是按钮)。
示例中btnm_map由 14 个元素组成,布局为两行数字键盘(每行 5 个键)+ 一行两个操作按钮:
1 2 3 4 5 6 7 8 9 0 Action1 Action2从源码结构看,lv_btnmatrix_create()创建对象时默认使用内置 maplv_btnmatrix_def_map[] = {"Btn1", "Btn2", "Btn3", "\n", "Btn4", "Btn5", ""}(见 lv_btnmatrix.c),即默认是 2 行 3 + 2 的按钮布局。一旦调用lv_btnmatrix_set_map(),按钮会按新 map 重新创建。
3.1 按钮索引的计数方式
需要特别留意:按钮索引从 0 开始,且不把"\n"计入索引。例如示例中 "Action1" 的索引是 10,"Action2" 的索引是 11。这一点在set_btn_width(10, 2)、set_btn_ctrl(btnm1, 10, ...)等调用中直接体现,也与 lv_btnmatrix.h 中 "0 based index of the button to modify. (Not counting new lines)" 的注释一致。
四、按钮宽度:相对宽度机制
lv_btnmatrix_set_btn_width(btnm1, 10, 2); /* Action1 宽度是 Action2 的 2 倍 */宽度值width的取值范围是[1..7],表示该按钮相对同一行内其他按钮的宽度倍数。示例将索引 10 的 "Action1" 设为 2,而 "Action2"(索引 11)保持默认宽度 1,因此视觉上 "Action1" 占满 2/3 行宽、"Action2" 占 1/3 行宽。
宽度值实际存放在每个按钮的 ctrl 位低 3 位(LV_BTNMATRIX_WIDTH_MASK 0x0007,见 lv_btnmatrix.h)。实现上lv_btnmatrix_set_btn_width()会先清除低 3 位再写入新宽度(lv_btnmatrix.c):
ext->ctrl_bits[btn_id] &= (~LV_BTNMATRIX_WIDTH_MASK); ext->ctrl_bits[btn_id] |= (LV_BTNMATRIX_WIDTH_MASK & width);布局计算时通过get_button_width()(lv_btnmatrix.c)读取该低 3 位,把每行所有按钮的宽度单位累加后按比例分配像素宽度。
注意:头文件注释提示,
set_btn_width属于相对耗时的“动态修改”接口,会触发矩阵重新布局;如需在创建时就指定多个按钮宽度,推荐使用lv_btnmatrix_set_ctrl_map()一次性传入 ctrl 数组(宽度位与控制位可通过按位或组合,例如width | LV_BTNMATRIX_CTRL_CHECKABLE,见 lv_btnmatrix.h)。
五、按钮控制位(ctrl bits):隐藏、禁用与勾选
控制位定义于 lv_btnmatrix.h,类型为lv_btnmatrix_ctrl_t(uint16_t)。常用标志如下:
| 控制位 | 值 | 含义 |
|---|---|---|
LV_BTNMATRIX_CTRL_HIDDEN | 0x0008 | 隐藏该按钮(不显示、不参与布局) |
LV_BTNMATRIX_CTRL_NO_REPEAT | 0x0010 | 长按时不重复触发 |
LV_BTNMATRIX_CTRL_DISABLED | 0x0020 | 禁用按钮(灰显且不可点击) |
LV_BTNMATRIX_CTRL_CHECKABLE | 0x0040 | 按钮可以被勾选(按下后保持选中状态) |
LV_BTNMATRIX_CTRL_CHECK_STATE | 0x0080 | 按钮当前处于勾选(按下)状态 |
LV_BTNMATRIX_CTRL_CLICK_TRIG | 0x0100 | 1:在 CLICK 时发送选中事件;0:在 PRESS 时发送 |
示例中用到了后两个:
lv_btnmatrix_set_btn_ctrl(btnm1, 10, LV_BTNMATRIX_CTRL_CHECKABLE); /* "Action1" 可勾选 */ lv_btnmatrix_set_btn_ctrl(btnm1, 11, LV_BTNMATRIX_CTRL_CHECK_STATE); /* "Action2" 初始即被勾选 */配合lv_btnmatrix_set_one_check()可将按钮矩阵变成“单选器”(同一时间仅一个按钮保持勾选),这与lv_tabview、lv_msgbox的底层按钮布局共享同一套机制(在 lvgl/src/lv_widgets/lv_tabview.h、lv_msgbox.h 中均有 “lv_btnmis required” 的编译期依赖检查)。
六、事件处理:捕捉按钮点击
if(event == LV_EVENT_VALUE_CHANGED) { const char * txt = lv_btnmatrix_get_active_btn_text(obj); printf("%s was pressed\n", txt); }按钮矩阵在被点击时向事件回调发送LV_EVENT_VALUE_CHANGED。lv_btnmatrix_get_active_btn_text()(声明于 lv_btnmatrix.h)返回最近一次被激活(按下/释放)按钮的文本,配合lv_btnmatrix_get_active_btn()可拿到按钮索引,进而读取 ctrl 状态、动态修改宽度或文本。
从源码看,LV_EVENT_VALUE_CHANGED在信号处理函数lv_btnmatrix_signal()中多次由lv_event_send(btnm, LV_EVENT_VALUE_CHANGED, &b)触发(见 lv_btnmatrix.c),覆盖按下、释放、重复按压等不同点击路径,因此回调内无需区分具体来源,统一读取get_active_btn_text()即可。
七、如何在 HoloCubic 工程中启用并运行
按钮矩阵示例通过#if LV_USE_BTNMATRIX编译开关包裹(见 lv_ex_btnmatrix_1.c)。在 LvglSimulator 工程的 lv_conf.h 中LV_USE_BTNMATRIX已默认开启:
/*Button matrix (dependencies: -)*/ #define LV_USE_BTNMATRIX 1运行方式:
- 在 VS2019 中打开 lvgl_similator.sln,编译并运行 LvglSimulator(基于 SDL2 + LVGL,工程配置见 lvgl_similator.vcxproj);
- 在模拟器代码中调用
lv_ex_btnmatrix_1()(原型位于 lv_ex_widgets.h),即可看到上述两行数字键盘 + 两个操作按钮的界面,点击按钮时终端输出XXX was pressed。
在 HoloCubic 固件(2.Firmware/HoloCubic-fw)中,GUI 由 LVGL 驱动(见src/lv_cubic_gui.c与src/setup_scr_home.c等),按钮矩阵非常适合用来实现设置菜单、数字输入面板等需要大量规则按钮的场景——每个按钮只占用一个字符串数组元素,内存占用远低于逐个创建独立按钮对象。启用方式与模拟器一致,在固件侧 lv_conf.h 中开启LV_USE_BTNMATRIX即可。
八、常见误区与小结
- map 数组必须保持存活:头文件明确提示按钮矩阵只是“持有”map 指针,字符串数组在矩阵生命周期内不能被释放或改写(见 lv_btnmatrix.h),因此示例把
btnm_map声明为静态数组; - 索引不含
"\n":给按钮设置宽度/控制位/文本时,索引从 0 计数且跳过换行符,写错索引会作用到错误按钮; - 宽度上限 7:
lv_btnmatrix_set_btn_width()的宽度参数范围为[1..7](低 3 位掩码0x0007限制); - 勾选三要素:要让按钮具备“按下保持选中”行为,至少需要
LV_BTNMATRIX_CTRL_CHECKABLE;读取当前勾选状态用LV_BTNMATRIX_CTRL_CHECK_STATE,实现单选互斥则调用lv_btnmatrix_set_one_check()。
综上,lv_ex_btnmatrix示例虽短,却完整覆盖了按钮矩阵控件的 map 编排、相对宽度、控制位与事件回调四大核心知识点;结合 lv_btnmatrix.h 与 lv_btnmatrix.c 源码,即可在 HoloCubic 的 LVGL 界面中高效复用这套规则化的按钮布局能力。
- 嵌入式
- 物联网
- 智能硬件
- 嵌入式GUI
- 硬件开发
【免费下载链接】HoloCubic
带网络功能的伪全息透明显示桌面站
相关推荐
HoloCubic 固件 LVGL 之 lv_btnmatrix 按钮矩阵:Simple Button matrix 示例与源码级 API 解析
HoloCubic 固件 LVGL 之 lv_btnmatrix 按钮矩阵:Simple Button matrix 示例与源码级 API 解析 HoloCub
嵌入式物联网智能硬件嵌入式GUI硬件开发LVGL v7 按钮矩阵(Button Matrix)示例全解析:从 map 布局到事件处理(HoloCubic 仓库实战)
LVGL v7 按钮矩阵(Button Matrix)示例全解析:从 map 布局到事件处理(HoloCubic 仓库实战) 本文以 HoloCubic 仓库内
嵌入式物联网智能硬件嵌入式GUI硬件开发X-TRACK 模拟器中的 LVGL 按钮矩阵(lv_btnmatrix)控件完全指南
X TRACK 模拟器中的 LVGL 按钮矩阵(lv_btnmatrix)控件完全指南 导读 本文是 X TRACK 开源 GPS 码表项目所携带的 LVGL
智能硬件嵌入式硬件开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考