数码之家
标题:
[分享代码]Arduino中按键处理的类,支持短按和长按两种事件回调方式
[打印本页]
作者:
jjbboox
时间:
2020-2-18 11:27
标题:
[分享代码]Arduino中按键处理的类,支持短按和长按两种事件回调方式
本帖最后由 jjbboox 于 2020-2-19 13:27 编辑
gpio_button
.h文件修改了一下,原来的版本定义多个按钮的时候事件响应会混乱
处理按键其实很简单,Arduino可以用C++,所以我们可以定义一个类来处理按键。
关键就是我们把按键定义在哪个GPIO口,以及按键按下后需要进行哪些处理。
简单的类定义不需要cpp,直接一个.h文件就可以了。
将该文件放在.ino同一个目录中就可以了。
gpio_button.h
#ifndef _GPIO_BUTTON_H_
#define _GPIO_BUTTON_H_
#include <Arduino.h>
#define DEF_ELIMINATING_JITTER_MS 20 // 默认消抖延时
#define DEF_LONG_PRESS_WAIT_MS 1000 // 默认长按延时
class GpioButton {
public:
// 构造函数,定义端口号,回调函数,初始化默认值
GpioButton(uint8_t gpio_pin, void(*btn_press_event)()=nullptr) :
GpioPin(gpio_pin),
ButtonPressEvent(btn_press_event),
LongPressWaitMS(DEF_LONG_PRESS_WAIT_MS),
ButtonLongPressEvent(nullptr),
key_down_millis(0),
action_done(false) {
pinMode(GpioPin, INPUT_PULLUP);
digitalWrite(GpioPin, HIGH);
};
// 绑定按键回调函数
void BindBtnPress(void(*btn_press_event)()) {ButtonPressEvent = btn_press_event;};
// 绑定长按事件回调函数和长按的判定时长
bool BindBtnLongPress(void(*btn_long_press_event)(), uint16_t wait_ms=DEF_LONG_PRESS_WAIT_MS) {
if(wait_ms < DEF_LONG_PRESS_WAIT_MS) return false;
ButtonLongPressEvent = btn_long_press_event;
LongPressWaitMS = wait_ms;
return true;
};
// 轮询函数
void loop(){
// 读取端口状态
uint8_t pin_val = digitalRead(GpioPin);
// 获取当前系统时间
uint32_t now_millis = millis();
// 计算按下时点到当前经过的毫秒数
uint32_t pass_millis = now_millis - key_down_millis;
// 记录按键按下时点的系统时间,清楚动作执行标志
if(pin_val == LOW && key_down_millis == 0) {
key_down_millis = now_millis;
action_done = false;
}
// 按键按下状态如果超过长按判定时长时,调用长按事件回调(如果已绑定长按事件回调)
else if(pin_val == LOW && ButtonLongPressEvent != nullptr && pass_millis > LongPressWaitMS && !action_done) {
// 防止重复调用,设置执行标志
action_done = true;
// 调用回调方法
ButtonLongPressEvent();
}
// 按键释放状态
else if(pin_val == HIGH) {
// 如果是按键释放瞬间,按下时长超过消抖间隔时长,且尚未执行过回调函数
if(!action_done && key_down_millis > 0 && pass_millis > DEF_ELIMINATING_JITTER_MS && ButtonPressEvent != nullptr) {
// 设置执行标志
action_done = true;
// 调用回调方法
ButtonPressEvent();
}
// 清空按下时点的值
key_down_millis = 0;
}
};
protected:
uint8_t GpioPin;
void (*ButtonPressEvent)();
uint16_t LongPressWaitMS;
void (*ButtonLongPressEvent)();
uint32_t key_down_millis;
bool action_done;
};
#endif
复制代码
使用方法(例)
GPIOButtonTest.ino
#include <Arduino.h>
#include <gpio_button.h>
#define MY_BUTTON_PIN 8
// 定义按钮对象,指定按钮的GPIO口
GpioButton myButton(MY_BUTTON_PIN);
void setup() {
Serial.begin(9600);
Serial.println("Start.");
// 初始化板载LED信号灯
pinMode(LED_BUILTIN, OUTPUT);
// 绑定按钮事件处理
myButton.BindBtnPress([](){
Serial.println("Led Button Press Event\r\n\tSetup Bind Event.");
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
});
// 绑定长按事件处理(长按判定为1500ms)
myButton.BindBtnLongPress([](){
Serial.println("Led Button Long Press Event\r\n\tSetup Bind Event.");
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}, 1500);
}
void loop() {
// 调用按键轮询处理
myButton.loop();
}
复制代码
同样可以先在代码中写好事件处理函数(void onButtonClick()),在setup中用myButton.BindBtnPress(onButtonClick)的形式绑定回调函数。
绑定长按事件的时候可以指定时间,上例中持续按下1.5s后执行长按事件。
不绑定任何回调事件的时候按键不会执行任何处理,也不会导致系统崩溃。
有兴趣的兄弟可以完善一下,让这个类能够实现双击功能。
已做消抖处理,使用的时候无需考虑硬件消抖,按键一只脚接地,另一只脚直接接GPIO口。
对象初始化时会自动将该端口设定为内部上拉方式。
作者:
jjbboox
时间:
2020-2-19 13:27
头文件修改了一下,见顶楼红字
作者:
kindzhon
时间:
2020-2-19 16:32
:victory::handshake:很棒,可以写类了。
作者:
jjbboox
时间:
2020-6-30 13:56
已实现单击,双击,长按3种事件同时处理
已经放到GitHub上共享
https://github.com/jjbboox/GpioKeyEvent
GpioKeyEvent.h
#ifndef _GPIO_BUTTON_H_
#define _GPIO_BUTTON_H_
#include <Arduino.h>
#define DEF_ELIMINATING_JITTER_MS 20 // default eliminating jitter ms
#define DEF_LONG_PRESS_WAIT_MS 1000 // default long press wait ms
#define DEF_DB_PRESS_MS 300
class GpioButton {
public:
GpioButton(uint8_t gpio_pin, void(*btn_press_event)()=nullptr) :
GpioPin(gpio_pin),
ButtonPressEvent(btn_press_event),
LongPressWaitMS(DEF_LONG_PRESS_WAIT_MS),
ButtonLongPressEvent(nullptr),
first_key_down_millis(0),
first_key_up_millis(0),
action_done(false),
last_gpio_state(HIGH) {
pinMode(GpioPin, INPUT_PULLUP);
digitalWrite(GpioPin, HIGH);
};
// bind click event CB function
void BindBtnPress(void(*btn_press_event)()) {
ButtonPressEvent = btn_press_event;
};
// bind long key press CB function
bool BindBtnLongPress(void(*btn_long_press_event)(), uint16_t wait_ms=DEF_LONG_PRESS_WAIT_MS) {
if(wait_ms < DEF_LONG_PRESS_WAIT_MS) return false;
ButtonLongPressEvent = btn_long_press_event;
LongPressWaitMS = wait_ms;
return true;
};
// bind double click CB function
void BindBtnDblPress(void(*btn_dbl_press_event)()) {
ButtonDblPressEvent = btn_dbl_press_event;
};
// loop function
void loop(){
uint8_t current_gpio_state = digitalRead(GpioPin);
uint32_t current_millis = millis();
// gpio status no change
if(current_gpio_state == last_gpio_state) {
if(current_gpio_state == LOW) {
if(first_key_down_millis && !first_key_up_millis && (current_millis - first_key_down_millis > LongPressWaitMS)) {
if(!action_done && ButtonLongPressEvent != nullptr) {
ButtonLongPressEvent();
action_done = true;
}
}
}
else {
if(first_key_up_millis && (current_millis - first_key_up_millis > DEF_DB_PRESS_MS)) {
if(!action_done && ButtonPressEvent != nullptr) {
// Serial.println("Debug:Press Event.");
ButtonPressEvent();
action_done = true;
}
}
}
}
// gpio status changed
else {
if(current_millis - last_jitter_millis > DEF_ELIMINATING_JITTER_MS) {
// key down
if(current_gpio_state == LOW) {
// is first keydown in cycle
if(0 == first_key_down_millis) {
first_key_down_millis = current_millis;
first_key_up_millis = 0;
action_done = false;
}
// is not first key down in cycle
else {
// has define double click CB function
if(nullptr != ButtonDblPressEvent){
// key down mill - last key up mill > elimination jitter interval
if( 0 != first_key_up_millis // is release key in event cycle
&& (current_millis - first_key_up_millis) > DEF_ELIMINATING_JITTER_MS) { // skip eliminating jitter
// is double click?
if( false == action_done // did in event cycle?
&& current_millis - first_key_up_millis < DEF_DB_PRESS_MS) { // and 2nd click is in interval
// call double click event function
// Serial.println("Debug:Double Press Event.");
ButtonDblPressEvent();
action_done = true;
}
}
}
}
}
// key up
else {
if(!action_done && first_key_down_millis && first_key_up_millis == 0) {
first_key_up_millis = current_millis;
}
}
// Keep gpio status
last_gpio_state = current_gpio_state;
last_jitter_millis = current_millis;
}
}
if(action_done && current_gpio_state == HIGH) {
// Serial.println("Event Reset.");
first_key_down_millis = 0;
first_key_up_millis = 0;
action_done = false;
}
};
protected:
uint8_t GpioPin; // gpio pin of key
void (*ButtonPressEvent)(); // Click Event CB function
uint16_t LongPressWaitMS; // Long press ms
void (*ButtonLongPressEvent)(); // Long press Event CB function
void (*ButtonDblPressEvent)(); // Double click Event CB function
uint32_t first_key_down_millis;
uint32_t first_key_up_millis;
bool action_done;
uint8_t last_gpio_state;
uint32_t last_jitter_millis;
};
#endif
复制代码
作者:
茶壹杯
时间:
2020-7-3 13:02
感谢分享
ARDUINO 早就有了模拟鼠标、键盘、摇杆的扩展产品
可能和LZ的思路不谋而合?
欢迎光临 数码之家 (https://www.mydigit.cn/)
Powered by Discuz! X3.4