Как обеспечить считывание двойного клика кнопки в теле прерывания?

Ардуино УНО. Кнопка энкодера повешена на прерывание. В приведенном скетче, естественно, ее нажатие приводит к увеличению переменной button. Подскажите, как в “теле” прерывания можно отследить однократный и двойной клик? Есть ли ссылка, где посмотреть? Если кто поделится кусочком скетча - умру от счастья. Но лучше первый вариант (хочу разобраться). Заранее спасибо!

//UNO button
volatile int button; // Значение кнопки

void setup() 
{
  Serial.begin (115200); 
  pinMode (2, INPUT_PULLUP); 
  attachInterrupt(0, BUTTON, FALLING);
  button=0;
  
}

void loop() 
{
  Serial.println (button);
}
void BUTTON ()
{
  button++;
}

открой библиотеку Гайвера и посмотри там и двойной клик есть

Точно так же как и в теле основного цикла.
Если между текущим и предыдущим “кликами” прошло не более n-миллисекунд - значит это двойной клик.

Я пользуюсь библиотекой от @Kakmyc:

kakmyc_btn.cpp
// Библиотека кнопок - https://github.com/kakmyc-github/kakmyc_btn
#include "Arduino.h"
#include "kakmyc_btn.h"
#define _bounce_time 50
#define _long_press 1500
#define _wait_multiclick 300

                                                                          // функции класса Btn(обработчик кнопок)
byte kakmyc_btn::read() {
  uint16_t _millis = millis();
  !_state ? button = digitalRead(_pin) : button = !digitalRead(_pin);     // в зависимости от того, что в конструкторе настраиваем кнопку
  if (!button) {
    num = 0;                                                              // если кнопка отпущена, выдаем результат 0
  }
  if (button && !pressFlag) {                                             // если кнопка нажата и флаг опущен
    pressTime = _millis - start_press;                                    // считаем время нажатия кнопки
    if (pressTime >= _long_press) {                                       // если длительность нажатия больше 1,5 сек
      pressFlag = 1;                                                      // поднимаем флаг
      num = 255;                                                          // значение кнопки long
      pressTime = 0;                                                      // сбрасываем длительность нажатия
    }
  }
  if (!button) {                                                          // если кнопка отпущена
    start_press = _millis;                                                // сбрасываем время нажатия
    pressFlag = 0;                                                        // опускаем флаг
  }
  if (!button && !pressFlag) {                                            // если кнопка и флаг отпущены
    if (pressTime > _bounce_time && pressTime < _long_press) {            // а время нажатия больше 50мс, но меньше 1,5сек
      press_one++;                                                        // увеличиваем счетчик количества нажатий
      if (press_one > _val) press_one = _val;                             // ограничиваем значение счетчика
      pressTime = 0;                                                      // сбрасываем длительность нажатия
      double_press = _millis;                                             // запускаем таймер ожидания следующего нажатия
    }
  }
  if (press_one) {                                                        // если было короткое нажатие
    if (_millis - double_press >= _wait_multiclick) {                     // ждем 0,3сек
      pressTime = 0;                                                      // сбрасываем длительность нажатия
      num = press_one;                                                    // значение кнопки приравниваем к количеству нажатий
      press_one = 0;                                                      // сбрасываем количество нажатий
    }
  }
  _num = num;                                                             // для одиночного вывода результата, используем временную переменную
  num = 0;                                                                // обнудяем основную переменную значения кнопки
  return _num;                                                            // возвращаем значение кнопки
}
//функция конструктора класса
/*в конструкторе указываем:
  номер пина
  тип сигнала (лог0/лог1)
  максимальное количество отслеживаемых кликов*/
kakmyc_btn::kakmyc_btn(byte pin, byte state, byte val) {
  _pin = pin;                                                             // передаем внутренней переменной номер пина
  //!state?_state=2:_state=0;                                             // в зависимости от выбранного типа сигнала меняем значение переменной
  _state = state;
  _val = val;                                                             // передаем внутренней переменной количество кликов
  pinMode(pin, _state);                                                   // конфигурируем пин согласно типа сигнала
}

Только в прерывании переменные volatile должны быть…

1 лайк

Как то я себе слабо представляю обработку двойного клика по прерыванию.
Это тогда нужно таймер аппаратный заводить по первому клику, а по достижению счета вывод делать был второй клик или нет.
В общем сомнительная затея.
Даже представить не могу зачем может понадобиться данный функционал.

2 лайка

BOOM, такое в прерываниях не прокатит.
Событие одиночного клика будет обрабатывать некорректно.
Это в цикле мы вызовем при следующем проходе опрос, а в прерывании опрос будет только при следующем заходе в прерывание.
Т.е. при одиночном нажатии результата не будет никакого.

Ну я так никогда не “извращался”, просто предположил…

Подобное думал, но не применил еще. Может подойдет просто счетчик?

“В теле” - никак. Ожидание второго клика внутри обработчика противоречит самой сути прерывания.

Каждый клик регистрируйте отдельно и потом смотрите время между ними.

3 лайка

А нужен ли двойной клик? Особенно если есть действия которые нужно делать мгновенно по первому нажатию? Двойной слик в винде имеет логику - никогда его не пользуют сразу, только по подготовленному предварительно объекту. Т.е. идёт первый одиночный создание фокуса, затем в зависимости от одинарного или двойного выполняются те или иные действия. В смысле действий сразу, без фокуса, проще реагировать на длинное и короткое нажатие в зависимости от желаемой реакции. Нужно просто продумать логику управления.
Ни к чему не призываю. Просто мысли.

это если нет ресурсов на вторую кнопку

В Виндовсе событию DblClick всегда предшествует просто Click, одинарный. Вот где извращацца-то надо

Хочется узнать - а зачем ТС вообще с прерываниями для кнопок связался?
Может быть без прерываний обойтись можно?

Я ж спросил может не двойной а длинный? Чем хуже? А реализация проще и логика работы железки легче обслуживается. Или виндовая мышь уже условные рефлексы привила?

Гайвер вон и тройные, и четверные клики использует. И как только запоминает, сколько нужно кликнуть? ))

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

А иногда и простой, и двойной, и длинный приходится отслеживать. Не всегда же кучу кнопок можно в девайс напихать ))

Но сам тоже стараюсь двойной использовать только в третью очередь - не больно-то удобно двойные клики тактовыми кнопками исполнять

Логика различения двойного-одинарного сложнее, но вполне реализуется.
Главное что надо сделать - это вкладывать в прерывание только регистрацию события, а не его обработку.
Код в прерывании только взводит признак того, что сейчас была нажата кнопка.
Код в loop же постоянно проверяет этот признак и обнаружив сбрасывает и переходит в режим отслеживания числа нажатий. В этом режиме каждый раз до истечения таймаута он увеличивает счётчик нажатий (другая переменная) и снова отсрачивает (слово то какое!) таймер. И, наконец, когда таймер превысит лимит переходит к обработке нажатия - количество нажатий будет лежать в переменной и их может быть хоть 255. Так и надо в инструкции написать: если нажав первый раз вы передумали и хотите отменить нажатие всего лишь кликайте по кнопке еще 255 раз, байтовый счётчик обнулится и получится великолепное нажатие ноль раз. Тот самый случай когда красивый алгоритм помогает юзабилити.

1 лайк

самолёт … перекурить…это тройные и четверные

63 три раза перекурить и один раз самолёт настучать )))
Логично

Я всё пытаюсь протолкнуть мысль, что если есть двойной торойной и более клик, то придётся отслеживать всё до конца, что бы понять какой клик был и что с ним делать. Т.е. если нужен коротенький одинарный, то система всё равно должна ждать до до конца самого длинного клика или давать что типа - клик! ой нет! двойной! о! тройной…

Нет, сначала приходит событие onClick, а потом сразу же onDblClick, и попробуй отличи одно от другого, особенно, если ты их обрабатываешь оба.

Сначала клик (если таймер не запущен, запускаем), потом клик и даблклик. Если таймер отработал, а даблклика не последовало, то отрабатываем одиночный, иначе - двойной (таймер останавливаем принудительно)