Определение установившегося состояния на входах. антидребезг.

Тема в разделе "Arduino & Shields", создана пользователем SergeiL, 30 июн 2021.

  1. Unixon

    Unixon Оракул Модератор

    Исхожу из следующего принципа работы анти-дребезга: чтобы определить, что кнопка нажата или отпущена, в течение заданного времени не должно быть переключений, после того, как зарегистрировано последнее переключение. Если же переключение происходит (собственно сам дребезг), отсчет времени начинается от нового события. Т.е. для надежного детектирования требуется, чтобы заданное время прошло с момента окончания дребезга. Это увеличивает время реакции на время дребезга, однако в противном случае невозможно надежно сказать, было ли переключение и в какое состояние. Для программы уровнем выше событием нажатия\отпускания кнопки является изменение стабилизированного состояния. Процедура типа "N измерений через M миллисекунд" пропускает весь дребезг между измерениями и дает вероятностный результат. Это происходит и в моей реализации тоже. Собственно критика состоит в том, что при малом N получается высокая вероятность ошибки. Однако, если на кнопках будет висеть аппаратный ФНЧ и т.о. гарантировать, что помех в проблемной области АЧХ нет, тогда такой метод становится очень даже неплох, но следует помнить, что ФНЧ тоже будет гробить время реакции.
     
    SergeiL нравится это.
  2. akl

    akl Гуру

    вот походу добыдлил более-менее
    короче два "класса" кнопок:
    BUTTONPUSH - реагирует на простое нажатие и удержание (можно включать и выключать удержание и время повторения при удержании)
    BUTTONHOLD - реагирует на нажатие, на отпускание через короткое время, а так же на удержание долгое время. повторения нет

    к ним идут две пары функций -
    readButtonPush
    readButtonPushD
    readButtonHold
    readButtonHoldD

    Push и Hold соответственно жрут соответствующие структуры.
    с D в конце имеют задержку срабатывания, то есть могут игнорировать короткие ложные импульсы.
    без буквы D сработают на фронт сразу без задержек, антидребезг там постфактумный типа.

    сразу вместе с виндосом:

    Код (C++):
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>

    unsigned long millis(void){
        return GetTickCount();
    }
    unsigned char digitalRead(int key){
        if(GetKeyState(key)&0b10000000){return 0;}
        else{return 1;}
    }

    #define BUTT_1 '1'
    #define BUTT_2 '2'
    #define BUTT_3 '3'

    #define BUTT_4 '4'
    #define BUTT_5 '5'
    #define BUTT_6 '6'

    #define DEBTIME 100
    #define REPEATTIME 100
    typedef struct _BUTTONPUSH {
      unsigned char pin;
      unsigned int debtime;
      unsigned char repeat;
      unsigned int reptime;
      unsigned char state;
      unsigned char flag;
      unsigned long timer;
      unsigned char r_push;
      unsigned char r_fast;
      unsigned char r_hold;
    } BUTTONPUSH;
    BUTTONPUSH bt1 = {BUTT_1, DEBTIME,0};
    BUTTONPUSH bt2 = {BUTT_2, DEBTIME,1,REPEATTIME,0};
    BUTTONPUSH bt4 = {BUTT_4, DEBTIME,0};
    BUTTONPUSH bt5 = {BUTT_5, DEBTIME,1,REPEATTIME,0};
    void readButtonPush(BUTTONPUSH *b, unsigned long* curr_time) {
        b->state = !digitalRead(b->pin);
        b->r_push = 0;
        if (b->state && !b->flag) {
            b->flag = 1;
            b->timer = *curr_time;
            b->r_push = 1;
        }
        if (b->flag==1 && *curr_time - b->timer > b->debtime) {
            b->timer = *curr_time;
            if (!b->state) {
                b->flag = 0;
            }else if(b->repeat){
                b->flag = 2;
            }
        }
        if (b->flag==2 && *curr_time - b->timer > b->reptime) {
            b->timer = *curr_time;
            if (!b->state) {
                b->flag = 0;
            }else{
                b->r_push = 1;
            }
        }
    }
    void readButtonPushD(BUTTONPUSH *b, unsigned long* curr_time) {
        b->state = !digitalRead(b->pin);
        b->r_push = 0;
        if (b->state && !b->flag) {
            b->flag = 1;
            b->timer = *curr_time;
        }
        if (!b->state && b->flag){
            b->flag = 0;
        }
        if (b->flag==1 && *curr_time - b->timer > b->debtime) {
            b->r_push = 1;
            b->flag = 2;
            b->timer = *curr_time;
        }
        if (b->repeat && b->flag==2 && *curr_time - b->timer > b->reptime) {
            b->timer = *curr_time;
            b->r_push = 1;
        }
    }

    #define FASTTIME 200
    #define HOLDTIME 1000
    typedef struct _BUTTONHOLD {
      unsigned char pin;
      unsigned int debtime;
      unsigned int fasttime;
      unsigned int holdtime;
      unsigned char state;
      unsigned char flag;
      unsigned long timer;
      unsigned char r_push;
      unsigned char r_fast;
      unsigned char r_hold;
    } BUTTONHOLD;
    BUTTONHOLD bt3 = {BUTT_3, DEBTIME, FASTTIME, HOLDTIME,0};
    BUTTONHOLD bt6 = {BUTT_6, DEBTIME, FASTTIME, HOLDTIME,0};
    void readButtonHold(BUTTONHOLD* b, unsigned long* curr_time) {
        b->state = !digitalRead(b->pin);
        b->r_hold = 0;
        b->r_fast = 0;
        b->r_push = 0;
        if (b->state && !b->flag) {
            b->flag = 1;
            b->timer = *curr_time;
            b->r_push=1;
        }
        if(b->flag == 1){
            if(!b->state){
                if(*curr_time - b->timer > b->fasttime){
                    b->r_fast = 1;
                    b->flag = 2;
                    b->timer = *curr_time;
                }else{
                    b->flag = 2;
                }
            }else if(*curr_time - b->timer > b->holdtime){
                b->r_hold = 1;
                b->flag = 2;
                b->timer = *curr_time;
            }
        }
        if(!b->state && b->flag==2 && *curr_time - b->timer > b->debtime){
            b->flag=0;
        }
    }
    void readButtonHoldD(BUTTONHOLD* b, unsigned long* curr_time) {
        b->state = !digitalRead(b->pin);
        b->r_hold = 0;
        b->r_fast = 0;
        b->r_push = 0;
        if (b->state && !b->flag) {
            b->flag = 1;
            b->timer = *curr_time;
        }
        if (!b->state && b->flag==1){
            b->flag = 0;
        }
        if (b->flag==1 && *curr_time - b->timer > b->debtime) {
            b->r_push = 1;
            b->flag = 2;
            b->timer = *curr_time;
        }
        if(b->flag == 2){
            if(!b->state){
                if(*curr_time - b->timer > b->fasttime){
                    b->r_fast = 1;
                    b->flag = 3;
                    b->timer = *curr_time;
                }else{
                    b->flag = 3;
                }
            }else if(*curr_time - b->timer > b->holdtime){
                b->r_hold = 1;
                b->flag = 3;
                b->timer = *curr_time;
            }
        }
        if(!b->state && b->flag==3 && *curr_time - b->timer > b->debtime){
            b->flag=0;
        }
    }


    unsigned long mil;
    int main()
    {
    while(1){
        mil=millis();

        readButtonPush(&bt1,&mil);
        if(bt1.r_push){printf("%c%s\n",BUTT_1," push");}
        readButtonPush(&bt2,&mil);
        if(bt2.r_push){printf("%c%s\n",BUTT_2," push");}
        readButtonHold(&bt3,&mil);
        if(bt3.r_push){printf("%c%s\n",BUTT_3," push");}
        if(bt3.r_fast){printf("%c%s\n",BUTT_3," fast");}
        if(bt3.r_hold){printf("%c%s\n",BUTT_3," hold");}

        readButtonPushD(&bt4,&mil);
        if(bt4.r_push){printf("%c%s\n",BUTT_4," push");}
        readButtonPushD(&bt5,&mil);
        if(bt5.r_push){printf("%c%s\n",BUTT_5," push");}
        readButtonHoldD(&bt6,&mil);
        if(bt6.r_push){printf("%c%s\n",BUTT_6," push");}
        if(bt6.r_fast){printf("%c%s\n",BUTT_6," fast");}
        if(bt6.r_hold){printf("%c%s\n",BUTT_6," hold");}
    }
    return 0;
    }
    чтобы нормально потестить в виндосе можно дефайны
    DEBTIME REPEATTIME FASTTIME HOLDTIME
    увеличить в несколько раз, тогда можно вручную быстро тыкая кнопки имитировать дребезги и помехи

    это конечно какая-то дичь жрущая кучу ресурсов и далеко не идеально работающая, но мне нравится
    что думаю полезно будет добавить - в функции Push изменение скорости повторений. типа держишь - повторяет медленно, держишь дальше - начинает повторять быстрее.

    нашел недостаток - в функции readButtonPushD при зажатой кнопке короткий импульс может вызвать ложное срабатывание. но это не дребезг, а именно шум. Но при зажатой кнопке когда она замкнута на землю, шум маловероятен

    о readButtonHold как-то криво работает. надо доделывать.
     
    Последнее редактирование: 4 июл 2021
  3. parovoZZ

    parovoZZ Гуру

    прерывания не копятся. Взводится флаг, а если прерывание разрешено, то оно будет выполнено. Если запросов на прерывание от одной ноги снаружи поступило очень много, а ты их всех не успел обработать, то все они уйдут в небытие, так как флаг всего один.
     
  4. SergeiL

    SergeiL Оракул Модератор

    Полностью согласен. Я понимаю о чем речь.
    Предложенный алгоритм как раз обеспечивает сброс счетчика в "0", при получении неожидаемого значения на любом шаге, хоть на последнем.
    По поводу вероятности ошибки, мы же предположительно знаем время дребезга. Он же не может быть в три раза дольше, чем мы предполагаем?
    То есть мы должны получить 4 корректных ответа из 4 ожидаемых. Получили неожидаемый - сбросили счетчик - начали заново.

    Беспроблемно можно сделать и 3 бита вертикального счетчика (плюс одна переменная и строчка для инкремента счетчика).
    Это конечно значительно уменьшит вероятность ошибки, будет - восемь из восьми.
    Но и при 2-х битах я ошибок ни разу не видел.



    UPD: Добавил 1 разряд - теперь 3 разряда вертикального счетчика, быстродействие не сильно изменилось. Как говорят, найдите 4 отличия с примером 1 ;).
    Функция для "восемь из восьми", вызываем в 2 раза чаще 5мс:
    Код (C++):
    void Input_CHK(void)         // вызывается каждые 5 мсек, можно из таймера
    {
        static uint8_t SLATCH = 0;            // текущее устоявшееся значение входов после устранения дребезга
        static uint8_t VCBIT0 = 0;            // Вертикальный счетчик бит 0
        static uint8_t VCBIT1 = 0;            // Вертикальный счетчик бит 1
        static uint8_t VCBIT2 = 0;            // Вертикальный счетчик бит 2

        uint8_t        SWKEYS = 0;            // состояние вводов
        uint8_t        VCTEMP = 0;            // промежуточная переменная
        uint8_t        VCMASK = 0;            // Маска

        SWKEYS = Input_Read();                         // загрузим значения входов в глобальную переменную SWKEYS

        VCMASK = SWKEYS ^ SLATCH;                   // скинем счетчики для установившихся и неактивных значений
        VCBIT0 &= VCMASK;
        VCBIT1 &= VCMASK;
        VCBIT2 &= VCMASK;
                                                      //  Каждая '1' в SLATCH представляет установившееся значение
                                                 
        SLATCH ^= (VCTEMP = VCMASK & VCBIT0 & VCBIT1 & VCBIT2);

        if( VCTEMP )                                  // есть изменения входов, взведем флаги
      {
         LATCH_ON  |= VCTEMP &  SWKEYS;               // взведем биты нажатых кнопок и сработавших входов.
         LATCH_OFF |= VCTEMP & ~SWKEYS;               // Биты сбрасываются в обработчиках.
      }

        VCBIT2 ^= (VCBIT1 & VCBIT0 & VCMASK);          // инкрементируем счетчик.
        VCBIT1 ^= (VCBIT0 & VCMASK);                
        VCBIT0 ^= (VCMASK);
    }
     
    Вход последовательность считываемых значений:
    Код (C++):
    uint8_t test_buff[] = {0,1,3,0,1,3,3,3,3,1,1,3,1,2,2,4,0,2,0,0,0,0,1,0,0};   // тестовый массив со значениями.
     
    Вывод:
    Serial Connected
    Inp=0 00000000
    Inp=1 00000001
    Inp=3 00000011
    Inp=0 00000000
    Inp=1 00000001
    Inp=3 00000011
    Inp=3 00000011
    Inp=3 00000011
    Inp=3 00000011
    Inp=1 00000001
    Inp=1 00000001
    Inp=3 00000011
    Input 0 = ON
    Inp=1 00000001
    Inp=2 00000010
    Inp=2 00000010
    Inp=4 00000100
    Inp=0 00000000
    Inp=2 00000010
    Inp=0 00000000
    Inp=0 00000000
    Inp=0 00000000
    Input 0 = OFF
    Inp=0 00000000
    Inp=1 00000001
    Inp=0 00000000
    Inp=0 00000000
     
    Последнее редактирование: 5 июл 2021
  5. akl

    akl Гуру

    полностью переделал быдло-алгоритмы.
    теперь стало совсем классно, дебоунсит моднейше.

    две функции
    readButtonPush
    readButtonHold
    опцией rapidpush можно включить реагирование нажатия на фронт без задержки (для геймеров, которые ненавидят инпут-лаг)
    readButtonPush в зависимости от настроек созданной кнопки BUTTONPUSH может действовать однократно либо повторять с определенной частотой будучи зажатой
    readButtonHold делает разное при нажатии, при отпускании после короткого удержания и при долгом нажатии. Особенность - если во время долгого нажатия будет короткий импульс (короче чем время антидребезга) то это не будет воспринято как отпускание, но время длинного нажатия обнулится и соответственно будет продлено. Это можно исправить если завести еще одну переменная под счетчик времени чтоб антидребезг отдельно измерять.

    Код (C++):
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>

    unsigned long millis(void){
        return GetTickCount();
    }
    unsigned char digitalRead(int key){
        if(GetKeyState(key)&0b10000000){return 0;}
        else{return 1;}
    }

    #define BUTT_1 '1'
    #define BUTT_2 '2'
    #define BUTT_3 '3'

    #define BUTT_4 '4'
    #define BUTT_5 '5'
    #define BUTT_6 '6'

    #define DEBTIME 50
    #define REPEATTIME 200
    typedef struct _BUTTONPUSH {
      unsigned char pin;
      unsigned int debtime;
      unsigned char repeat;
      unsigned int reptime;
      unsigned char state;
      unsigned char state_o;
      unsigned char deb;
      unsigned long timer;
      unsigned char r_push;
    } BUTTONPUSH;
    BUTTONPUSH bt1 = {BUTT_1, DEBTIME,0};
    BUTTONPUSH bt2 = {BUTT_2, DEBTIME,1,REPEATTIME,0};
    BUTTONPUSH bt4 = {BUTT_4, DEBTIME,0};
    BUTTONPUSH bt5 = {BUTT_5, DEBTIME,1,REPEATTIME,0};
    void readButtonPush(BUTTONPUSH *b, unsigned long* curr_time, unsigned char rapidpush) {
        b->r_push = 0;
        b->state = !digitalRead(b->pin);
        if(b->state_o != b->state &&!b->deb){
            b->deb=1;
            b->timer = *curr_time;
            if(b->state && rapidpush){b->r_push=1;}
        }
        if(b->deb && *curr_time - b->timer > b->debtime){
            b->deb=0;
        }
        if(!b->deb){
            if(!b->state_o && b->state){
                b->state_o = b->state;
                if(!rapidpush){
                    b->r_push=1;
                    b->timer = *curr_time;
                }

            }
            if(b->state_o && b->state){
                if(b->repeat && *curr_time - b->timer > b->reptime){
                    b->timer = *curr_time;
                    b->r_push=1;
                }
            }
            if(b->state_o && !b->state){
                b->state_o = b->state;
            }
        }
    }

    #define FASTTIME 100
    #define HOLDTIME 2000
    typedef struct _BUTTONHOLD {
      unsigned char pin;
      unsigned int debtime;
      unsigned int fasttime;
      unsigned int holdtime;
      unsigned char state;
      unsigned char state_o;
      unsigned char deb;
      unsigned long timer;
      unsigned char flag;
      unsigned char r_push;
      unsigned char r_fast;
      unsigned char r_hold;
    } BUTTONHOLD;
    BUTTONHOLD bt3 = {BUTT_3, DEBTIME, FASTTIME, HOLDTIME,0};
    BUTTONHOLD bt6 = {BUTT_6, DEBTIME, FASTTIME, HOLDTIME,0};
    void readButtonHold(BUTTONHOLD* b, unsigned long* curr_time, unsigned char rapidpush) {
        b->r_push = 0;
        b->r_fast = 0;
        b->r_hold = 0;
        b->state = !digitalRead(b->pin);
        if(b->state_o != b->state &&!b->deb){
            b->deb=1;
            b->timer = *curr_time;
            if(b->state && rapidpush){
                b->r_push=1;
            }
        }
        if(b->deb && *curr_time - b->timer > b->debtime){
            b->deb=0;
        }
        if(!b->deb){
            if(!b->state_o && b->state){
                b->state_o = b->state;
                if(!rapidpush){
                    b->r_push=1;
                    b->timer = *curr_time;
                }
            }
            if(b->state_o && b->state){
                if(*curr_time - b->timer > b->holdtime && !b->flag){
                    b->r_hold=1;
                    b->flag=1;
                }
            }
            if(b->state_o && !b->state){
                b->state_o = b->state;
                if(!b->flag && *curr_time - b->timer > b->fasttime && *curr_time - b->timer < b->holdtime){
                    b->r_fast = 1;
                }
                b->flag=0;
            }
        }
    }

    unsigned long mil;
    int main()
    {
    while(1){
        mil=millis();

        readButtonPush(&bt1,&mil,0);
        if(bt1.r_push){printf("%c%s\n",BUTT_1," push");}
        readButtonPush(&bt2,&mil,0);
        if(bt2.r_push){printf("%c%s\n",BUTT_2," push");}
        readButtonHold(&bt3,&mil,0);
        if(bt3.r_push){printf("%c%s\n",BUTT_3," push");}
        if(bt3.r_fast){printf("%c%s\n",BUTT_3," fast");}
        if(bt3.r_hold){printf("%c%s\n",BUTT_3," hold");}

        readButtonPush(&bt4,&mil,1);
        if(bt4.r_push){printf("%c%s\n",BUTT_4," push");}
        readButtonPush(&bt5,&mil,1);
        if(bt5.r_push){printf("%c%s\n",BUTT_5," push");}
        readButtonHold(&bt6,&mil,1);
        if(bt6.r_push){printf("%c%s\n",BUTT_6," push");}
        if(bt6.r_fast){printf("%c%s\n",BUTT_6," fast");}
        if(bt6.r_hold){printf("%c%s\n",BUTT_6," hold");}


    }
    return 0;
    }
    теоретически, в эти функции в раздел антидребезга можно засунуть широко обсуждаемый тут ФНЧ или что-то вроде, надо будет попробовать
     
    Последнее редактирование: 5 июл 2021
    SergeiL нравится это.
  6. SergeiL

    SergeiL Оракул Модератор

    Так и есть, предложенный здесь алгоритм антидребезга - это по сути низкий уровень.
    На выходе получаем событие по нажатию кнопки и событие по ее отпусканию.
    Дальше, на основании этого - формирование событий по коротким, длительным двойным и т.д. нажатиям.
    Просто для кнопок это нужно, а для проводных датчиков - главное убрать импульсные помехи и дребезг
     
  7. akl

    akl Гуру

    запилил библиотеку ардуины для кнопок. На ардуине не тестировал, тестировал на виндосе, но вроде должно работать - библиотека кроссплатформенная. на си++ прям с классами :cool:
     

    Вложения:

    • butt.zip
      Размер файла:
      2,8 КБ
      Просмотров:
      117
  8. KindMan

    KindMan Гуру

    Архи запутано, куча переменных, ничего непонятно ... прям как моя первая программа, где мне нужно было обрабатывать кнопки :)
     
  9. akl

    akl Гуру

    это пожалуй лучший алгоритм кнопкодавства. интуитивно понятный интерфейс в лучших традициях ооп сочетает надежность с кроссплатформенностью и широкими возможностями.
    звоните и заказывайте прямо сейчас
     
  10. KindMan

    KindMan Гуру

    Я у себя использую такой вариант:
    Код (C++):
    #pragma once
    #include "Arduino.h"

    const uint8_t Button_COUNT = 6; // Количество подключенных кнопок (0..3 - А0..А3, 4..5 - А6..А7, А4 А5 - I2C) должны быть "подтянуты" к VCC
    const uint16_t Button_adc = 50; // Уровень нажатия кнопки ADC
    const uint16_t Moved_active = 700; // Уровень сработки датчика движения ADC
    const uint16_t Button_hold = 1000; // Время, через которое считается, что кнопка удержана ms
    const uint16_t Button_calc = 1000; // Время, после которого считаем количество нажатий ms
    const uint8_t Moved_Delay = 100; // Время игнорирования датчика движения после срабатывания ms (значение * 50ms)

    enum BTN_Oper : uint8_t  {Idle = 0, Push, Wait, Hold};
    enum BTN_State : uint8_t {Release, Press};

    typedef struct {
      BTN_Oper Oper;
      uint32_t TimePress;
      uint8_t PressCount;
      } Button;

    class HMC_Button {
      private:
        Button Buttons[Button_COUNT];
        boolean debounce;
        void Do(uint8_t ButtonNumber, BTN_State in);
     
      public:
        HMC_Button();
        ~HMC_Button();
        uint8_t Moved[Button_COUNT];
        void run();          
        inline virtual void onPush(uint8_t) {};           // После нажатия
        inline virtual void onClick(uint8_t) {};          // После клика
        inline virtual void onHold(uint8_t) {};           // Удержание кнопки
        inline virtual void onCMD(uint8_t, uint8_t) {};   // После подсчета нажатий
        inline virtual void onAfterHold(uint8_t) {};      // После отпускания удержанной кнопки
        inline virtual void onMoved(uint8_t) {};          // Обнаружено движение
    };
    Код (C++):
    #include "Button.h"

    HMC_Button::HMC_Button() {}

    HMC_Button::~HMC_Button() {}

    void HMC_Button::Do(uint8_t ButtonNumber, BTN_State in) {
      if (in == Release) {// Кнопка отпущена
        switch (Buttons[ButtonNumber].Oper) {
          case Idle: // Ждём первое нажатие
            break;
          case Push: // Кнопка была нажата
            Buttons[ButtonNumber].PressCount++; // Прибавляем кол-во нажатий
            onClick(ButtonNumber);  // Генерируем событие "клик"
            Buttons[ButtonNumber].Oper = Wait;  
            break;
          case Wait:
            if((millis() - Buttons[ButtonNumber].TimePress) < Button_calc) break; // Пришло время считать кол-во нажатий кнопки
              Buttons[ButtonNumber].Oper = Idle;
              onCMD(ButtonNumber, Buttons[ButtonNumber].PressCount); // Сообщаем, сколько насчитали
              Buttons[ButtonNumber].PressCount = 0;
            break;
          case Hold: // Кнопку отпустили после удержания
            onAfterHold(ButtonNumber); // Сообщим об этом
            Buttons[ButtonNumber].PressCount = 0;
            Buttons[ButtonNumber].Oper = Idle;
            break;
        }
    }
    else {
        switch (Buttons[ButtonNumber].Oper) {
          case Idle:
          case Wait:
            Buttons[ButtonNumber].TimePress = millis();
            Buttons[ButtonNumber].Oper = Push;
            onPush(ButtonNumber);   // Кнопку нажали      
            break;
          case Push:
            if((millis() - Buttons[ButtonNumber].TimePress) < Button_hold) break; // Кнопку держат достаточно долго
              Buttons[ButtonNumber].Oper = Hold;
              onHold(ButtonNumber); // Генерируем событие о удержании
            break;
        }
      }
    }

    void HMC_Button::run() {
      uint8_t T;
      for (uint8_t N = 0; N <= Button_COUNT-1; N++) { // Проверяем все подключенные кнопки
         (N>3) ? T = 2 : T = 0; // "Обходим" входы А4 и А5
         uint16_t A = analogRead(N+T); // Считываем состояние
         (A < Moved_active) ? debounce = true : debounce = false;
         if (Moved[N] > 0) { // Датчик движения сработал ранее
                Moved[N]--; //уменьшаем время игнора
            } else {
              if (A > Button_adc && A < Moved_active && debounce) { // Проверяем на наличие движения
                  onMoved(N);  // Сигнализируем о движении
                  Moved[N] = Moved_Delay; //  Включаем игнор на последующее движение
            }
         }
         (A < Button_adc && debounce) ? Do(N, Press) : Do(N, Release); // Смотрим нажата ли кнопка
      }
    }
    Код (C++):
    #include "Button.h"

    class myButton: public HMC_Button {
      public:
        void onPush(uint8_t ButtonNumber);               // После нажатия
        void onClick(uint8_t ButtonNumber);              // После клика
        void onHold(uint8_t ButtonNumber);               // Удержание кнопки
        void onCMD(uint8_t ButtonNumber, uint8_t count); // После подсчета нажатий
        void onAfterHold(uint8_t ButtonNumber);          // После отпускания удержанной кнопки
        void onMoved(uint8_t ButtonNumber);              // Сработал датчик движения
    };

    void myButton::onHold(uint8_t ButtonNumber) {
      Serial.print("Button HOLD - ");
      Serial.println(ButtonNumber);
    }

    void myButton::onCMD(uint8_t ButtonNumber, uint8_t count) {
      Serial.print("Button - ");
      Serial.print(ButtonNumber);
      Serial.print("; ClickCount - ");
      Serial.println(count);
    }

    void myButton::onPush(uint8_t ButtonNumber) {
      Serial.print("Button PUSH - ");
      Serial.println(ButtonNumber);
    }

    void myButton::onClick(uint8_t ButtonNumber) {
      Serial.print("Button CLICK - ");
      Serial.println(ButtonNumber);
    }

    void myButton::onAfterHold(uint8_t ButtonNumber) {
      Serial.print("Button RELEASED - ");
      Serial.println(ButtonNumber);
    }

    void myButton::onMoved(uint8_t ButtonNumber) {
      Serial.print("Moved - ");
      Serial.println(ButtonNumber);
    }

    myButton Switch;
    uint32_t Timer;

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

    void loop()
    {  
      if (millis() - Timer > 50) {    
        Switch.run();
        Timer = millis();
      }
    }
    У меня это в схеме управления освещением в квартире работает: 6 выключателей без фиксации и 4 датчика движения, подключены по схеме ниже к аналоговым входам, т.е. на 1 вход можно повесить несколько кнопок сразу. Но в функции run можно выбрать любой пин, без переделки остального функционала.

    amp_btn.jpg
     
    SergeiL и akl нравится это.
  11. akl

    akl Гуру

    тут тоже всё изрядно запутано. а в моей методике по сути четыре элементарные функции, которым скармливаешь текущее состояние пина (аналоговое значение в диапазоне тоже можно туда пихать) и текущее время, а обратно берешь флаги-события типа кнопка была нажата, отпущена, или удерживалась определенное время, или отпущена до того как прошло время, так же есть вариант с автоповтором нажатий при удержании.
    При этом антидребезг в двух вариантах - стандартный с задержкой и противоположный с мгновенной реакцией на случай если инпут-лаг не нужен.