Melis4.0[D1s]:7.lvgl添加物理按键

news2025/6/10 4:35:29

文章目录

  • 1.lvgl注册keypad驱动
    • 1.1 在melis的ADC按键中发送消息
      • 1.1.1 创建消息队列,并初始化
      • 1.1.2 扫描按键时,发送按下和松开消息
    • 1.2 编写读取按键的回调函数
    • 1.3 lvgl按键驱动注册
  • 2.在gui中测试物理按键效果
    • 2.1 测试效果

参考资料:
1.韦东山老师B站视频:3-3-1使用物理按键代替触摸(groups)
2.作者:zhbi98,文章:lvgl8.x 对接实体按键驱动
3.百问网lvgl中文文档:Keypad and encoder(键盘和编码器)

搞了2天,没有搞定D1s在Melis下的I2c驱动(gt911触摸屏)。先测试物理按键。

1.lvgl注册keypad驱动

1.1 在melis的ADC按键中发送消息

在前面的文章中Melis4.0[D1s]:1.启动流程(与adc按键初始化相关部分)跟踪笔记,已经做好ADC按键的驱动,直接在驱动中发送消息。
如何在rt-thread中使用消息队列发送消息,请参考官方资料: RT-Thread API参考手册-消息队列。

1.1.1 创建消息队列,并初始化

相关代码在 《D1s-Melis/ekernel/drivers/drv/source/input/keyboard/sunxi_keyboard.c》 中。完整内容点击链接 sunxi_keyboard.c。

/* 消息队列控制块 */
 struct rt_messagequeue mq;
/* 消息队列中用到的放置消息的内存池 */
static rt_uint8_t msg_pool[2048];
int sunxi_keyboard_init(void)
{
......
    rt_err_t result;
    /* 初始化消息队列 */
    result = rt_mq_init(&mq,
                        "mqt",
                        &msg_pool[0],               /* 内存池指向msg_pool */
                        1,                          /* 每个消息的大小是 1 字节 */
                        sizeof(msg_pool),           /* 内存池的大小是msg_pool的大小 */
                        RT_IPC_FLAG_FIFO);          /* 如果有多个线程等待,按照先来先得到的方法分配消息 */
    if (result != RT_EOK)
    {
        rt_kprintf("init message queue failed.\n");
        return -1;
    }
    return 0;
}

1.1.2 扫描按键时,发送按下和松开消息

这部分代码也在 sunxi_keyboard.c 中。

int keyboard_irq_callback(uint32_t data_type, uint32_t data)
{
    if (data_type == GPADC_UP && key_flag == 1)
    {
......
                            /* 发送按键松开消息到消息队列中 */
                            key_data->compare_later = 0xf6;
                            result = rt_mq_send(&mq, &(key_data->compare_later), 1);
                            if (result != RT_EOK)
                            {
                                rt_kprintf("rt_mq_send ERR\n");
                            }
    }
    .......
                            __log("key: %d",key_data->compare_before);
                            /* 发送按键按下消息到消息队列中 */
                            result = rt_mq_send(&mq, &(key_data->compare_before), 1);
                            if (result != RT_EOK)
                            {
                                rt_kprintf("rt_mq_send ERR\n");
                            } 
    return 0;
}

1.2 编写读取按键的回调函数

这里只是通过消息队列读取按键按下和松开的消息。

收到按键按下的消息值为0,1,2,3,4;
收到按键松开的消息值为0xF6.

extern   struct rt_messagequeue mq;
uint8_t   msgbyte;
static void melis_keypad_driver_read_cb(lv_indev_drv_t* indev_drv,    lv_indev_data_t* data)
{
    /* 从消息队列中接收消息 */
    if (rt_mq_recv(&mq, (void *)&msgbyte, sizeof(msgbyte), RT_WAITING_NO) == RT_EOK)
    {
        rt_kprintf("read_cb: recv msg:%d\n", msgbyte);
        data->state = LV_INDEV_STATE_PR;
        switch (msgbyte)
        {
        case 0:
            data->key = LV_KEY_PREV;            break;
        case 1:
            data->key = LV_KEY_NEXT;            break;
        case 2:
            data->key = LV_KEY_ENTER;            break;
        case 3:
            data->key = LV_KEY_ENTER;            break;
        case 4:
            data->key = LV_KEY_ESC;            break;
        case 0xf6:
          data->state = LV_INDEV_STATE_REL;	break;
        default:
            break;
        }
    }
}

1.3 lvgl按键驱动注册

完成前面的准备工作后,就可以在 lv_main()中注册按键驱动,完成lvgl物理按键的初始化。
直接拷贝韦东山老师在windows下键盘的驱动,00_lv_100ask_sim_codeblocks_win/lv_drivers/win32drv/win32drv.c 完整代码点击链接:

    static lv_indev_drv_t keypad_driver;
    lv_indev_drv_init(&keypad_driver);
    keypad_driver.type = LV_INDEV_TYPE_KEYPAD;
    keypad_driver.read_cb = lv_win32_keypad_driver_read_callback;
    lv_win32_keypad_device_object = lv_indev_drv_register(&keypad_driver);

把上面的代码稍作改动。

lv_win32_keypad_driver_read_callback --> melis_keypad_driver_read_cb
lv_win32_keypad_device_object --> g_keypad_device_object 【 说明:这个变量其他文件需要用到】

于是,改动后的代码如下(完整代码点击链接 lv_main.c):

#define   LV_USE_GPADC    1
lv_indev_t* g_keypad_device_object ;
static lv_disp_t * hal_init(void  )
{
......
#if   LV_USE_GPADC    
    static lv_indev_drv_t keypad_driver;
    lv_indev_drv_init(&keypad_driver);
    keypad_driver.type = LV_INDEV_TYPE_KEYPAD;
    keypad_driver.read_cb = melis_keypad_driver_read_cb;
    g_keypad_device_object = lv_indev_drv_register(&keypad_driver);
#endif
	lv_disp_t * disp = NULL;
  return disp;
}

2.在gui中测试物理按键效果

在gui中应用物理按键(groups)需要3个步骤:

1.要创建一个 组(Groups) : lv_group_t * g = lv_group_create();
2.然后将一个对象添加到 组(Groups) 中: lv_group_add_obj(g, obj); 也可以指定默认组:lv_group_set_default(g);
3.最后要将组(Groups)与输入设备相关联: lv_indev_set_group(g_keypad_device_object , g);

物理按键中有3个必备的按键消息,这3个消息就可以实现焦点切换和触发焦点对象:

消息名称含义
LV_KEY_NEXT聚焦到下一个对象
LV_KEY_PREV聚焦到上一个对象
LV_KEY_ENTER触发 LV_EVENT_PRESSED/CLICKED/LONG_PRESSED 等事件

其他消息:

消息名称含义
LV_KEY_UP增加值或向上移动
LV_KEY_DOWN减少值或向下移动
LV_KEY_RIGHT增加值或向右移动
LV_KEY_LEFT减少值或向左移动
LV_KEY_ESC关闭或退出(例如关闭 下拉列表)
LV_KEY_DEL删除(例如 文本区域 中右侧的字符)
LV_KEY_BACKSPACE删除左边的一个字符(例如在文本区域)
LV_KEY_HOME跳到开头/顶部(例如在 文本区域)
LV_KEY_END跳到最后(例如在 文本区域))

这里我使用了官方例程 lv_example_btn_1.c。

extern lv_indev_t* g_keypad_device_object ;
static void event_handler(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);

    if(code == LV_EVENT_CLICKED) {
        LV_LOG_USER("Clicked");
    }
    else if(code == LV_EVENT_VALUE_CHANGED) {
        LV_LOG_USER("Toggled");
    }
}

void lv_example_btn_1(void)
{
    lv_obj_t * label;

    // 创建一个组,稍后将需要使用键盘或编码器或按钮控制的部件(对象)添加进去,并且将输入设备和组关联
    // 如果将这个组设置为默认组,那么对于那些在创建时会添加到默认组的部件(对象)就可以省略 lv_group_add_obj()
    lv_group_t * g = lv_group_create();

    // 将上面创建的组设置为默认组
    // 如果稍后创建的部件(对象),使用默认组那必须要在其创建之前设置好默认组,否则不生效
    lv_group_set_default(g);

    lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
    lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);

    label = lv_label_create(btn1);
    lv_label_set_text(label, "Button");
    lv_obj_center(label);

    lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
    lv_obj_add_event_cb(btn2, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 40);
    lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
    lv_obj_set_height(btn2, LV_SIZE_CONTENT);

    label = lv_label_create(btn2);
    lv_label_set_text(label, "Toggle");
    lv_obj_center(label);

    lv_indev_set_group(g_keypad_device_object, g);
}

2.1 测试效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412398.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第七章 基于 RNN 的生成文本

目录7.1 使用语言模型生成文本7.1.1 使用 RNN 生成文本的步骤7.1.2 文本生成的实现7.1.3 更好的文本生成7.2 seq2seq 模型7.2.1 seq2seq 的原理7.2.2 时序数据转换的简单尝试7.2.3 可变长度的时序数据7.2.4 加法数据集7.3 seq2seq 的实现7.3.1 Encoder类7.3.2 Decoder类7.3.3 S…

静态时序分析Static Timing Analysis3——特殊路径(多周期、半周期、伪路径)的时序检查

文章目录前言一、多周期路径1、建立时间检查2、保持时间检查二、半周期路径1、建立时间检查2、保持时间检查三、伪路径前言 2023.4.12 一、多周期路径 对于建立时间,要设置为N(向后移);对于保持时间,要设置为N-1&…

9.8.0.32:ProEssentials数据可视化2D和3D图表:Crack

下面是我们的Winforms、Wpf、C MFC、VCL、ActiveX图表组件示例项目中的屏幕捕获。 有关下图,请参见我们的示例项目和演示中的030。 ProEssentials Winforms 图表, WPF 图表, C/MFC/VCL 图表. Gigasoft拥有20多年帮助企业开发大型客户端和嵌入式图表项目的经验。图…

JavaScript基础-02

常量(字面量):数字和字符串 常量也称之为“字面量”,是固定值,不可改变。看见什么,它就是什么。 常量有下面这几种: 数字常量(数值常量)字符串常量布尔常量自定义常量…

传输线的物理基础(九):N 截面集总电路模型

理想的传输线电路元件是一种分布式元件,可以非常准确地预测实际互连的测量性能。下图显示了 1 英寸长传输线在频域中的实测阻抗和仿真阻抗对比。我们看到甚至高达 5 GHz 的测量带宽也能达成出色的协议。 1英寸长、50欧姆传输线的测量(圆圈)和…

Java实现hdfs的8个api操作

Java实现hdfs的8个api操作一、预处理准备1. 配置本地hadoop3.1.3目录文件2. 配置环境变量二、Maven项目依赖三、Java源代码四、api操作的实现1. 实现前的准备2. 创建hdfs上的路径3. 删除hdfs上的路径4. 创建hdfs文件并写入数据5. 删除hdfs上的文件6. hdfs上的文件移动路径并改名…

算法笔记:Frechet距离度量

曲线之间相似性的度量,它考虑了沿曲线的点的位置和顺序 1 概念 1.1 直观理解 主人走路径A,狗走路径B,他们有不同的配速方案主人和狗各自走完这两条路径过程中所需要的最短狗绳长度 (在某一种配速下需要的狗绳长度)&a…

MySQL-高可用MHA(二)

目录 🍁通过keepalived方式 🍂安装keepalived 🍃防火墙策略 🍃keep配置文件 🍂MHA应用keepalived 🍃停止MHA 🍃启动MHA 🍃检查状态 🍃测试 🍁通过脚本实现VIP…

数据结构——线段树

线段树的结构 线段树是一棵二叉树,其结点是一条“线段”——[a,b],它的左儿子和右儿子分别是这条线段的左半段和右半段,即[a, (ab)/2 ]和[(ab)/2 ,b]。线段树的叶子结点是长度为1的单位线段[a,a1]。下图就是一棵根为[1,10]的线段树&#xff1…

真题详解(UML图)-软件设计(四十七)

真题详解(Flynn分类)-软件设计(四十六)https://blog.csdn.net/ke1ying/article/details/130072198 某搜索引擎在使用过程中,若要增加接受语音输入的功能,使用户可以通过语音来进行搜索,此时对应系统进行____维护? 正确…

基于逻辑回归构建肿瘤预测模型

使用逻辑回归构建肿瘤预测模型 描述 乳腺癌数据集包括569个样本,每个样本有30个特征值(病灶特征数据),每个样本都属于恶性(0)或良性(1)两个类别之一,要求使用逻辑回归&…

Python学习笔记--函数

(一) 函数介绍 1. 函数:是组织好的,可重复使用的,用来实现特定功能的代码段。 eg. len():实现统计长度这一特定功能的代码段。 2. 函数好处: * 将功能封装在函数内,可随时随地重复…

eSearch使用教程大全

下载: https://www.xsoftnet.com/share/a0002tNuuOswc.html产品: eSearch 即拥有 截屏OCR搜索翻译贴图以图搜图录屏功能。 截屏 框选裁切 框选大小位置可调整(支持方向键或 WASD) 框选大小栏可输入四则运算式调整 取色器 放大镜 画笔(自由画…

Kafka系统整理 一

一、Kafka 概述 1.1 定义 Kafka传统定义:Kafka是一个分布式的基于发布/订阅模式的消息队列 (Message Queue), 主要应用于大数据实时处理领域。 kafka最新定义:kafka是一个开源的分布式事件流平台(Event Streaming Platform), 被…

PostgreSQL下载、安装、Problem running post-install step的解决、连接PostgreSQL

我是参考《SQL基础教程》来安装的,关于书的介绍、配套视频、相关代码可以参照下面的链接: SQL基础教程(第2版) (ituring.com.cn) 一、下载 我直接打开书中的下载链接时,显示的是这个界面: You are not …

Flink的窗口机制

窗口机制 tumble(滚动窗口) hop(滑动窗口) session(会话窗口) cumulate(渐进式窗口) Over(聚合窗口) 滚动窗口(tumble) 概念 滚…

002:Mapbox GL更改大气、空间及星星状态

第002个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中更改大气、空间及星星状态 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共71行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:…

java截取字符串的方法

在 Java中,截取字符串的方法有多种,下面我们来介绍一下。 第一种方法,在 class中直接获取字符串, class. str是一个内置的字符串类,它有一个方法可以截取字符串,这个方法叫做 class. str (&…

linux入门---缓冲区

查看问题 首先大家来看看下面这段代码 1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6 printf("i am printf\n");7 fprintf(stdout,"i am fprintf\n");8 fputs("i am fputs\n",…

Unity --- 物理引擎 --- 触发器 与 碰撞器详解

碰撞器补充讲解 对第一个条件进行补充 --- 不仅要两者都具有碰撞组件&#xff0c;同时还需要两者的碰撞组件中都没有勾选 Is Trigger属性 1.上一篇文章中说了那么多&#xff0c;其实也可以总结为两个碰撞条件 --- a.两个游戏物体都具有碰撞器组件 &#xff0c;如果没有的话&…