Одиночное и длительное нажатие энкодера

blidge
Offline
Зарегистрирован: 27.10.2013

Всем привет. Подключаю оптический энкодер с нажатием и никак не могу решить одну задачку.

Мне нужно реализовать одиночное нажатие и длительное нажатие кнопки через 5 секунд.

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

Если удерживать нажатой кнопку больше 5-ти секунд, тогда срабатывает длительное нажатие, но после отпускания кнопки ещё раз выполняется одиночное.

Так вот, впорос... Как мне убрать последнее одиночное повторение?

Спасибо.



int val=0;
int flag=0;
long previousMillis = 0;

#define encoder0PinC  4

void setup() 
{ 
  pinMode(13, OUTPUT); //Светодиод
  pinMode(encoder0PinC, INPUT);           // нажатие энкодера
  digitalWrite(encoder0PinC, HIGH);       // подключить подтягивающий резистор
  Serial.begin(115200); 
}
 
void loop() 
{
// Нажатие кнопки энкодера
     if(digitalRead(encoder0PinC)==LOW&&flag==0) //если кнопка нажата ...
     { flag=1; } 
       
      if(digitalRead(encoder0PinC)==HIGH&&flag==1)//если кнопка НЕ нажата 
     { 
       digitalWrite(13,!digitalRead(13)); 
       delay(500);
       digitalWrite(13,LOW);//выключаем
       flag=0;//обнуляем переменную flag 
     } 
// End // Нажатие кнопки энкодера

// Удержание кнопки энкодера
  if(digitalRead(encoder0PinC)==LOW) //если кнопка нажата ... 
      { 
        if (millis() -previousMillis >500)    
       { 
        previousMillis = millis();     
        val++; 
       }  
      } 
      else 
      { val=0; } 
      
      if(val>=5) 
     { 
       digitalWrite(13,!digitalRead(13));//инвертируем состояние пина 
       delay(500);
       digitalWrite(13,LOW);//выключаем
       val=0; 
     } 
// End // Удержание кнопки энкодера
}

 

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

поищите на форуме темку про реализацию меню однокнопочного

vvadim
Offline
Зарегистрирован: 23.05.2012
blidge
Offline
Зарегистрирован: 27.10.2013

Спасибо за ссылку - очень помогло.

deliter
Offline
Зарегистрирован: 18.06.2014

А результаты есть? А то по ссылке сейчас код как-то криво работает..

X-Dron
Offline
Зарегистрирован: 24.01.2015

можете попробовать так:
 

#include <DI.h>
#include <Timer_P.h>

#define Button    3
DI Key(Button, 10);
Timer_P KeyDelay;
boolean Key_old;

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

void loop()
{
  Key.DI_Refresh();
  if (!Key.DI_Read() && Key_old && !KeyDelay.Q0()) Serial.println("Shot"); // Было короткое нажатие
  if (!Key.DI_Read() && Key_old && KeyDelay.Q0()) Serial.println("Long"); // Было длинное нажатие
  KeyDelay.TimerV(Key.DI_Read(), 0, 2, 1000);                  // 1000мс для длительного нажатия
  Key_old = Key.DI_Read();
}

пример использования, ссылки на библиотеки. Здесьhttp://forum.amperka.ru/threads/%D0%BA%D0%BE%D0%B4%D0%BE%D0%B2%D1%8B%D0%B9-%D0%B7%D0%B0%D0%BC%D0%BE%D0%BA.4758/#post-37763
Если подключаете со стягивающим резюком, или используется оптопара, то можно сделать так

#include <DI.h>
#include <Timer_P.h>

#define Button    3
DI Key(Button, 10);
Timer_P KeyDelay;
boolean Key_old;

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

void loop()
{
  Key.DI_Refresh();
  boolean _Key = !Key.DI_Read();
  if (!_Key && Key_old && !KeyDelay.Q0()) Serial.println("Shot"); // Было короткое нажатие
  if (!_Key && Key_old && KeyDelay.Q0()) Serial.println("Long"); // Было длинное нажатие
  KeyDelay.TimerV(_Key, 0, 2, 1000);                  // 1000мс для длительного нажатия
  Key_old = _Key;
}

 

deliter
Offline
Зарегистрирован: 18.06.2014

Привет! Спасибо большое за код! 

Подскажи, пожалуйста, что может быть не так:

у меня кнопка работает "наоборот", тоесть на размыкание а не на замыкание...

хотя я сделал подтяжку к нулю кнопки..

Спасибо!

deliter
Offline
Зарегистрирован: 18.06.2014

Может у меня на Uno какая-то стандартная подтяжка к плюсу... хз

X-Dron
Offline
Зарегистрирован: 24.01.2015

Класс DI предполагает, что кнопка подключена с использованием встроенного подтягивающего резистора (режим INPUT_PULLUP). Т.е. при замыкании на ходе 0, при размыкании 1. Не хочу использовать лишние провода и элементы. Ссылка на то, как должна быть подключена кнопка дана посте. Метод .DI_Refresh() нормализуе сигнал INPUT_PULLUP, т.е. при нажатии будет 1, при отпускании 0.
Если, все-таки хитите со стягивающим резюком с подтяжкой к нулю, то второй предложенный код, все-таки не работает.
но работает этот, проверено:


#include <DI.h>
#include <Timer_P.h>
 
#define Button    3
DI Key(Button, 10);
Timer_P KeyDelay;
boolean Key_old;
 
void setup()
{
  Serial.begin(9600);
  pinMode(Button, INPUT);
}
 
void loop()
{
  Key.DI_Refresh();
  if (!Key.DI_Read() && Key_old && !KeyDelay.Q0()) Serial.println("Shot"); // Было короткое нажатие
  if (!Key.DI_Read() && Key_old && KeyDelay.Q0()) Serial.println("Long"); // Было длинное нажатие
  KeyDelay.TimerV(Key.DI_Read(), 0, 2, 1000);                  // 1000мс для длительного нажатия
  Key_old = Key.DI_Read();
}

разница с первым только в pinMode(Button, INPUT); в сетапе.

deliter
Offline
Зарегистрирован: 18.06.2014

X-Dron пишет:

первым только в pinMode(Button, INPUT); в сетапе.

 

Спасибо большое! Пошел пробовать!

+ тебе в карму.

edcop
Offline
Зарегистрирован: 23.07.2016

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

vrd
Offline
Зарегистрирован: 20.01.2022

Это у вас дребезг и лечится он задержкой.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

edcop пишет:

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

Нужно отслеживать не состояние кнопки, а изменение состояния с не нажатого на нажатое. И только в момент изменения состояния менять нужный параметр

vrd
Offline
Зарегистрирован: 20.01.2022
int knopka (int x) {

int y;

if (x == 1 & y == 0) {

y = 1;

return knopka = 1;

}

if (x == 0) {

y = 0;

return knopka = 0;

}
return knopka = 0;
}

Гдето так.

X - состояние кнопки. Вызываем при нажатии. Полученное значение плюсуем к необходимой переменной.

Вроде не накосячил в коде, но перепроверте.

edcop
Offline
Зарегистрирован: 23.07.2016

Мда... Это специально выбраны переменные x и у? У меня и так условие сработки тача будет

if (tft.getTouch(&x, &y)) {       
     if ((x > 240) && (x < 280)) {   
     if ((y >124) && (y < 154)){
       // действие при нажатии
       }
      } 
    }
 
если я правильно понимаю, то с другими переменными будет так
 
     if ((x > 240) && (x < 280)) {   
     if ((y >124) && (y < 154)){
         currBt = HIGH;                 //  вместо х = 1
          if (currBt != lastBt)          // вместо if (x == 1 & y == 0)
           lastBt = HIGH;               // вместо y = 1;
       }
       else {
       currBt = LOW;
       lastBt = LOW;                    // вместо y = 0;
      }
     }
    }

и потом

if (lastBt == HIGH){

                           // изменяем нужный параметр

}

Но параметр проматывается до конца ((

Мне кажется, что нужно отслеживать состояние из нажатого в ненажатое и только тогда менять параметр.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

edcop пишет:

Мне кажется, что нужно отслеживать состояние из нажатого в ненажатое и только тогда менять параметр.

Для кого это я писал?

v258 пишет:

edcop пишет:

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

Нужно отслеживать не состояние кнопки, а изменение состояния с не нажатого на нажатое. И только в момент изменения состояния менять нужный параметр

edcop
Offline
Зарегистрирован: 23.07.2016

Я написал про другое изменение состояния. Возможно это и не важно, но к сожалению я не стал ближе к пониманию, как решить проблему.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

У кнопки только два состояния - нажатое и не нажатое. Про какое другое состояние можно вести речь?

edcop пишет:

но к сожалению я не стал ближе к пониманию, как решить проблему.

Вот, собственно, непонимание этого и ведет к отсутствию понимания в целом ))

edcop
Offline
Зарегистрирован: 23.07.2016

Вот только дурачка не нужно из меня делать. Я написал дословно "Я написал про другое изменение состояния", а не про другое состояние. Речь про передний и задний фронты сигнала. Так понятнее?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Для начала научись изъясняться внятно. Это во-первых. Во-вторых, не имеет значение какой фронт. Нужно учитывать любое изменение состояния. Изменение с не нажатой на нажатую - для отработки чего там у тебя задумано, изменение с нажатой на не нажатую - чтобы сработало при следующем нажатии. А у тебя в #14 хрень какая-то написана

vrd
Offline
Зарегистрирован: 20.01.2022

Каждые 100 или 200 милис опрашиваем состояние кнопки. Так избавляемся от дребезга.

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

Икс замените на "in" например. Это тоже внутренняя переменная.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

У него нет дребезга. У него кнопка - это прямоугольник на сенсорном экране

vrd
Offline
Зарегистрирован: 20.01.2022

v258 пишет:

У него нет дребезга. У него кнопка - это прямоугольник на сенсорном экране

Эту вспышку я пропустил.

Без полного кода всё равно непонятно чего надобно.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

edcop пишет:

если я правильно понимаю, то с другими переменными будет так
 
     if ((x > 240) && (x < 280)) {   
     if ((y >124) && (y < 154)){
         currBt = HIGH;                 //  вместо х = 1
          if (currBt != lastBt)          // вместо if (x == 1 & y == 0)
           lastBt = HIGH;               // вместо y = 1;
       }
       else {
       currBt = LOW;
       lastBt = LOW;                    // вместо y = 0;
      }
     }
    }

и потом

if (lastBt == HIGH){

                           // изменяем нужный параметр

}

Но параметр проматывается до конца ((

Мне кажется, что нужно отслеживать состояние из нажатого в ненажатое и только тогда менять параметр.

Параметр нужно изменять В МОМЕНТ смены состояния кнопки, а не потом. 

if ((x > 240) && (x < 280))
{
  if ((y > 124) && (y < 154))
  {
    currBt = HIGH;        //  вместо х = 1
    if (currBt != lastBt) // вместо if (x == 1 & y == 0)
    {
      lastBt = HIGH; // вместо y = 1;
                     // =============  вот здесь нужно изменять параметр
    }
  }
  else
  {
    currBt = LOW;
    lastBt = LOW; // вместо y = 0;
  }
}

Выделил, где это должно делаться

vrd
Offline
Зарегистрирован: 20.01.2022

Между 10 и 11 ещё одну скобку. На 17 убрать?

Или 11 сместить после 16?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Код поправлен, все скобки на месте

vrd
Offline
Зарегистрирован: 20.01.2022

11 перекинуть на 16. Иначе логика теряется. Элсе идёт к игреку!

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Да там вообще оба условия нужно объединить

if (((x > 240) && (x < 280)) && ((y > 124) && (y < 154)))
{
  currBt = HIGH;        //  вместо х = 1
  if (currBt != lastBt) // вместо if (x == 1 & y == 0)
  {
    lastBt = HIGH; // вместо y = 1;
                   // =============  вот здесь нужно изменять параметр
  }
}
else
{
  currBt = LOW;
  lastBt = LOW; // вместо y = 0;
}

 

vrd
Offline
Зарегистрирован: 20.01.2022

И элсе идёт к первому ифу:(

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Туда и должно. Если клик идет в пределах прямоугольника, то кнопка нажата, иначе отжата

b707
Offline
Зарегистрирован: 26.05.2017

vrd пишет:

int knopka (int x) {

int y;

if (x == 1 & y == 0) {

y = 1;

return knopka = 1;

}

if (x == 0) {

y = 0;

return knopka = 0;

}
return knopka = 0;
}

Гдето так.

 

это что за бред?

Цитата:
Вроде не накосячил в коде, но перепроверте.

весь код один сплошной косяк.

 

b707
Offline
Зарегистрирован: 26.05.2017

vrd - судя по вашему коду и замечаниям, языка вы не знаете, так что лучше не лезьте с советами, v258 отлично без вас справится

 

vrd
Offline
Зарегистрирован: 20.01.2022

b707 пишет:

vrd - судя по вашему коду и замечаниям, языка вы не знаете, так что лучше не лезьте с советами, v258 отлично без вас справится

 

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

b707
Offline
Зарегистрирован: 26.05.2017

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

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

b707 пишет:

 нафига вы полезли помогать, если сами ничего не знаете.

Так это ж основная фишка форума.

Кто знает - не помогает, помогает кто не знает )))

lilik
Offline
Зарегистрирован: 19.10.2017

Logik пишет:

Так это ж основная фишка форума.

Кто знает - не помогает, помогает кто не знает )))

:-)

Итить, переитить! Так и есть.

Кто может, не видит смысла, а кто не может - имеет только смысл.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

lilik пишет:

:-)

Итить, переитить! Так и есть.

Кто может, не видит смысла, а кто не может - имеет только смысл.

как назвать человека который может но не хочет... сволочь ))) (из анекдота)

edcop
Offline
Зарегистрирован: 23.07.2016

Никак не могу победить одиночное нажатие на кнопке сенсорного дисплея. Немного опишу, что должно происходить при нажатии.

Есть 4 экрана (меню), которые отличаются только наличием доп записей, основные данные в т.ч. кнопка листания присутствуют на всех меню. И находится эта кнопка в одном месте на всех четырёх экранах (меню).

Нажатие кнопки должно отработать menu++; но при этом долгое нажатие продолжает отрабатывать переход в следующее меню, а должно было остановиться.

Поскольку переход между меню осуществляется не только по нажатию кнопки, но и по условиям датчиков, поэтому я создал флаг перехода и условие перехода добавил в "или" (данные с датчиков или флаг кнопки). Пробовал и без флага menu++;

Каждый этап я расписал через Switch/Case, там отрабатываются другие задачи, а нажатие этой кнопки в общем цикле

Видимо проблема одинакового расположения кнопки во всех 4-х меню (экранах) и бороться нужно, как с дребезгом

Возможно нужно возвращать flag7 или currBt в LOW в начале каждого case?

Какие ещё будут мысли? Заранее благодарен за подсказки

Кусок кода по нажатию кнопки

if (tft.getTouch(&x, &y)) {     
  if ((x > 220) && (x < 300)) {   // Нажатие на "-->" переход к следующему этапу
      if ((y >170) && (y < 220)){
         currBt = HIGH;
         flag6 = LOW;                // Отрисовываем заливки
          if (currBt != lastBt){
           lastBt = HIGH;
           flag7 = HIGH;
       }
       else {
       currBt = LOW;
       lastBt = LOW;
       }
      }
     }
    }