Снова о кнопке.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Вынесу идею по чтению состояния кнопки на форум. 

Состояние кнопки содержится в переменной stateButt.
БИТ_0 - настоящее состояние
БИТ_1 - предыдущее состояние
биты 2-7 - история состояний

Скетч

word carrMillis, prevMillis, intervalMs = 40;
const byte pinButt = 7;
#define NOT_PRESSED B11
#define CLICK_DOWN  B10
#define RETENTION    0
#define CLICK_UP    B01
byte stateButt = NOT_PRESSED;

void setup() { 
  Serial.begin(9600);  
  pinMode(pinButt, INPUT_PULLUP);
}

void loop() {
  carrMillis = millis();
  if(carrMillis - prevMillis >= intervalMs){
    prevMillis = carrMillis;
    stateButt <<= 1;
    if(digitalRead(pinButt))  stateButt |= 1;
    switch (stateButt & B11){
    case NOT_PRESSED:   
      Serial.println(1);
      break;
    case CLICK_DOWN:   
      Serial.println("CLICK_DOWN");
      break;
    case RETENTION:   
      Serial.println(0);
      break;
    case CLICK_UP:   
      Serial.println("CLICK_UP");
      break;
    }   
  }
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

У меня два вопроса:

1. Зачем это может понадобиться?

2. Что делать, если у меня в проекте 40 кнопок?

sadman41
Offline
Зарегистрирован: 19.10.2016

andriano пишет:

2. Что делать, если у меня в проекте 40 кнопок?

Призвать Квона заклинанием "class Cl_but"

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

та шо его призывать, он сам придёть. 

Pyotr
Offline
Зарегистрирован: 12.03.2014

andriano пишет:

У меня два вопроса:

1. Зачем это может понадобиться?

2. Что делать, если у меня в проекте 40 кнопок?

1. Вот купит новичок Ардуино, кнопку и захочет прикрутить ее к плате. Зайдет на ардуино.ру а тут готовый пример. Он обрадуется и на радостях кинет мне в кошелек пару баксов. Вот я и разбогатею))

2. Застрелицца)) Я больше пяти не подключал...

А если серьезно, это всего лишь один из вариантов работы с кнопкой.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Pyotr пишет:
1. Вот купит новичок Ардуино, кнопку и захочет прикрутить ее к плате. Зайдет на ардуино.ру а тут готовый пример. Он обрадуется и на радостях кинет мне в кошелек пару баксов. Вот я и разбогатею))
 Вот соберутся уважаемые люди в бане,снимут мундир,и уже не понятно, кто уважаемый, а кто пиво им носит. 

Так что я не против, вы богатейте по маленьку. дурень думкою багатие

nik182
Онлайн
Зарегистрирован: 04.05.2015

А вот ещё время помнить. И повтор при нажатой иметь.

Pyotr
Offline
Зарегистрирован: 12.03.2014

nik182 пишет:

А вот ещё время помнить. И повтор при нажатой иметь.

Повтор при удержании примерно так.

#define NOT_PRESSED B11
#define CLICK_DOWN  B10
#define RETENTION    0
#define CLICK_UP    B01
byte stateButt = NOT_PRESSED;
word carrMillis, prevMillis, intervalMs = 40, howLongPress;
const byte pinButt = 7;
byte repeat = 0;

void setup() { 
  Serial.begin(9600);  
  pinMode(pinButt, INPUT_PULLUP);
}

void loop() {
  carrMillis = millis();
  if(carrMillis - prevMillis >= intervalMs){
    prevMillis = carrMillis;
    stateButt <<= 1;
    if(digitalRead(pinButt))  stateButt |= 1;
    
    switch (stateButt & B11){   
    case CLICK_DOWN:   
      Serial.println("CLICK_DOWN");
      break;
    case RETENTION:       
      howLongPress++;
      if(howLongPress < 50) 
        repeat = !(howLongPress%16);
      else 
        repeat = !(howLongPress%8);
      if(repeat)  Serial.println("REPEAT");
      break; 
    case CLICK_UP:   
      Serial.println("CLICK_UP");
      howLongPress = 0;
      repeat = 0;
      break;   
    }   
  }
}

А время клика помнить не обязательно. История состояния за 0.3 сек содержится в stateButt

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Pyotr пишет:

1. Вот купит новичок Ардуино, кнопку и захочет прикрутить ее к плате. Зайдет на ардуино.ру а тут готовый пример. Он обрадуется и на радостях кинет мне в кошелек пару баксов. Вот я и разбогатею))

Ну, прикрутить - дело не хитрое.

А опросить - достаточно одной строки buttonState = digitalRead(buttonPin);

Зачем больше?

Цитата:

А если серьезно, это всего лишь один из вариантов работы с кнопкой.

И чем этот вариант лучше других? (чем хуже - понятно: длиннее)

Pyotr
Offline
Зарегистрирован: 12.03.2014

andriano, троллите что-ли?

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

#define NOT_PRESSED B11
#define CLICK_DOWN  B10
byte stateButt = NOT_PRESSED;
word carrMillis, prevMillis, intervalMs = 50, howLongPress;
const byte pinButt = 7;
byte repeat = 0, retention;

void setup() { 
  Serial.begin(9600);  
  pinMode(pinButt, INPUT_PULLUP);
}

void loop() {
  carrMillis = millis();
  if(carrMillis - prevMillis >= intervalMs){
    prevMillis = carrMillis;
    stateButt <<= 1;
    
    if(digitalRead(pinButt)){//сейчас отпущена
      stateButt |= 1;
      retention = 0;
      howLongPress = 0;
      repeat = 0;
    }
            
    if((stateButt >> 6) == CLICK_DOWN){//была нажата 7*50=350 мс назад
      if(stateButt == 0x80){//Всё это время не отпускалась
         retention = 1;
         Serial.println("RETENTION");     
      }else{     
         byte numClick = 1;
         for(byte i=0; i<6; i++){
           if(((stateButt >> i) & B11) == CLICK_DOWN){ 
             numClick++;
             break;
           }
         }
         stateButt = 0;
         Serial.print("CLICK=");
         Serial.println(numClick);
       }     
     }
     if(retention){
       howLongPress++;
       if(howLongPress < 35) repeat = !(howLongPress%8);
       else                  repeat = !(howLongPress%3);
       if(repeat) Serial.println("REPEAT");       
    }
  }
}

 

 

Logik
Offline
Зарегистрирован: 05.08.2014

А чего в первом примере значение intervalMs 40, а в этом уже 50? А если stateButt , prevMillis и др будут не в одном экземпляре, то можна 40 кнопок подключить?

 

nik182
Онлайн
Зарегистрирован: 04.05.2015

Для 40 кнопок достаточно stateButt размножить. А вот с дребезгом и двойным нажатием всёж не всё ясно. 

Pyotr
Offline
Зарегистрирован: 12.03.2014

Logik пишет:

А чего в первом примере значение intervalMs 40, а в этом уже 50? А если stateButt , prevMillis и др будут не в одном экземпляре, то можна 40 кнопок подключить?

 

При интервале опроса 40 мс сложнее двойной клик отловить. Нужно быстро кликать. Или stateButt объявлять в два байта.

Про 40 кнопок не думал. Наверное проще в матрицу объединить 5х8 и задействовать два порта.

Pyotr
Offline
Зарегистрирован: 12.03.2014

nik182 пишет:

Для 40 кнопок достаточно stateButt размножить. А вот с дребезгом и двойным нажатием всёж не всё ясно. 

Массив в 40 байт? Не думаю, что это оптимальный вариант.

За 40-50 мс дребезг "устаканивается".

Двойной клик хорошо отлавливается. Или у Вас не работает?

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Pyotr пишет:

andriano, троллите что-ли?

Это я троллю?

То есть размещение без всяких комментариев сомнительного кода - это не троллинг, а попытка выяснить, что же хотел сказать автор этого кода - троллинг?

Цитата:

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

И чем этот способ лучше традиционных?

Если бы код сразу без "пример как можно отслеживать" содержал признаки одиночного, двойного нажатия и удержания - тогда хотя бы понятно. Можно по крайней мере сравнивакть с творением Клапауция (которое, кстати, тоже вызывает множество вопросов), а так - вообще нет предмета для разговора. 

Так зачем был размещен данный код?

Что в нем есть такого, чего не было хотя бы в том же примере Клапауция?

Чем предложенная реализация лучше имеющихся?

nik182
Онлайн
Зарегистрирован: 04.05.2015

Меня всегда удивлял двойной клик на микропроцессорных игрушках. С одной стороны надо иметь быструю реакцию на кнопку, с другой стороны надо различить одинарный и двойной, значит надо ждать какое то время после первого нажатия, иначе начнёшь реагировать на одинарное, а тут двойное. В этом ключе мне кажется более логичным короткое, длинное и множественный повтор короткого при постоянном удержании кнопки. Покрайней мере этого достаточно ходить по меню и менять цифровые константы. Для этого нужно время нажатия помнить. У вас только 300 мс можно запомнить. Этого мало для длинного клика.  

Pyotr
Offline
Зарегистрирован: 12.03.2014

andriano пишет:

Чем предложенная реализация лучше имеющихся?

Тем, что это моя реализация. Не нравится, проходим мимо. Есть замечания по коду - велкам.

Коментарии типа "это битовый сдвиг" считаю неуместными.

Кто не понял как подключается кнопка. Только вместо D2 у меня D7. R1 не обязателен. Если длинные провода до кнопки или сильные помехи, тогда между D7 и +питания резистор 1-10 кОм.

Pyotr
Offline
Зарегистрирован: 12.03.2014

nik182 пишет:

Меня всегда удивлял двойной клик на микропроцессорных игрушках. С одной стороны надо иметь быструю реакцию на кнопку, с другой стороны надо различить одинарный и двойной, значит надо ждать какое то время после первого нажатия, иначе начнёшь реагировать на одинарное, а тут двойное. В этом ключе мне кажется более логичным короткое, длинное и множественный повтор короткого при постоянном удержании кнопки. Покрайней мере этого достаточно ходить по меню и менять цифровые константы. Для этого нужно время нажатия помнить. У вас только 300 мс можно запомнить. Этого мало для длинного клика.  

Все Ваши хотелки, плюс отлавливается двойной и тройной клик при быстрой реакции. 

#define NOT_PRESSED B11
#define CLICK_DOWN  B10

word stateButt = NOT_PRESSED;
word carrMillis, prevMillis, intervalMs = 40, howLongPress;
const byte pinButt = 7;
byte repeat = 0, retention;

void setup() { 
  Serial.begin(9600);  
  pinMode(pinButt, INPUT_PULLUP);
}

void loop() {
  carrMillis = millis();
  if(carrMillis - prevMillis >= intervalMs){
    prevMillis = carrMillis;
    stateButt <<= 1;
    
    if(digitalRead(pinButt)){//сейчас отпущена
      stateButt |= 1;
      retention = 0;
      howLongPress = 0;
      repeat = 0;
    }
            
    if((stateButt >> 14) == CLICK_DOWN){//была нажата 15*40=600 мс назад
      if(stateButt == 0x8000){//Всё это время не отпускалась
         retention = 1;
         Serial.println("RETENTION");     
      }else{     
         byte numClick = 1;
         for(byte i=0; i<14; i++){
           if(((stateButt >> i) & B11) == CLICK_DOWN){ 
             numClick++;
             //break;
           }
         }
         if(numClick == 1){
           if((stateButt << 1) > 0x7FE){
             Serial.println("SHOT_CLICK");   
           }else{
             Serial.println("LONG_CLICK");
           }
         }else{
           stateButt = 0;
           Serial.print("DOWBLE_CLICK=");
           Serial.println(numClick);
         }
       }     
     }
     if(retention){
       howLongPress++;
       if(howLongPress < 35) repeat = !(howLongPress%8);
       else                  repeat = !(howLongPress%3);
       if(repeat) Serial.println("REPEAT");       
    }
  }
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

nik182 пишет:

Меня всегда удивлял двойной клик на микропроцессорных игрушках. С одной стороны надо иметь быструю реакцию на кнопку, с другой стороны надо различить одинарный и двойной, значит надо ждать какое то время после первого нажатия, иначе начнёшь реагировать на одинарное, а тут двойное. 

Вопрос на самом деле гораздо глубже.

Любой проект должен сначала проходить через стадию проектирования. И именно на этой стадии должна быть принята непротиворечивая система сигналов. В частности, действия, "повешенные" на одинарный и двойной щелчок должны не противоречить, а, наоборот - дополнять друг друга. Т.е. при двойном щелчке одновременно выполняется и одиночный, и реакция на него должна быть такой, чтобы это не разрушало логику отклика системы на воздействие.

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Pyotr пишет:

Тем, что это моя реализация. Не нравится, проходим мимо. Есть замечания по коду - велкам.

Понятно. Вопросов больше нет.

Теперь замечания по коду:

1. Код бесполезен. По крайней мере, автор не сумел привести никаких аргументов его полезности (а то, что это его код, полезности ему никак не добавляет).

2. Код практически недокументирован. Неясно, что, почему и зачем.

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

4. Код невозможно использовать без переписывания. Например, если у меня кнопка не простая, а в матрице, то представленный код не подходит, вместо него надо писать другой. Никаких механизмов адаптации кода под конкретную задачу (то же самое ООП) в коде не предусмотрено.

Logik
Offline
Зарегистрирован: 05.08.2014

Pyotr пишет:

Logik пишет:

А чего в первом примере значение intervalMs 40, а в этом уже 50? А если stateButt , prevMillis и др будут не в одном экземпляре, то можна 40 кнопок подключить?

 

При интервале опроса 40 мс сложнее двойной клик отловить. Нужно быстро кликать. Или stateButt объявлять в два байта.

Про 40 кнопок не думал. Наверное проще в матрицу объединить 5х8 и задействовать два порта.

Разумеется в матрицу. И возможно даже сигналы с матрицы не напрямую получать, через расширение портов. Но это на данном уровне софта вобще не важно. Просто у матоицы номер нажатой/отпущеной кнопки немного по другому формируется, как кортеж строба и возврата, а без матрицы - привязка к пину. Вобщем не важно. Хуже другое. Кнопка одна и её номер вобще не предусмотрен. Из-за этого и сложности с масштабированием.