Подключение кнопки к Arduino. GyverButton v3.8

ОБНОВЛЕНИЯ


  • v3.6: добавлен отдельный класс для работы с аналоговыми клавиатурами, см пример analogKeyboardG
  • v3.7: исправления от Dryundel:
    • Любой таймаут удержания
    • Single, Double и Triple теперь не мешают hasClicks и getClicks и работают совместно
    • isStep() тоже теперь ничего не мешает и он работает более корректно
  • v3.8: исправления от Dryundel

ТЕОРИЯ


На сайте есть отдельный подробный урок по работе с кнопками.

Кнопка – простейший орган управления микроконтроллером. Подключить кнопку к Arduino очень просто, но нужно помнить, что пин должен иметь два стабильных состояния – высокое и низкое, GND или VCC. Для этого пин кнопки подтягивают резистором ~10 кОм противоположно подключению кнопки, т.е. если кнопка подключена второй ногой к GND, пин подтягивают к VCC, и наоборот.

 

Микроконтроллер имеет “встроенную” подтяжку ног к VCC, что даёт возможность подключать кнопку только к GND и пину, но режим работы пина нужно выбрать INPUT_PULLUP. Я, например, всегда подключаю отладочную кнопку на D3 вот таким образом:

 

Также можно подключить несколько кнопок к аналоговому пину, получится так называемая аналоговая клавиатура. Значение функции analogRead() будет зависеть от нажатой кнопки.

БИБЛИОТЕКА


GyverButton v3.8

Для удобной и многофункциональной работы с кнопкой я написал библиотеку GyverButton. Что она умеет:

  • Работа с нормально замкнутыми и нормально разомкнутыми кнопками
  • Работа с подключением PULL_UP и PULL_DOWN Опрос кнопки с программным антидребезгом контактов (настраиваемое время)
  • Отработка нажатия, удерживания, отпускания, клика по кнопке (+ настройка таймаутов)
  • Отработка одиночного, двойного и тройного нажатия (вынесено отдельно)
  • Отработка любого количества нажатий кнопки (функция возвращает количество нажатий)
  • Функция изменения значения переменной с заданным шагом и заданным интервалом по времени
  • Возможность работы с “виртуальными” кнопками (все возможности библиотеки используются для матричных и резистивных клавиатур)

Поддерживаемые платформы: все Arduino (используются стандартные Wiring-функции)

Версия 3.5: значительно увеличена производительность для AVR Ardiuno плат

УСТАНОВКА


  • Библиотеку можно найти и установить через менеджер библиотек по названию GyverButton в:
    • Arduino IDE (Инструменты/Управлять библиотеками)
    • Arduino IDE v2 (вкладка «Library Manager»)
    • PlatformIO (PIO Home, вкладка «Libraries»)
  • Про ручную установку читай здесь

ДОКУМЕНТАЦИЯ


// Варианты инициализации:
// GButton btn;                     // без привязки к пину (виртуальная кнопка) и без указания типа (по умолч. HIGH_PULL и NORM_OPEN)
// GButton btn(пин);                // с привязкой к пину и без указания типа (по умолч. HIGH_PULL и NORM_OPEN)
// GButton btn(пин, тип подключ.);  // с привязкой к пину и указанием типа подключения (HIGH_PULL / LOW_PULL) и без указания типа кнопки (по умолч. NORM_OPEN)
// GButton btn(пин, тип подключ., тип кнопки);         // с привязкой к пину и указанием типа подключения (HIGH_PULL / LOW_PULL) и типа кнопки (NORM_OPEN / NORM_CLOSE)
// GButton btn(BTN_NO_PIN, тип подключ., тип кнопки);  // без привязки к пину и указанием типа подключения (HIGH_PULL / LOW_PULL) и типа кнопки (NORM_OPEN / NORM_CLOSE)
void setDebounce(uint16_t debounce);        // установка времени антидребезга (по умолчанию 80 мс)
void setTimeout(uint16_t timeout);          // установка таймаута удержания (по умолчанию 300 мс)
void setClickTimeout(uint16_t timeout);	    // установка таймаута между кликами (по умолчанию 500 мс)	
void setStepTimeout(uint16_t step_timeout); // установка таймаута между инкрементами (по умолчанию 400 мс)	
void setType(uint8_t type);     // установка типа кнопки (HIGH_PULL - подтянута к питанию, LOW_PULL - к gnd)	
void setDirection(uint8_t dir); // установка направления (разомкнута/замкнута по умолчанию - NORM_OPEN, NORM_CLOSE)	
	
void setTickMode(uint8_t tickMode); // (MANUAL / AUTO) ручной или автоматический опрос кнопки функцией tick()	
// MANUAL - нужно вызывать функцию tick() вручную														
// AUTO - tick() входит во все остальные функции и опрашивается сама
	
void tick();               // опрос кнопки	
void tick(boolean state);  // опрос внешнего значения (0 нажато, 1 не нажато) (для матричных, резистивных клавиатур и джойстиков)
	
boolean isPress();    // возвращает true при нажатии на кнопку. Сбрасывается после вызова
boolean isRelease();  // возвращает true при отпускании кнопки. Сбрасывается после вызова
boolean isClick();    // возвращает true при клике. Сбрасывается после вызова
boolean isHolded();   // возвращает true при удержании дольше timeout. Сбрасывается после вызова
boolean isHold();     // возвращает true при нажатой кнопке, не сбрасывается
boolean state();      // возвращает состояние кнопки

boolean isSingle();   // возвращает true при одиночном клике. Сбрасывается после вызова
boolean isDouble();   // возвращает true при двойном клике. Сбрасывается после вызова
boolean isTriple();   // возвращает true при тройном клике. Сбрасывается после вызова
	
boolean hasClicks();  // проверка на наличие кликов. Сбрасывается после вызова
uint8_t getClicks();  // вернуть количество кликов
uint8_t getHoldClicks();// вернуть количество кликов, предшествующее удерживанию
	
boolean isStep();     // возвращает true по таймеру setStepTimeout, смотри пример

void resetStates();   // сбрасывает все is-флаги и счётчики
В версии 3.6 библиотеки добавлен новый файл: AnalogKey.h, позволяющий удобно работать с аналоговыми клавиатурами. Пример работы - пример analogKeyboardG

Инициализация


Объект создаётся следующим образом: AnalogKey<аналоговый пин, количество кнопок> объект;

Настройка


При помощи метода .attach(номер, сигнал) задаются "кнопки" клавиатуры: кнопке под номером номер ставится в соответствие сигнал. Сигнал измеряется предварительно например выводом значений analogRead() в порт. При помощи .setWindow(размер) устанавливается точность обработки сигнала кнопок, по умолчанию равен 20. Что это значит? Ожидаемое нажатие кнопки будет обработано, если сигнал с клавиатуры попадает в промежуток (сигнал-размер, сигнал+размер).

Опрос


Метод .status(номер) возвращает true, если нажата кнопка под указанным номером. Метод .pressed() возвращает номер нажатой в данный момент кнопки, если таковых нет - возвращает -1.

Пример


/*
  Сигналы кнопок
  1023
  927
  856
  783
  671
  632
  590
  560
  504
  480
  455
  440
  399
  319
  255
  230
*/
#include "AnalogKey.h"
// указываем пин и количество кнопок
AnalogKey< A0, 16 > keys;

void setup() {
  Serial.begin(9600);
  
  // назначаем кнопкам их сигналы
  keys.attach(0, 1023);
  keys.attach(1, 927);
  keys.attach(2, 856);
  keys.attach(3, 783);
  keys.attach(4, 671);
  keys.attach(5, 632);
  keys.attach(6, 590);
  keys.attach(7, 560);
  keys.attach(8, 504);
  keys.attach(9, 480);
  keys.attach(10, 455);
  keys.attach(11, 440);
  keys.attach(12, 399);
  keys.attach(13, 319);
  keys.attach(14, 255);
  keys.attach(15, 230);
}

void loop() {  
  // проверяем каждую кнопку в ручном режиме
  if (keys.status(0)) Serial.println("press 0");
  if (keys.status(1)) Serial.println("press 1");
  if (keys.status(2)) Serial.println("press 2");
  if (keys.status(3)) Serial.println("press 3");
  if (keys.status(4)) Serial.println("press 4");
  if (keys.status(5)) Serial.println("press 5");
  if (keys.status(6)) Serial.println("press 6");

  // или выводим номер текущей нажатой (-1 значит ни одна не нажата)
  if (keys.pressed() != -1) Serial.println(keys.pressed());
  delay(10);
}

Интеграция с GyverButton


Для работы с GButton достаточно передавать в "тик" статус конкретных кнопок, таким образом аналоговую клавиатуру можно обрабатывать со всеми фишками GyverButton.
// новый пример работы с аналоговой клавиатурой
/*
  Сигналы кнопок
  1023
  927
  856
  783
  671
  632
  590
  560
  504
  480
  455
  440
  399
  319
  255
  230
*/
#include "AnalogKey.h"
// указываем пин и количество кнопок
AnalogKey< A0, 3 > keys;

#include 
GButton btn1, btn2, btn3;

void setup() {
  Serial.begin(9600);
  // указываем сигналы кнопок аналоговой клавы
  keys.attach(0, 1023);
  keys.attach(1, 927);
  keys.attach(2, 856);
}

void loop() {
  // тикаем кнопки как статусы аналоговых
  btn1.tick(keys.status(0));
  btn2.tick(keys.status(1));
  btn3.tick(keys.status(2));
  
  if (btn1.isClick()) Serial.println("click 0");
  if (btn2.isClick()) Serial.println("click 1");
  if (btn3.isClick()) Serial.println("click 2");

  if (btn1.isHolded()) Serial.println("click 0");
  if (btn2.isHolded()) Serial.println("hold 1");
  if (btn3.isHolded()) Serial.println("hold 2");
}

ПРИМЕРЫ


Остальные примеры смотри в папке examples библиотеки, также примеры можно открыть из Arduino IDE/Файл/Примеры

// Пример использования библиотеки GyverButton, все возможности в одном скетче.

#define BTN_PIN 3				// кнопка подключена сюда (BTN_PIN --- КНОПКА --- GND)

#include "GyverButton.h"
GButton butt1(BTN_PIN);

// Варианты инициализации:
// GButton btn;               // без привязки к пину (виртуальная кнопка) и без указания типа (по умолч. HIGH_PULL и NORM_OPEN)
// GButton btn(пин);          // с привязкой к пину и без указания типа (по умолч. HIGH_PULL и NORM_OPEN)
// GButton btn(пин, тип подключ.);    // с привязкой к пину и указанием типа подключения (HIGH_PULL / LOW_PULL) и без указания типа кнопки (по умолч. NORM_OPEN)
// GButton btn(пин, тип подключ., тип кнопки);      // с привязкой к пину и указанием типа подключения (HIGH_PULL / LOW_PULL) и типа кнопки (NORM_OPEN / NORM_CLOSE)
// GButton btn(BTN_NO_BTN_PIN, тип подключ., тип кнопки); // без привязки к пину и указанием типа подключения (HIGH_PULL / LOW_PULL) и типа кнопки (NORM_OPEN / NORM_CLOSE)

int value = 0;

void setup() {
  Serial.begin(9600);

  butt1.setDebounce(50);        // настройка антидребезга (по умолчанию 80 мс)
  butt1.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  butt1.setClickTimeout(600);   // настройка таймаута между кликами (по умолчанию 300 мс)

  // HIGH_PULL - кнопка подключена к GND, пин подтянут к VCC (BTN_PIN --- КНОПКА --- GND)
  // LOW_PULL  - кнопка подключена к VCC, пин подтянут к GND
  // по умолчанию стоит HIGH_PULL
  butt1.setType(HIGH_PULL);

  // NORM_OPEN - нормально-разомкнутая кнопка
  // NORM_CLOSE - нормально-замкнутая кнопка
  // по умолчанию стоит NORM_OPEN
  butt1.setDirection(NORM_OPEN);
}

void loop() {
  butt1.tick();  // обязательная функция отработки. Должна постоянно опрашиваться

  if (butt1.isClick()) Serial.println("Click");         // проверка на один клик
  if (butt1.isSingle()) Serial.println("Single");       // проверка на один клик
  if (butt1.isDouble()) Serial.println("Double");       // проверка на двойной клик
  if (butt1.isTriple()) Serial.println("Triple");       // проверка на тройной клик

  if (butt1.hasClicks())                                // проверка на наличие нажатий
    Serial.println(butt1.getClicks());                  // получить (и вывести) число нажатий

  if (butt1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (butt1.isRelease()) Serial.println("Release");     // отпускание кнопки (+ дебаунс)
  if (butt1.isHolded()) Serial.println("Holded");       // проверка на удержание
  if (butt1.isHold()) {	                                // если кнопка удерживается
    Serial.print("Holding ");                  // выводим пока удерживается
    Serial.println(butt1.getHoldClicks());     // можно вывести количество кликов перед удержанием!
  }
  //if (butt1.state()) Serial.println("Hold");          // возвращает состояние кнопки

  if (butt1.isStep()) {                                 // если кнопка была удержана (это для инкремента)
    value++;                                            // увеличивать/уменьшать переменную value с шагом и интервалом
    Serial.println(value);                              // для примера выведем в порт
  }
}
// Пример использования библиотеки GyverButton, все возможности в одном скетче.
// автоматический тик

#define BTN_PIN 3				// кнопка подключена сюда (BTN_PIN --- КНОПКА --- GND)

#include "GyverButton.h"
GButton butt1(BTN_PIN);
// GButton butt1(BTN_PIN, HIGH_PULL, NORM_OPEN); 	// можно инициализировать так

int value = 0;

void setup() {
  Serial.begin(9600);

  butt1.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  butt1.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)

  // HIGH_PULL - кнопка подключена к GND, пин подтянут к VCC (BTN_PIN --- КНОПКА --- GND)
  // LOW_PULL  - кнопка подключена к VCC, пин подтянут к GND
  // по умолчанию стоит HIGH_PULL
  butt1.setType(HIGH_PULL);

  // NORM_OPEN - нормально-разомкнутая кнопка
  // NORM_CLOSE - нормально-замкнутая кнопка
  // по умолчанию стоит NORM_OPEN
  butt1.setDirection(NORM_OPEN);

  // MANUAL - нужно вызывать функцию tick() вручную
  // AUTO - tick() входит во все остальные функции и опрашивается сама!
  butt1.setTickMode(AUTO);
}

void loop() {
  // butt1.tick();  // НЕ НУЖНА, в этом режиме (AUTO) она входит в каждую функцию

  if (butt1.isClick()) Serial.println("Click");         // проверка на один клик
  if (butt1.isSingle()) Serial.println("Single");       // проверка на один клик
  if (butt1.isDouble()) Serial.println("Double");       // проверка на двойной клик
  if (butt1.isTriple()) Serial.println("Triple");       // проверка на тройной клик

  if (butt1.hasClicks())                                // проверка на наличие нажатий
    Serial.println(butt1.getClicks());                  // получить (и вывести) число нажатий

  if (butt1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (butt1.isRelease()) Serial.println("Release");     // отпускание кнопки (+ дебаунс)
  if (butt1.isHolded()) Serial.println("Holded");       // проверка на удержание
  if (butt1.isHold()) Serial.println("Holding");        // проверка на удержание
  //if (butt1.state()) Serial.println("Hold");          // возвращает состояние кнопки

  if (butt1.isStep()) {                                 // если кнопка была удержана (это для инкремента)
    value++;                                            // увеличивать/уменьшать переменную value с шагом и интервалом
    Serial.println(value);                              // для примера выведем в порт
  }
}
Позволяет реализовать логику работы "клик-клик-удержание" с любым количеством кликов
// ловим степ с накликиванием
#define BTN_PIN 0				// кнопка подключена сюда (BTN_PIN --- КНОПКА --- GND)
#include "GyverButton.h"
GButton butt1(BTN_PIN);

void setup() {
  Serial.begin(9600);
}

void loop() {
  butt1.tick();

  // удержание
  if (butt1.isStep()) {
    Serial.println("0x");
  }

  // один клик + удержание
  if (butt1.isStep(1)) {
    Serial.println("1x");
  }

  // два клика + удержание
  if (butt1.isStep(2)) {
    Serial.println("2x");
  }
}
// Пример использования библиотеки GyverButton, 1- 2- 3- нажатие

#define BTN_PIN 3   // кнопка подключена сюда (BTN_PIN --- КНОПКА --- GND)

#include "GyverButton.h"
GButton butt1(BTN_PIN);

void setup() {
  Serial.begin(9600);
}

void loop() {
  butt1.tick();  // обязательная функция отработки. Должна постоянно опрашиваться

  if (butt1.isSingle()) Serial.println("Single");     // проверка на один клик
  if (butt1.isDouble()) Serial.println("Double");     // проверка на двойной клик
  if (butt1.isTriple()) Serial.println("Triple");     // проверка на тройной клик
}
// Пример использования библиотеки GyverButton
// опрос 5 кнопок в ручном режиме

// кнопки подключены к земле (PIN --- КНОПКА --- GND)
#define BTN1 3
#define BTN2 4
#define BTN3 5
#define BTN4 6
#define BTN5 7

#include "GyverButton.h"
GButton butt1(BTN1);
GButton butt2(BTN2);
GButton butt3(BTN3);
GButton butt4(BTN4);
GButton butt5(BTN5);

void setup() {
  Serial.begin(9600);
}

void loop() {
  // тик в ручном режиме
  butt1.tick();
  butt2.tick();
  butt3.tick();
  butt4.tick();
  butt5.tick();

  // проверяем одиночный клик
  if (butt1.isClick()) Serial.println("Button 1");
  if (butt2.isClick()) Serial.println("Button 2");
  if (butt3.isClick()) Serial.println("Button 3");
  if (butt4.isClick()) Serial.println("Button 4");
  if (butt5.isClick()) Serial.println("Button 5");
}
// Пример использования библиотеки GyverButton
// опрос 5 кнопок в автоматическом режиме

// кнопки подключены к земле (PIN --- КНОПКА --- GND)
#define BTN1 3
#define BTN2 4
#define BTN3 5
#define BTN4 6
#define BTN5 7

#include "GyverButton.h"
GButton butt1(BTN1);
GButton butt2(BTN2);
GButton butt3(BTN3);
GButton butt4(BTN4);
GButton butt5(BTN5);

void setup() {
  Serial.begin(9600);

  // устанавливаем опрос на автоматический
  butt1.setTickMode(AUTO);
  butt2.setTickMode(AUTO);
  butt3.setTickMode(AUTO);
  butt4.setTickMode(AUTO);
  butt5.setTickMode(AUTO);
}

void loop() {
  // проверяем одиночный клик
  // tick уже сидит внутри опроса
  if (butt1.isClick()) Serial.println("Button 1");
  if (butt2.isClick()) Serial.println("Button 2");
  if (butt3.isClick()) Serial.println("Button 3");
  if (butt4.isClick()) Serial.println("Button 4");
  if (butt5.isClick()) Serial.println("Button 5");
}
// Пример использования библиотеки GyverButton с аналоговой клавиатурой
// аналоговая клавиатура подключена на А7
// Схему смотри на странице библиотеки https://alexgyver.ru/gyverbutton/
// также она есть в папке с примером

#include "GyverButton.h"

// создаём кнопки без привязки к пину
GButton myButt1;
GButton myButt2;
GButton myButt3;

void setup() {
  Serial.begin(9600);
  // меняем тип на LOW_PULL, потому что по умолчанию стоит HIGH_PULL
  myButt1.setType(LOW_PULL);
  myButt2.setType(LOW_PULL);
  myButt3.setType(LOW_PULL);
}

void loop() {
  // читаем значение
  int analog = analogRead(7);

  // для начала нужно вывести и запомнить значение для каждой кнопки
  //Serial.println(analog);

  // проверяем у каждой кнопки свой диапазон (+- 20 от полученного значения)
  myButt1.tick(analog < 860 && analog > 820);
  myButt2.tick(analog < 740 && analog > 700);
  myButt3.tick(analog < 650 && analog > 600);

  // проверка на удержание, например
  if (myButt1.isHolded()) {
    Serial.println("hold 1");
  }
  if (myButt2.isHolded()) {
    Serial.println("hold 2");
  }
  if (myButt3.isHolded()) {
    Serial.println("hold 3");
  }
  
  delay(10);  // задержка тут не нужна, чисто для вывода
}
// Пример использования библиотеки GyverButton, все возможности в одном скетче.
// Дополнительный опрос по аппаратному прерыванию

#define BTN_PIN 3				// кнопка подключена сюда (BTN_PIN --- КНОПКА --- GND)

#include "GyverButton.h"
GButton butt1(BTN_PIN);
int value = 0;

void setup() {
  Serial.begin(9600);
  attachInterrupt(1, isr, CHANGE);

  butt1.setDebounce(80);      // настройка антидребезга (по умолчанию 80 мс)
  butt1.setTimeout(300);      // настройка таймаута на удержание (по умолчанию 500 мс)
}

void isr() {
  butt1.tick();  // опрашиваем в прерывании, чтобы поймать нажатие в любом случае
}

void loop() {
  butt1.tick();  // опрашиваем в скетче, иначе не будут работать проверки по времени!

  if (butt1.isClick()) Serial.println("Click");         // проверка на один клик
  if (butt1.isSingle()) Serial.println("Single");       // проверка на один клик
  if (butt1.isDouble()) Serial.println("Double");       // проверка на двойной клик
  if (butt1.isTriple()) Serial.println("Triple");       // проверка на тройной клик

  if (butt1.hasClicks())                                // проверка на наличие нажатий
    Serial.println(butt1.getClicks());                  // получить (и вывести) число нажатий

  if (butt1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (butt1.isRelease()) Serial.println("Release");     // отпускание кнопки (+ дебаунс)
  if (butt1.isHolded()) Serial.println("Holded");       // проверка на удержание
  //if (butt1.isHold()) Serial.println("Hold");         // возвращает состояние кнопки

  if (butt1.isStep()) {                                 // если кнопка была удержана (это для инкремента)
    value++;                                            // увеличивать/уменьшать переменную value с шагом и интервалом
    Serial.println(value);                              // для примера выведем в порт
  }
}
/*
   Пример использования библиотеки GyverButton, управляем переменной value при помощи двух кнопок
   Конструкция с isIncr делает увеличение/уменьшение переменной при нажатой кнопке с шагом по времени
*/

#define BTN1 2		// кнопка подключена сюда (PIN --- КНОПКА --- GND)
#define BTN2 3      // кнопка подключена сюда (PIN --- КНОПКА --- GND)

#include "GyverButton.h"
GButton butt1(BTN1);
GButton butt2(BTN2);
int value = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  butt1.tick();  // обязательная функция отработки. Должна постоянно опрашиваться
  butt2.tick();  // обязательная функция отработки. Должна постоянно опрашиваться

  if (butt1.isClick()) {           // одиночное нажатие
    value++;                       // инкремент
    Serial.println(value);         // для примера выведем в порт
  }

  if (butt2.isClick()) {           // одиночное нажатие
    value--;                       // декремент
    Serial.println(value);         // для примера выведем в порт
  }

  if (butt1.isStep()) {            // обработчик удержания с шагами
    value++;                       // увеличивать/уменьшать переменную value с шагом и интервалом!
    Serial.println(value);         // для примера выведем в порт
  }

  if (butt2.isStep()) {            // обработчик удержания с шагами
    value--;                       // увеличивать/уменьшать переменную value с шагом и интервалом!
    Serial.println(value);         // для примера выведем в порт
  }
}

ВИДЕО


ПОДДЕРЖАТЬ


Вы можете поддержать меня за создание доступных проектов с открытым исходным кодом, полный список реквизитов есть вот здесь.

4.6/5 - (11 голосов)
5 1 голос
Рейтинг статьи
Подписаться
Уведомить о
guest

35 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Прокрутить вверх