По итогам эксплуатации слегка доработал библиотеку.
+ в методе isButtonClosed() добавлен параметр toChecked; при значении true автоматически вызывает процедуру getButtonState() перед определением положения кнопки; при значении false (принято по умолчанию) определение положения кнопки выполняется по результату последнего опроса;
+ добавлен метод resetButtonState(), сбрасывающий счетчик кликов кнопки (_clckcount); метод позволяет исключить возникновение событий BTN_ONECLICK и BTN_DBLCLICK, если они по каким-то причинам в данный момент не желательны;
+ все булевы и часть байтовых полей упакованы в один байт, что позволило уменьшить размер занимаемой кнопкой памяти;
+ добавлен пример обработки одновременного нажатия на две кнопки
Файл shButton.h
#pragma once
#include <Arduino.h>
/*
shButton - библиотека для отработки нажатия кнопки. Возможности:
- Работа с кнопками с нормально разомкнутыми и нормально замкнутыми контактами;
- Работа с подключением PULL_UP и PULL_DOWN;
- Опрос кнопки как с программным антидребезгом контактов, так и без него; возможность настройки интервала антидребезга;
- Отработка нажатия, отпускания кнопки, двойного клика; возможность настройки интервала двойного клика;
- Возможность использования виртуального клика; в этом режиме событие клика (BTN_ONECLICK) генерируется по истечении интервала двойного клика, если не наступило событие двойного клика и в это же время кнопка не удерживается нажатой; таким образом появляется возможность организовать раздельную реакцию на однократный клик, двойной клик и удержание кнопки без написания дополнительного кода в скетче;
- Отработка удержания кнопки; возможность настройки интервала удержания;
- Возможность настройки генерируемого при удержании кнопки свыше интервала удержания события (BTN_LONGCLICK) - непрерываная выдача события (т.е. фактически - состояния кнопки), однократная подача события или циклическая подача события через равные интервалы времени, пока кнопка нажата; возможность настройки этого интервала;
Версия 1.1
+ в методе isButtonClosed() добавлен параметр toChecked; при значении true автоматически вызывает процедуру getButtonState() перед определением положения кнопки; при значении false (принято по умолчанию) определение положения кнопки выполняется по результату последнего опроса;
+ добавлен метод resetButtonState(), сбрасывающий счетчик кликов кнопки (_clckcount); метод позволяет исключить возникновение событий BTN_ONECLICK и BTN_DBLCLICK, если они по каким-то причинам в данный момент не желательны;
+ все булевы и часть байтовых полей упакованы в один байт, что позволило уменьшить размер занимаемой кнопкой памяти на 4 байта;
*/
// флаги свойств и состояния кнопки - биты поля _flags
#define FLAG_BIT 0 // сохраненное состояние кнопки - нажата/не нажата
#define INPUTTYPE_BIT 1 // тип подключения - PULL_UP/ PULL_DOWN
#define BTNTYPE_BIT 2 // тип кнопки - нормально разомкнутая или нормально замкнутая
#define DEBOUNCE_BIT 3 // флаг включения подавления дребезга
#define VIRTUALCLICK_BIT 4 // режим виртуального клика
// значения по умолчанию
#define DEBOUNCETIMEOUT 50
#define PRESSEDTIMEOUT 500
#define DBLCLICKTIMEOUT 300
#define LONGCLICKTIMEOUT 200
// типы кнопки
#define BTN_NO 0 // кнопка с нормально разомкнутыми контактами
#define BTN_NC 1 // кнопка с нормально замкнутыми контактами
// типы подключения кнопки
#define PULL_UP 0 // кнопка подтянута к VCC
#define PULL_DOWN 1 // кнопка подтянута к GND
// состояние кнопки
#define BTN_RELEASED 0 // кнопка отпущена
#define BTN_PRESSED 1 // кнопка нажата, но время удержания не вышло
// события кнопки
#define BTN_UP 2 // кнопка только что отпущена
#define BTN_DOWN 3 // кнопка только что нажата
#define BTN_DBLCLICK 4 // двойной клик
// виртуальные события кнопки
#define BTN_ONECLICK 5 // одиночный клик, следует через некоторый интервал после нажатия кнопки, если за это время не последовал двойной клик или длительное удержание кнопки нажатой; по умолчанию событие отключено
#define BTN_LONGCLICK 6 // событие, соответствующее удержанию кнопки дольше времени удержания; может быть однократным, следовать через определенные интервалы или быть непрерывным (по умолчанию)
// режимы отработки удержания кнопки
#define LCM_CONTINUED 0 // непрерывное событие, генерируется постоянно, пока кнопка удерживается нажатой, если интервал удержания превышен; значение по умолчанию
#define LCM_ONLYONCE 1 // однократное событие, генерируется только один раз по истечении интервала удержания кнопки
#define LCM_CLICKSERIES 2 // по истечении интервала удержания кнопки событие генерируется постоянно через равные интервалы времени
class shButton
{
private:
byte _PIN = 0; // пин, на который посажена кнопка
word _debounce = DEBOUNCETIMEOUT; // интервал подавления дребезга контактов, мс
word _timeout = PRESSEDTIMEOUT; // интервал удержания кнопки нажатой, мс
word _dblclck = DBLCLICKTIMEOUT; // интервал двойного клика, мс
byte _clckcount = 0; // счетчик кликов
byte _longclickcount = 0; // счетчик длинных кликов
byte _longclickmode = LCM_CONTINUED; // режим удержания кнопки;
word _longclicktimeout = LONGCLICKTIMEOUT; // интервал следования события BTN_LONGCLICK, если установлен режим LCM_CLICKSERIES, мс
byte _btnstate = BTN_RELEASED; // текущее состояние кнопки
byte _flags = 0; // набор флагов свойств и состояния кнопки
/*
* 0 бит - сохраненное состояние кнопки - нажата(1)/не нажата(0)
* 1 бит - тип подключения - PULL_UP(0)/PULL_DOWN(1)
* 2 бит - тип кнопки - нормально разомкнутая (BTN_NO) или нормально замкнутая (BTN_NC)
* 3 бит - флаг включения подавления дребезга - пока флаг поднят (1), изменения состояния не принимаются
* 4 бит - режим виртуального клика, 0 - выключен, 1 - включен
*/
unsigned long btn_timer = 0; // таймер удержания кнопки нажатой
unsigned long deb_timer = 0; // таймер подавления дребезга контактов
unsigned long dbl_timer = 0; // таймер двойного клика
unsigned long lclck_timer = 0; // таймер серийного BTN_LONGCLICK
// получение мгновенного состояния кнопки - нажата/не нажата с учетом типа подключения и без учета дребезга контактов
bool getButtonFlag();
// установка кнопке состояния "только что нажата" или "только что отпущена"
void setBtnUpDown(bool flag, unsigned long thisMls);
// получение состояния бита
bool getFlag(byte _bit);
// установка состояния бита
void setFlag(byte _bit, bool x);
public:
// Варианты инициализации:
// shButton btn(пин); - с привязкой к пину и без указания типа подключения (по умолч. PULL_UP) и типа контактов (по умолчанию BTN_NO - нормально разомкнутые контакты)
// shButton btn(пин, тип подключ.); - с привязкой к пину и указанием типа подключения (PULL_UP / PULL_DOWN)
// shButton btn(пин, тип подключ., тип кнопки); - с привязкой к пину и указанием типа подключения (PULL_UP / PULL_DOWN) и типа кнопки (BTN_NO/BTN_NC)
shButton(byte pin, byte inputtype = PULL_UP, byte btntype = BTN_NO);
// получение состояния кнопки - отпущена/нажата/удерживается
byte getButtonState();
// возвращает true, если контакты кнопки по результату последней проверки замкнуты; если toChecked == true будет выполнен опрос кнопки
bool isButtonClosed(bool toChecked = false);
// принудительный сброс состояния кнопки; может понадобиться, если по каким-то причинам нужно исключить возникновение событий BTN_ONECLICK и BTN_DBLCLICK
void resetButtonState();
// установка типа подключения кнопки (PULL_UP - подтянута к VCC, PULL_DOWN - к GND)
void setInputType(byte inputtype);
// установка типа кнопки (BTN_NO - нормально разомкнутая, BTN_NC - нормально замкнутая)
void setButtonType(byte btntype);
// установка времени антидребезга (по умолчанию 50 мс); для отключения антидребезга нужно задать 0 мс
void setDebounce(word debounce);
// установка таймаута удержания кнопки (по умолчанию 500 мс)
void setTimeout(word new_timeout);
// установка интервала двойного клика
void setDblClickTimeout(word new_timeout);
// включение режима "Виртуальный клик"
void setVirtualClickOn(bool virtualclick);
// установка режима обработки удержания кнопки нажатой
void setLongClickMode(byte longclickmode);
// установка интервала выдачи события BTN_LONGCLICK в режиме LCM_CLICKSERIES
void setLongClickTimeout(word longclicktimeout);
};
Файл shButton.cpp
#include "shButton.h"
#include <Arduino.h>
shButton::shButton(byte pin, byte inputtype, byte btntype)
{
_PIN = pin;
setInputType(inputtype);
setButtonType(btntype);
}
byte shButton::getButtonState()
{
bool flag = getButtonFlag();
unsigned long thisMls = millis();
// состояние кнопки не изменилось с прошлого опроса
if (flag == getFlag(FLAG_BIT))
{ // и не поднят флаг подавления дребезга
if (!getFlag(DEBOUNCE_BIT))
{
if (!flag)
{ // кнопка находится в отжатом состоянии
_btnstate = BTN_RELEASED;
if (millis() - dbl_timer > _dblclck)
{ // если период двойного клика закончился, проверить на виртуальный клик и обнулить счетчик кликов
if (getFlag(VIRTUALCLICK_BIT) && _clckcount == 1)
{
_btnstate = BTN_ONECLICK;
}
_clckcount = 0;
_longclickcount = 0;
}
}
else if (millis() - btn_timer < _timeout)
{ // кнопка находится в нажатом состоянии, но время удержания еще не вышло
_btnstate = BTN_PRESSED;
}
else
{ // если кнопка удерживается нажатой дольше времени удержания, то дальше возможны варианты
switch (_longclickmode)
{
case LCM_ONLYONCE:
if (_longclickcount == 0)
{
_longclickcount++;
_btnstate = BTN_LONGCLICK;
}
else
{
_btnstate = BTN_PRESSED;
}
break;
case LCM_CLICKSERIES:
if (millis() - lclck_timer >= _longclicktimeout)
{
lclck_timer = thisMls;
_longclickcount++;
_btnstate = BTN_LONGCLICK;
}
else
{
_btnstate = BTN_PRESSED;
}
break;
default:
_btnstate = BTN_LONGCLICK;
break;
}
_clckcount = 0;
}
}
}
// состояние кнопки изменилось с прошлого опроса
else
{ // если задано подавление дребезга контактов
if (_debounce > 0)
{ // если флаг подавления еще не поднят - поднять и больше ничего не делать
if (!getFlag(DEBOUNCE_BIT))
{
deb_timer = thisMls;
setFlag(DEBOUNCE_BIT, true);
} // иначе, если поднят, и интервал вышел - установить состояние кнопки
else if (millis() - deb_timer >= _debounce)
{
setBtnUpDown(flag, thisMls);
}
}
else // если подавление вообще не задано, то сразу установить состояние кнопки
{
setBtnUpDown(flag, thisMls);
}
}
return _btnstate;
}
bool shButton::isButtonClosed(bool toChecked)
{
if (toChecked)
{
getButtonState();
}
return _btnstate != BTN_RELEASED && _btnstate != BTN_UP;
}
void shButton::resetButtonState()
{
_clckcount = 0;
}
void shButton::setInputType(byte inputtype)
{
setFlag(INPUTTYPE_BIT, inputtype);
switch (inputtype)
{
case PULL_UP:
pinMode(_PIN, INPUT_PULLUP);
break;
default:
pinMode(_PIN, INPUT);
break;
}
}
void shButton::setButtonType(byte btntype)
{
setFlag(BTNTYPE_BIT, btntype);
}
void shButton::setDebounce(word debounce)
{
_debounce = debounce;
}
void shButton::setTimeout(word new_timeout)
{
_timeout = new_timeout;
}
void shButton::setDblClickTimeout(word new_timeout)
{
_dblclck = new_timeout;
}
void shButton::setVirtualClickOn(bool virtualclick)
{
setFlag(VIRTUALCLICK_BIT, virtualclick);
}
void shButton::setLongClickMode(byte longclickmode)
{
_longclickmode = longclickmode;
if (_longclickmode == LCM_CLICKSERIES && _longclicktimeout == 0)
{
_longclicktimeout = LONGCLICKTIMEOUT;
}
}
void shButton::setLongClickTimeout(word longclicktimeout)
{
_longclicktimeout = longclicktimeout;
// если установлено нулевое значение, то перевести режим на однократное событие
if (_longclicktimeout == 0)
{
_longclickmode = LCM_ONLYONCE;
}
}
bool shButton::getButtonFlag()
{
bool val = digitalRead(_PIN);
if (getFlag(INPUTTYPE_BIT) == PULL_UP)
{
val = !val;
}
if (getFlag(BTNTYPE_BIT) == BTN_NC)
{
val = !val;
}
return val;
}
void shButton::setBtnUpDown(bool flag, unsigned long thisMls)
{
setFlag(DEBOUNCE_BIT, false);
setFlag(FLAG_BIT, flag);
if (flag)
{ // если кнопка только что нажата, то запустить таймер удержания
btn_timer = thisMls;
if (_clckcount == 0)
{ // если это первый клик, запустить таймер двойного клика и увеличить счетчик кликов
_btnstate = BTN_DOWN;
_clckcount++;
dbl_timer = thisMls;
}
else if (millis() - dbl_timer <= _dblclck)
{
_btnstate = BTN_DBLCLICK;
_clckcount = 0;
}
}
else
{
_btnstate = BTN_UP;
}
}
bool shButton::getFlag(byte _bit)
{
bool result;
(_bit < 8) ? result = (((_flags) >> (_bit)) & 0x01) : result = false;
return (result);
}
void shButton::setFlag(byte _bit, bool x)
{
if (_bit < 8)
{
(x) ? (_flags) |= (1UL << (_bit)) : (_flags) &= ~(1UL << (_bit));
}
}
У меня с тремя работает. По отдельности. Но здесь не раз поднимался вопрос про одновременное нажатие, вот я пример и добавил. Да и сам у себя, если честно, в одном приборе использую ))
У меня с тремя работает. По отдельности. Но здесь не раз поднимался вопрос про одновременное нажатие, вот я пример и добавил. Да и сам у себя, если честно, в одном приборе использую ))
ок.
откуда, вообще, у кого-то должны возникать сомнения, что оно не будет работать со 100 кнопками, например...
одновременно/неодновременно, отжатие/нажатие - какая вжопу разница.
То у него старая рана в нижней части спины ноет. На погоду наверное ;)
Он забульбенил развесистый класс для кнопки. Ну и когда речь зашла о поддержке сотни кнопок на устройство то фигонул этот класс в массив. На том ОЗУ контроллера и кончилось. До обработки одновременного нажатия дело кажись и не дошло)))) А я ему намекнул что все это делается сильно проще, в полсотни байт ОЗУ влазит, с даблкликами, комбинациями одновременно нажатых кнопок и прочим. Но будет ограничение по кол-ву одновременно нажатых кнопок, например десяток. Так он долго у меня код клянчил. Три года уже прошло, гнали его с форума его пару раз, а ему все чешется )))
Клапа, там все просто! Очередь клавиатурных событий ведется. Типа циклический массив записей время-код события-номер кнопки. А по ней уже и даблклики, и комбинации определяются... А ограничение на кол-во одновременных нажатий - оно из ограничения длины очереди следует. Легко понять что чем больше очередь больше кнопок одновременно приймется. Считай, Клапа, этот абзац гуманитаркой ;)
По мотивам этого поста немного доработал библиотеку. В основном в плане работы с виртуальными кнопками, т.е. кнопками, не привязанными к конкретному пину. Например, аналоговая клавиатура (см. примеры) или кнопки, подключенные через сдвиговый регистр.
Для виртуальной кнопки используется конструктор без аргументов
shButton btn;
И в метод getButtonState нужно передавать состояние контактов кнопки (нажата/не нажата), определенное внешним кодом, например
word res = analogRead(A0);
btn.getButtonState(res == 512);
В остальном работа с виртуальной кнопкой не отличается от работы с обычной
Вот с этим хокку 1024 раза согласен. Ни разу не встречал проблем с кнопками. Все эти дребезги-хуебезги для холериков. А остальное решается на аппаратном уровне.
какой из меня тетильщик?
Ничо-ничо! Главное, чтобы костюмчик сидел!
По итогам эксплуатации слегка доработал библиотеку.
Файл shButton.h
Файл shButton.cpp
Файл keywords.txt
Скетч - пример обработки одновременного нажатия на две кнопки
Скачать одним файлом
т.е. до того оно работало с одной кнопкой, а теперь может работать аж с двумя?
ок. но Логику не понравится - у него на ногах 10-ть пальцев...
о_О
У меня с тремя работает. По отдельности. Но здесь не раз поднимался вопрос про одновременное нажатие, вот я пример и добавил. Да и сам у себя, если честно, в одном приборе использую ))
У меня с тремя работает. По отдельности. Но здесь не раз поднимался вопрос про одновременное нажатие, вот я пример и добавил. Да и сам у себя, если честно, в одном приборе использую ))
ок.
откуда, вообще, у кого-то должны возникать сомнения, что оно не будет работать со 100 кнопками, например...
одновременно/неодновременно, отжатие/нажатие - какая вжопу разница.
Аминь, бро ))
Аминь, бро ))
)))
То у него старая рана в нижней части спины ноет. На погоду наверное ;)
Он забульбенил развесистый класс для кнопки. Ну и когда речь зашла о поддержке сотни кнопок на устройство то фигонул этот класс в массив. На том ОЗУ контроллера и кончилось. До обработки одновременного нажатия дело кажись и не дошло)))) А я ему намекнул что все это делается сильно проще, в полсотни байт ОЗУ влазит, с даблкликами, комбинациями одновременно нажатых кнопок и прочим. Но будет ограничение по кол-ву одновременно нажатых кнопок, например десяток. Так он долго у меня код клянчил. Три года уже прошло, гнали его с форума его пару раз, а ему все чешется )))
Клапа, там все просто! Очередь клавиатурных событий ведется. Типа циклический массив записей время-код события-номер кнопки. А по ней уже и даблклики, и комбинации определяются... А ограничение на кол-во одновременных нажатий - оно из ограничения длины очереди следует. Легко понять что чем больше очередь больше кнопок одновременно приймется. Считай, Клапа, этот абзац гуманитаркой ;)
Очередь клавиатурных событий ведется.
снова про жопу и клавиатуру...
КНОПКИ, упоротый норкоман...
...не клавиатура - кнооопки...
нужно тебе экономить память - разворачивай массив кнопок, опрашивай, очищай память если нужно. класс это позволяет.
По мотивам этого поста немного доработал библиотеку. В основном в плане работы с виртуальными кнопками, т.е. кнопками, не привязанными к конкретному пину. Например, аналоговая клавиатура (см. примеры) или кнопки, подключенные через сдвиговый регистр.
Для виртуальной кнопки используется конструктор без аргументов
И в метод getButtonState нужно передавать состояние контактов кнопки (нажата/не нажата), определенное внешним кодом, например
В остальном работа с виртуальной кнопкой не отличается от работы с обычной
Скачать можно здесь
снова про жопу и клавиатуру...
КНОПКИ, упоротый норкоман...
...не клавиатура - кнооопки...
Вот с этим хокку 1024 раза согласен. Ни разу не встречал проблем с кнопками. Все эти дребезги-хуебезги для холериков. А остальное решается на аппаратном уровне.
Еще немного допилил библиотеку:
Скачать можно здесь
Мне нравятся библиотеки на все случаи жизни для ОДНОЙ кнопки. А вот если нужно фиксировать одновременные нажатия?
А не поделитесь библиотекой с одновременным нажатием кнопок?
Мне нравятся библиотеки на все случаи жизни для ОДНОЙ кнопки. А вот если нужно фиксировать одновременные нажатия?
А не поделитесь библиотекой с одновременным нажатием кнопок?
Спасибо большое.