美文网首页
NodeMCU(ESP8266)按键中断实现单击-双击-长按功能

NodeMCU(ESP8266)按键中断实现单击-双击-长按功能

作者: Lengff | 来源:发表于2021-12-14 09:29 被阅读0次

    2021-12-18 更新

    B站网友 柳桥风起 分享了一个开源库使用效果更佳,OneButton 这个库功能更齐全,可直接使用,我个人分享的还存在bug,看看就好了,这里也贴出一段个人写的demo代码,当然更推荐的是到github上看原作者代码说明

    
    #include <OneButton.h>
    
    OneButton btn = OneButton(D3, false, false);
    
    // 记录按键按下时间
    uint32_t clicktime = 0;
    
    /**
     * 处理单击
     */
    static void singleClick() {
      Serial.println("按键单击");
    }
    
    /**
     * 处理双击
     */
    static void doubleClick() {
      Serial.println("按键双击");
    }
    
    /**
     * 按键长按开始做的事情
     */
    static void longClickStart() {
      Serial.println("按键长按开始");
      clicktime = millis();
    }
    
    /**
     * 处理按键长按
     */
    static void longClick() {
      Serial.println("按键长按结束");
      Serial.println("按键按下时间:");
      // 这里为啥加1000? 因为按键长按开始时间时按下一秒后开始计算的,所以就要加上我们原本已经按下的一秒种
      Serial.print(millis()-clicktime+1000);
      // 重置按下时间
      clicktime = 0;
    }
    
    void setup() {
      Serial.begin(115200);
      // 添加单击事件函数
      btn.attachClick(singleClick);
      // 添加双击事件函数
      btn.attachDoubleClick(doubleClick);
      // 添加长按事件函数
      btn.attachLongPressStop(longClick);
      // 添加按下事件函数
      btn.attachLongPressStart(longClickStart);
    }
    
    void loop() {
      btn.tick();
    }
    

    背景

    很多时候我们的设备就只有一个按键,但是我们需要的功能却比较多,所以就会围绕一个按键实现多种交互功能,单击,双击,长按可能就是最常见的几种交互了,所以我就想着用nodeMcu(esp8266)也搞一个出来,中途遇到很多的问题,为此便写下这篇笔记记录下来,分享给大家!

    实现方式

    如果只是要实现按键单击功能是比较简单的,只需要读取对应的GPIO的电平信号即可,但是如果我们要实现案件双击,长按此时单纯靠读取电平信号则无法解决此问题。需要使用外部中断来处理按键的状态值。 大概思路就是根据按键按下的时间,和按键回弹的时间,来判断按键是否是第一次按压和按压两次或者长按(这里我也不想不出好的文字来描述此过程,大家看代码就懂了的)!

    实现功能

    1. 单击切换LED显示状态
    2. 双击切换LED显示模式 (模式1:亮/灭 、模式2:闪烁/常亮)
    3. 长按超过3秒重启系统

    电路图及原理

    电路图和原理部分我直接搬运此文章的 ESP8266-12F 中断 ,有兴趣欢迎到原作者处查阅,我只是一个搬运工记录一下。

    电路图

    电路图

    这里的电阻10K左右即可,大到20多K也没问题,切勿放一个小电阻,形成短路主板无法正常工作

    外部中断

    基于ESP8266的NodeMcu的数字IO的中断功能是通过attachInterrupt,detachInterrupt函数所支持的。
    除了D0/GPIO16,中断可以绑定到任意GPIO的引脚上【D0-D10】。
    所支持的标准中断类型有:

    • CHANGE(改变沿,电平从低到高或者从高到低)
    • RISING(上升沿,电平从低到高)
    • FALLING(下降沿,电平从高到低)

    attachInterrupt(pin, function, mode); 设置触发中断的引脚

    • pin:要设置中断编号,注意,这里不是引脚编号
    • function:中断发生时运行的函数, 这个函数不带任何参数,不返回任何内容
    • Interrupt type/mode:它定义中断被触发的条件方式
      • CHANGE:改变沿,引脚电平从低变为高或者从高变为低时触发中断。
      • RISING:上升沿,引脚电平从低变为高时触发中断。
      • FALLING:下降沿,引脚电平从高变为低时触发中断
    • 返回值: 无

    detachInterrupt(pin); 取消指定引脚的中断

    • pin:中断号
    • 返回值: 无

    digitalPinToInterrupt(pin);获取指定引脚的中断号

    • pin:要获取中断号的GPIO引脚
    • 返回值: 中断号

    引脚对应的中断号:

    • D1 -> 5
    • D2 -> 4
    • D4 -> 2
    • D5 -> 14
    • D6 -> 12
    • D7 -> 13
    • D8 -> 15

    代码

    代码部分参考自CSDN文章Arduino 触摸按键:实现单击,双击,长按功能,稳定无抖动。
    感觉作者的代码分享,欢迎查阅原作者代码分享,我只是搬运了一下代码

    
    #include <ESP8266WiFi.h>
    
    // 按键设置在D8(gpio15)引脚
    int touchPin = D2;
    // 模式:0:LED亮和灭   1. LED常亮或闪烁    2. 重启系统
    int mode = 0;
    // 模式0:亮/灭   模式1: 常亮/闪烁
    bool isshow = false;
    // 按键按下去的时间     按键按起来的时间    第一次按按键的时间
    long touchDownTime = 0, touchUpTime = 0, firstTouchTime = 0;
    // 是否单击   是否双击
    bool isOne = 0, isDouble = 0;
    // 按键状态 0:无任何操作,1:单机  2: 双击   3:长按
    int touchStatus = 0;
    
    /** 
     * 获取功能菜单
     */
    void powerMode()
    {
      // 单击判断逻辑
      if (isOne && millis() - firstTouchTime > 150)
      {
        isDouble = 0;
        isOne = 0;
        touchStatus = 1;
      }
      if (touchStatus == 1)
      {
        isshow = (isshow == 0) ? 1 : 0;
      }
      else if (touchStatus == 2)
      {
        mode = (mode == 0) ? 1 : 0;
      }
      // 如果是长按,且按下到按起时间超过五秒,则重启系统
      else if (touchStatus == 3 && (touchUpTime - touchDownTime) >= 3000)
      {
        mode = 3;
      }
      // 重置按键状态
      touchStatus = 0;
    }
    
    /**
     * LED闪烁
     */
    void lightning()
    {
      // 每三百毫秒闪一次LED
      digitalWrite(LED_BUILTIN, HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN, LOW);
      delay(300);
    }
    
    /**
     * 显示模式0
     */
    void showMode0()
    {
      digitalWrite(LED_BUILTIN, isshow ? LOW : HIGH);
    }
    
    /**
     * 显示模式1
     */
    void showMode1()
    {
      if (isshow == 1)
      {
        lightning();
      }
      else
      {
        digitalWrite(LED_BUILTIN, LOW);
      }
    }
    
    void setup()
    {
      //打开串口
      Serial.begin(119200);
      // 使用板载的LED灯来显示
      pinMode(LED_BUILTIN, OUTPUT);
      // 设置按键中断(上升沿,引脚电平从低变为高时触发中断。)
      attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING);
      Serial.println("system is start");
    }
    
    void loop()
    {
      powerMode();
      if (mode == 0)
      {
        // 模式0
        showMode0();
      }
      else if (mode == 1)
      {
        // 模式1
        showMode1();
      }
      else if (mode == 3)
      {
        // 重启系统
        ESP.restart();
      }
    }
    
    /**
     * 按键按下去的中断
     * 
     */
    ICACHE_RAM_ATTR void touchDownInterrupt()
    {
      // 如果已经按下过一次,且第二次按下的时间不超过150ms则表示双击
      if (isOne && millis() - firstTouchTime <= 150)
      {
        isOne = 0;
        isDouble = 1;
        touchStatus = 2;
      }
      // 记录按下的时间
      touchDownTime = millis();
      attachInterrupt(digitalPinToInterrupt(touchPin), touchUpInterrupt, FALLING);
    }
    
    /**
     * 按键弹起来的中断
     * 
     */
    ICACHE_RAM_ATTR void touchUpInterrupt()
    {
      // 记录按键按起时间
      touchUpTime = millis();
      // 按键按下到按起时间超过700毫秒则表示处于长按状态
      if ((touchUpTime - touchDownTime) > 700)
      {
        touchStatus = 3;
      }
      else if (isDouble)
      {
        isDouble = 0;
      }
      else
      {
        isOne = 1;
        firstTouchTime = millis();
      }
      attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING);
    }
    
    

    参考文章

    Arduino 触摸按键:实现单击,双击,长按功能,稳定无抖动。
    ESP8266-12F 中断

    相关文章

      网友评论

          本文标题:NodeMCU(ESP8266)按键中断实现单击-双击-长按功能

          本文链接:https://www.haomeiwen.com/subject/pxsefrtx.html