2021-12-18 更新
B站网友 柳桥风起 分享了一个开源库使用效果更佳,OneButton 这个库功能更齐全,可直接使用,我个人分享的还存在bug,看看就好了,这里也贴出一段个人写的demo代码,当然更推荐的是到github上看原作者代码说明
#include
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的电平信号即可,但是如果我们要实现案件双击,长按此时单纯靠读取电平信号则无法解决此问题。需要使用外部中断来处理按键的状态值。 大概思路就是根据按键按下的时间,和按键回弹的时间,来判断按键是否是第一次按压和按压两次或者长按(这里我也不想不出好的文字来描述此过程,大家看代码就懂了的)!
实现功能
- 单击切换LED显示状态
- 双击切换LED显示模式 (模式1:亮/灭 、模式2:闪烁/常亮)
- 长按超过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
// 按键设置在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 中断