实现按键“按下事件”和“释放事件”的通用框架(V0.0.1) 精华

唐佐林
发布于 2020-11-1 10:38
浏览
10收藏

在帖子 《鸿蒙开发板外设控制》直播图文版(2020.10.28) 中我们提到过:“开发板上的按键也可以看作一种 GPIO 外设。” 因此,要捕捉按键的状态(按下或释放)必须操作对应的 GPIO 端口,具体步骤如下:

 

  1. 确定目标按键对应的 GPIO 引脚(可通过设备文档找到对应关系)
  2. 重新定义 GPIO 引脚的功能,数据传输方向设置为输入(WIFI_IOT_GPIO_DIR_IN)
  3. 设置按键事件触发方式(电平触发或边沿触发),并注册回调函数
     

示例代码如下:

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

 

代码说明:

  1. WIFI_IOT_IO_NAME_GPIO_8 端口连接了按键,若需要捕获按键事件需要调用 IoSetFunc() 函数重定义端口功能
  2. 调用 IoSetPull() 函数将端口初始电平拉高(Pull Up)
  3. 调用 GpioRegisterIsrFunc() 函数设置按键事件为边沿触发方式,具体为下降沿触发
  4. 将自定义的 OnButtonPressed() 函数注册为按键回调函数,即:按键被按下时调用此函数

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

综上可知,每个下降沿对应着按键按下,那么上升沿显然对应这按键释放;因此,可设置上升沿触发按键事件,即:按键释放时调用注册的回调函数。

 

代码如下:

GpioRegisterIsrFunc(WIFI_IOT_IO_NAME_GPIO_8,

                                    WIFI_IOT_INT_TYPE_EDGE, 

                                    WIFI_IOT_GPIO_EDGE_RISE_LEVEL_HIGH,

                                    OnButtonReleased, NULL);

 

0K! 接下来我们思考一个问题:如果同时需要捕获按键按下和释放两个事件,如何写代码实现?即:按键按下时 OnButtonPressed() 被调用,按键释放时 OnButtonReleased() 被调用。

 

也许有同学的第一想法会是:注册两次不就完事了吗!!!

用代码描述就是:

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

 

这看起来似乎合情合理,但,这么做是不行滴!!!因为,就目前来说,每个 GPIO 口只能注册一个回调函数;所以,第二次注册的信息将覆盖第一次的注册信息;说得更直白些:这么写,只会在上升沿触发回调 OnButtonReleased() !

 

那么是不是就没法同时捕获按键按下和释放两个事件呢?

 

那到不是,我们可以采用下面的方法实现。

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

 

对应代码如下:

 

初始回调注册:

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

回调函数中切换注册:

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

看到这里,相信大家已经掌握了同时捕获按下事件和释放事件的方法了。然而,即便如此,当程序中需要使用多个按键时,我们不得不反复的编写几乎一模一样的代码。。。这并不是一件有趣的事,我们的时间应该花在更有创造性的工作上!所以,我把上面的方法封装到两个独立的文件(dt_btn_util.hdt_btn_util.c)中,方便大家需要时一键调用!

 

示例代码如下:

实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)-鸿蒙开发者社区

 

是不是很简单?!一行代码即可同时捕获按下事件和释放事件。

 

示例代码和封装好的文件在附件中开箱即用。

 

 

PS:

dt_btn_util.c 的代码实现中加入了一个小技巧,你能看明白吗?可以评论区回复哦!

 

Enjoy it!

分类
dt_btn_demo.zip 3.21K 284次下载
已于2020-11-6 13:18:09修改
14
收藏 10
回复
举报
15条回复
按时间正序
/
按时间倒序
鲜橙加冰
鲜橙加冰

玩过Arduino和Microbit的同学看完一定倍感亲切。

1
回复
2020-11-1 10:52:58
zhang20120603
zhang20120603

这个demon不适合按键,适合接io外设,按键在实际应用中要消抖。

回复
2020-11-1 11:44:17
唐佐林
唐佐林 回复了 zhang20120603
这个demon不适合按键,适合接io外设,按键在实际应用中要消抖。

目前测试来看,还是可用。当然这也只是初始版本,后续可以进行更多迭代。大家可以都参与进来,多提改进方案哈。。。

1
回复
2020-11-1 11:50:04
zhang20120603
zhang20120603

消抖方案怎么做?等待读电平吗?

回复
2020-11-1 13:05:04
zhang20120603
zhang20120603 回复了 zhang20120603
消抖方案怎么做?等待读电平吗?

我一般是中断触发后,5ms后读电平。

回复
2020-11-23 23:22:37
开鸿包月东
开鸿包月东

你好,(void*)arg; 这样写有什么用?

回复
2021-3-26 08:34:07
唐佐林
唐佐林 回复了 开鸿包月东
你好,(void*)arg; 这样写有什么用?

这样写是防止“因为定义了变量,但未使用变量”而带来的警告或错误。

1
回复
2021-3-26 09:05:08
开鸿包月东
开鸿包月东 回复了 唐佐林
这样写是防止“因为定义了变量,但未使用变量”而带来的警告或错误。

这种防止未使用的情况只针对于入参,还是程序的所有变量呢?

回复
2021-3-26 09:14:39
唐佐林
唐佐林 回复了 开鸿包月东
这种防止未使用的情况只针对于入参,还是程序的所有变量呢?

所有程序变量都可以,但是经常用于局部变量,函数参数就是局部变量。

 

有兴趣可以看看我的《C语言进阶剖析教程》。。。

回复
2021-3-26 13:40:08
temjin
temjin

请教若受控端为低电平有效的io。 pwmstart的参数要怎样设置才能有效呢?

回复
2021-4-15 14:28:54
休止符、
休止符、

你好,能给出判断Wi-Fi IoT开发套件上的USER键和OLED显示屏上的S1、S2键中哪一个按键被按下的代码吗,书上的只给出了部分代码,我看不懂,而且显示好多未定义┭┮﹏┭┮

回复
2021-8-30 17:30:16
休止符、
休止符、

static void key234_isr_handler(char *arg){
    static enum button_id btn_record;
    unsigned short data;
    AdcRead(WIFI_IOT_ADC_CHANNEL_2, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0);
    if( (data > keyadc[KEY2].min) && (data < keyadc[KEY2].max)) {
        button_status_callback(KEY2_BUTTON, ACT_DOWN);
        btn_record = KEY2_BUTTON;
    }
    else if( (data > keyadc[KEY3].min) && (data < keyadc[KEY3].max)) {
        button_status_callback(KEY3_BUTTON, ACT_DOWN);
        btn_record = KEY3_BUTTON;
    }
    else if( (data > keyadc[KEY4].min) && (data < keyadc[KEY4].max)) {
        button_status_callback(KEY4_BUTTON, ACT_DOWN);
        btn_record = KEY4_BUTTON;
    }
    if ( data > keyadc[NO_KEY].min){
        button_status_callback(btn_record, ATC_UP);
        GpioRegisterIsrFunc(KEY2_GPIO_ID, WIFI_IOT_INT_TYPE_EDGE,
        WIFI_IOT_GPIO_EDGE_FALL_LEVEL_LOW, key234_isr_handler, NULL);
    }
    else{
        GpioRegisterIsrFunc(KEY2_GPIO_IDK, WIFI_IOT_INT_TYPE_EDGE,
        WIFI_IOT_GPIO_EDGE_RISE_LEVEL_HIGH, key234_isr_handler, NULL);
    }


    /* button queue int*/

    osMessageQueueId_t msgq;
    msgq = osMessageQueueNew(MSGQUE_COUNT, MSG_SIZE, NULL);
    if (msgq == NULL) {
        printf("[%s) failed to creat button queue!\r\n", __FUNCTION__);
        return;
    }
    else if{
        button.msgq = msgq;
    }
}

void button_status_callback(enum button_id id, enum button_action act){
    union button_msg msg;
    uint32_t rst;
    DEBUG_MSG("[$s] send button:%d act:%d.\r\n", __FUNCTION__, id, act);
    msg.msg.act = act;
    msg.msg.id = id;

    rst = osMessageQueuePut(button.msgq, &msg.value, 0, 0);
    if( rst != LOS_OK){
        printf("[%s] failed to put queue, lost a button action!\r\n", __FUNCTION__);
    }
}

static void button_thread_task(void* parameter){
    union button_msg msg;
    while(1)
    {
        if(osMessageQuequeGet(button.msgq, &msg.value, 0, ;LOS_WAIT_FOREVER) == LOS_OK){
            button_pressed_check();
        }
    }
}

回复
2021-8-30 17:30:52
休止符、
休止符、

请问为什么我会编译失败?

代码如下

#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifiiot_adc.h"
#include "wifiiot_errno.h"
#include "dt_btn_util.h"

static void OnButtonPressed(char* arg){
    (void) arg;

    WifiIotGpioValue value = WIFI_IOT_GPIO_VALUE0;
    GpioGetInputVal(WIFI_IOT_GPIO_IDX_5, &value);
    printf("电压值=%d\r\n",value);
}

static void OnButtonReleased(char* arg){
    (void) arg;
    printf("按钮已经释放\n");
}

static void* ButtonTask(const char* arg){
    int ret = 0;
    (void)arg;
    ret += GpioInit();
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_5, WIFI_IOT_IO_FUNC_GPIO_5_GPIO);
    GpioSetDir(WIFI_IOT_GPIO_IDX_5, WIFI_IOT_GPIO_DIR_OUT);
    IoSetPull(WIFI_IOT_IO_NAME_GPIO_5, WIFI_IOT_IO_PULL_UP);

    printf("[dt4sw] DTBtnDemo_Task()\n");

    /* 设置GPIO_8按键的回调函数,同时需要响应按下和释放两个事件 */
    ret += DTEnableButton(WIFI_IOT_IO_NAME_GPIO_5, OnButtonPressed, OnButtonReleased);

    if( ret == 0 )
    {
        while(1) 
        {
            usleep(100000);
        }

        DTDisableButton(WIFI_IOT_IO_NAME_GPIO_8);  // 取消按键事件
        DTDisableButton(WIFI_IOT_IO_NAME_GPIO_5);  // 取消按键事件
    }
    else
    {
        printf("[dt4sw] Gpio operation falied!\n");
    }

    return NULL;
}

static void DTBtnDemo_Entry(void)
{
    osThreadAttr_t attr = {0};

    printf("[dt4sw] DTBtnDemo_Entry()\n");

    attr.name = "ButtonTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024;
    attr.priority = osPriorityNormal;

    if (osThreadNew((osThreadFunc_t)ButtonTask, NULL, &attr) == NULL)
    {
        printf("[dt4sw] Falied to create ButtonTask!\n");
    }
}

SYS_RUN(DTBtnDemo_Entry);

 

回复
2021-8-31 17:28:56
唐佐林
唐佐林 回复了 休止符、
请问为什么我会编译失败? 代码如下 #include #include #include "ohos_init.h"#include "cmsis_os2.h"#include "wifiiot_adc.h"#include "wifiiot_errno.h"#include "dt_btn_util.h" static void OnButtonPressed(char* arg){ (void) arg; WifiIotGpioValue value = WIFI_IOT_GPIO_VALUE0; GpioGetInputVal(WIFI_IOT_GPIO_IDX_5, &value); printf("电压值=%d\r\n",value);}...

你好,更多的编译错误能不能贴出来看看。

回复
2021-9-8 17:34:33
唐佐林
唐佐林 回复了 休止符、
你好,能给出判断Wi-Fi IoT开发套件上的USER键和OLED显示屏上的S1、S2键中哪一个按键被按下的代码吗,书上的只给出了部分代码,我看不懂,而且显示好多未定义┭┮﹏┭┮

我在后续的文章给出了解决方案。

 

你也可以看这里的开源实现:https://gitee.com/delphi-tang/dtframe

回复
2021-9-8 17:36:22
回复
    相关推荐