2021-12-18 更新
B站網(wǎng)友 柳橋風(fēng)起 分享了一個(gè)開源庫(kù)使用效果更佳阅悍,OneButton 這個(gè)庫(kù)功能更齊全,可直接使用艇潭,我個(gè)人分享的還存在bug回还,看看就好了裆泳,這里也貼出一段個(gè)人寫的demo代碼,當(dāng)然更推薦的是到github上看原作者代碼說明
#include <OneButton.h>
OneButton btn = OneButton(D3, false, false);
// 記錄按鍵按下時(shí)間
uint32_t clicktime = 0;
/**
* 處理單擊
*/
static void singleClick() {
Serial.println("按鍵單擊");
}
/**
* 處理雙擊
*/
static void doubleClick() {
Serial.println("按鍵雙擊");
}
/**
* 按鍵長(zhǎng)按開始做的事情
*/
static void longClickStart() {
Serial.println("按鍵長(zhǎng)按開始");
clicktime = millis();
}
/**
* 處理按鍵長(zhǎng)按
*/
static void longClick() {
Serial.println("按鍵長(zhǎng)按結(jié)束");
Serial.println("按鍵按下時(shí)間:");
// 這里為啥加1000柠硕? 因?yàn)榘存I長(zhǎng)按開始時(shí)間時(shí)按下一秒后開始計(jì)算的工禾,所以就要加上我們?cè)疽呀?jīng)按下的一秒種
Serial.print(millis()-clicktime+1000);
// 重置按下時(shí)間
clicktime = 0;
}
void setup() {
Serial.begin(115200);
// 添加單擊事件函數(shù)
btn.attachClick(singleClick);
// 添加雙擊事件函數(shù)
btn.attachDoubleClick(doubleClick);
// 添加長(zhǎng)按事件函數(shù)
btn.attachLongPressStop(longClick);
// 添加按下事件函數(shù)
btn.attachLongPressStart(longClickStart);
}
void loop() {
btn.tick();
}
背景
很多時(shí)候我們的設(shè)備就只有一個(gè)按鍵运提,但是我們需要的功能卻比較多,所以就會(huì)圍繞一個(gè)按鍵實(shí)現(xiàn)多種交互功能闻葵,單擊民泵,雙擊,長(zhǎng)按可能就是最常見的幾種交互了槽畔,所以我就想著用nodeMcu(esp8266)也搞一個(gè)出來栈妆,中途遇到很多的問題,為此便寫下這篇筆記記錄下來厢钧,分享給大家鳞尔!
實(shí)現(xiàn)方式
如果只是要實(shí)現(xiàn)按鍵單擊功能是比較簡(jiǎn)單的,只需要讀取對(duì)應(yīng)的GPIO的電平信號(hào)即可早直,但是如果我們要實(shí)現(xiàn)案件雙擊寥假,長(zhǎng)按此時(shí)單純靠讀取電平信號(hào)則無法解決此問題。需要使用外部中斷來處理按鍵的狀態(tài)值霞扬。 大概思路就是根據(jù)按鍵按下的時(shí)間糕韧,和按鍵回彈的時(shí)間,來判斷按鍵是否是第一次按壓和按壓兩次或者長(zhǎng)按(這里我也不想不出好的文字來描述此過程喻圃,大家看代碼就懂了的)兔沃!
實(shí)現(xiàn)功能
- 單擊切換LED顯示狀態(tài)
- 雙擊切換LED顯示模式 (模式1:亮/滅 、模式2:閃爍/常亮)
- 長(zhǎng)按超過3秒重啟系統(tǒng)
電路圖及原理
電路圖和原理部分我直接搬運(yùn)此文章的 ESP8266-12F 中斷 级及,有興趣歡迎到原作者處查閱乒疏,我只是一個(gè)搬運(yùn)工記錄一下。
電路圖
這里的電阻10K左右即可饮焦,大到20多K也沒問題怕吴,切勿放一個(gè)小電阻,形成短路主板無法正常工作
外部中斷
基于ESP8266的NodeMcu的數(shù)字IO的中斷功能是通過attachInterrupt县踢,detachInterrupt函數(shù)所支持的转绷。
除了D0/GPIO16,中斷可以綁定到任意GPIO的引腳上【D0-D10】硼啤。
所支持的標(biāo)準(zhǔn)中斷類型有:
- CHANGE(改變沿议经,電平從低到高或者從高到低)
- RISING(上升沿,電平從低到高)
- FALLING(下降沿谴返,電平從高到低)
attachInterrupt(pin, function, mode); 設(shè)置觸發(fā)中斷的引腳
- pin:要設(shè)置中斷編號(hào)煞肾,注意,這里不是引腳編號(hào)
- function:中斷發(fā)生時(shí)運(yùn)行的函數(shù), 這個(gè)函數(shù)不帶任何參數(shù)嗓袱,不返回任何內(nèi)容
- Interrupt type/mode:它定義中斷被觸發(fā)的條件方式
- CHANGE:改變沿籍救,引腳電平從低變?yōu)楦呋蛘邚母咦優(yōu)榈蜁r(shí)觸發(fā)中斷。
- RISING:上升沿渠抹,引腳電平從低變?yōu)楦邥r(shí)觸發(fā)中斷蝙昙。
- FALLING:下降沿闪萄,引腳電平從高變?yōu)榈蜁r(shí)觸發(fā)中斷
- 返回值: 無
detachInterrupt(pin); 取消指定引腳的中斷
- pin:中斷號(hào)
- 返回值: 無
digitalPinToInterrupt(pin)奇颠;獲取指定引腳的中斷號(hào)
- pin:要獲取中斷號(hào)的GPIO引腳
- 返回值: 中斷號(hào)
引腳對(duì)應(yīng)的中斷號(hào):
- D1 -> 5
- D2 -> 4
- D4 -> 2
- D5 -> 14
- D6 -> 12
- D7 -> 13
- D8 -> 15
代碼
代碼部分參考自CSDN文章Arduino 觸摸按鍵:實(shí)現(xiàn)單擊败去,雙擊,長(zhǎng)按功能烈拒,穩(wěn)定無抖動(dòng)为迈。
感覺作者的代碼分享,歡迎查閱原作者代碼分享缺菌,我只是搬運(yùn)了一下代碼
#include <ESP8266WiFi.h>
// 按鍵設(shè)置在D8(gpio15)引腳
int touchPin = D2;
// 模式:0:LED亮和滅 1. LED常亮或閃爍 2. 重啟系統(tǒng)
int mode = 0;
// 模式0:亮/滅 模式1: 常亮/閃爍
bool isshow = false;
// 按鍵按下去的時(shí)間 按鍵按起來的時(shí)間 第一次按按鍵的時(shí)間
long touchDownTime = 0, touchUpTime = 0, firstTouchTime = 0;
// 是否單擊 是否雙擊
bool isOne = 0, isDouble = 0;
// 按鍵狀態(tài) 0:無任何操作葫辐,1:?jiǎn)螜C(jī) 2: 雙擊 3:長(zhǎng)按
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;
}
// 如果是長(zhǎng)按,且按下到按起時(shí)間超過五秒伴郁,則重啟系統(tǒng)
else if (touchStatus == 3 && (touchUpTime - touchDownTime) >= 3000)
{
mode = 3;
}
// 重置按鍵狀態(tài)
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);
// 設(shè)置按鍵中斷(上升沿耿战,引腳電平從低變?yōu)楦邥r(shí)觸發(fā)中斷。)
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)
{
// 重啟系統(tǒng)
ESP.restart();
}
}
/**
* 按鍵按下去的中斷
*
*/
ICACHE_RAM_ATTR void touchDownInterrupt()
{
// 如果已經(jīng)按下過一次焊傅,且第二次按下的時(shí)間不超過150ms則表示雙擊
if (isOne && millis() - firstTouchTime <= 150)
{
isOne = 0;
isDouble = 1;
touchStatus = 2;
}
// 記錄按下的時(shí)間
touchDownTime = millis();
attachInterrupt(digitalPinToInterrupt(touchPin), touchUpInterrupt, FALLING);
}
/**
* 按鍵彈起來的中斷
*
*/
ICACHE_RAM_ATTR void touchUpInterrupt()
{
// 記錄按鍵按起時(shí)間
touchUpTime = millis();
// 按鍵按下到按起時(shí)間超過700毫秒則表示處于長(zhǎng)按狀態(tài)
if ((touchUpTime - touchDownTime) > 700)
{
touchStatus = 3;
}
else if (isDouble)
{
isDouble = 0;
}
else
{
isOne = 1;
firstTouchTime = millis();
}
attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING);
}
參考文章
Arduino 觸摸按鍵:實(shí)現(xiàn)單擊剂陡,雙擊,長(zhǎng)按功能狐胎,穩(wěn)定無抖動(dòng)鸭栖。
ESP8266-12F 中斷