Кнопки,Прерывания.

frielender
Offline
Зарегистрирован: 26.11.2014

Возможно ли к одному прерыванию подключить несколько кнопок, например 5 штук. И в прерывании определять какая из них была нажата?

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

Можно. Для этого нужно использовать логический элемент ИЛИ, который при упрощении можно свести к кучке диодов. Сигнал с каждой кнопки идет на вывод ардуины и через диод на вывод прерывания. А в прерывании мы проверяем выводы и находим активные.

 

 

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

аналоговую кнопку сделайте. Каждой кнопке на ножке будет соответстоввать свое напряжение. АЦП снимаете уровень. Хватит 1 ножки арудины

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

Michal пишет:

аналоговую кнопку сделайте. Каждой кнопке на ножке будет соответстоввать свое напряжение. АЦП снимаете уровень. Хватит 1 ножки арудины

А прерывания куда?

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

NeiroN пишет:

Можно. Для этого нужно использовать логический элемент ИЛИ, который при упрощении можно свести к кучке диодов. Сигнал с каждой кнопки идет на вывод ардуины и через диод на вывод прерывания. А в прерывании мы проверяем выводы и находим активные.

Эту микросхему получится прикрутить?

74HC597

frielender
Offline
Зарегистрирован: 26.11.2014

1)Аналоговую клаву делал, только как к прерыванию ее "прикрепить"?

2)

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

на каждую кнопку по выходу=расточительство

 

 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

С дребезгом тригером Шмидта можно бороться.

frielender
Offline
Зарегистрирован: 26.11.2014

Все равно иногда бывает лишнее нажатие. Что я делаю не так)? Может как-то программно бороться можно?

Это просто пример, по которому я определяю был дребезг или нет.

byte intPin = 0;   
byte ledPin = 13;    
volatile int x = 0;
boolean LED=true;

void setup() 
{
  attachInterrupt(intPin,pause,RISING);.
  pinMode(ledPin, OUTPUT);
  pinMode(7, INPUT); 
  pinMode(9, INPUT); 
  Serial.begin(9600); 
}


void pause() 
{     
int i=0;
while(i <70) {i++; }            

     LED=!LED; // включаем/выключаем LED   
    if ((digitalRead(7)==LOW)and(digitalRead(9)==HIGH))
    {
    x=x-1;
    Serial.println(x);
    } else
    if ((digitalRead(7)==HIGH)and(digitalRead(9)==LOW))
    {
    x=x+1; 
    Serial.println(x); 
    }
    if ((digitalRead(7)==HIGH)and(digitalRead(9)==HIGH))
    {
    Serial.println(x);  
    }
    
 }

 void loop() 
{
  digitalWrite(ledPin,LED);
}

 

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

frielender, можно сделать прерывание по таймеру скажем раз в секунду, и в прерывании опрашивать кнопки. Тогда проблемы дребезга скорее всего не будет. Если с таймерами не очень дружите, то можно взять готовую библу типа timerOne.

frielender
Offline
Зарегистрирован: 26.11.2014

Спасибо за ответы, решил проблему с дребезгом. Но появилась другая проблемка...

Программа не выходит из прерывания(Это понятно  по светодиоду, который должен менять свое состояние в основной программе)

Я выяснил что это из-за части вывода меню на экран, если выводить что то в Serial, то все работает. В чем может быть проблема? И как мне выводить на экран через прерывание?

<div>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
byte intPin = 0;     // Номер прерывания, которое будет вызыватся.Контакт которому соответствует прерывание 2.
byte ledPin = 13;    // Светодиод
volatile int x = 0;  // volotile означает возможность внезапного, для основной программы, изменения переменной.
                     // В данном случае изменение произойдет в прерывании
volatile int y = 0;                     
volatile int A = 10;                      
boolean LED=true;    //Состояние светодиода
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() 
{
  attachInterrupt(intPin,pause,RISING); 
  pinMode(ledPin, OUTPUT);
  pinMode(7, INPUT); 
  pinMode(9, INPUT); 
  Serial.begin(9600); 
  lcd.begin();
  lcd.backlight();
  lcd.setCursor(4,0);
  lcd.print("WELCOME!");
       
}


void pause() 
{     
int i=0;
while(i <70) {i++; }            

    if ((digitalRead(7)==LOW)&&(digitalRead(9)==HIGH))// OK
    { if((A==10)||(A==20)) A++;
      else
      if((A==11)||(A==21)) A--;
      Serial.println(A); 
    }
    if ((digitalRead(7)==HIGH)&&(digitalRead(9)==LOW))// -
    {
      if(A==20) {A=10;}
      if(A==11){ x--;}
      if(A==12){ y--;}
      Serial.println(A); 
    }
    if ((digitalRead(7)==HIGH)&&(digitalRead(9)==HIGH))// +
    {
      if(A==10) {A=20;}
      if(A==11){ x++;}
      if(A==12){ y++;}
      Serial.println(A); 
    }
//////////////////////////////////////////////////////////////////////// Не выходит из прерывания из- за этой части, именно вывода на экран
if (A==10)
{ lcd.clear();
  lcd.print("Main Menu");
  lcd.setCursor(13,0);
  lcd.print(">1<");
  lcd.setCursor(0,1);
  lcd.print("Speed");

}
if(A==20)
{ lcd.clear();
  lcd.print("Main Menu");
  lcd.setCursor(13,0);
  lcd.print(">2<");
  lcd.setCursor(0,1);
  lcd.print("Animation");
}
if(A==11)
{ lcd.clear();
  lcd.print("Speed=");
  lcd.setCursor(7,0);
  lcd.print(x);
  lcd.setCursor(4,1);
  lcd.print("-  OK  +"); 
}
if(A==11)
{ lcd.clear();
  lcd.print("Animation#");
  lcd.setCursor(11,0);
  lcd.print(y);
  lcd.setCursor(4,1);
  lcd.print("-  OK  +");  
}

////////////////////////////////////////////////////////////////////////
}

void loop()
{
digitalWrite(ledPin,HIGH);
delay(1000);
digitalWrite(ledPin,LOW);
delay(1000);

}


</div>
 
maksim
Offline
Зарегистрирован: 12.02.2012

frielender пишет:

Возможно ли к одному прерыванию подключить несколько кнопок, например 5 штук. И в прерывании определять какая из них была нажата?

PCINT

frielender пишет:

Может как-то программно бороться можно?

Не только можно, а нужно.

frielender пишет:

И как мне выводить на экран через прерывание?

bool change = 0;  // флаг изменения
char dispmem[2][17] = {"                ","                "}; // то что выводим на дисплей

void setup() 
{
  //....
  attachInterrupt(0, Interrupt, CHANGE);
  //....
}

void loop() 
{
  //....
  Display();
  //....
}

void Interrupt() 
{
  //....
  strcpy(&dispmem[1][4], "WELCOME!"); 
  change = 1;  // взводим флаг
}

void Display() 
{
  if(change == 1) // Если есть изменения выводим на дисплей
  {
    lcd.setCursor(0,0);
    lcd.print(dispmem[0]);
    lcd.setCursor(0,1);
    lcd.print(dispmem[1]);
    change = 0;  // сбрасываем флаг
  }
}

 

frielender
Offline
Зарегистрирован: 26.11.2014

Получается,если у меня в loop  программа выполняется 20 сек, то мне придется ждать эти 20 секунд перед обновлением информации выводимой на экран?

maksim
Offline
Зарегистрирован: 12.02.2012

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

frielender
Offline
Зарегистрирован: 26.11.2014

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

bwn
Offline
Зарегистрирован: 25.08.2014

frielender пишет:

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

А это уже зависит от алгоритма, который вы накодите. Здесь, каждый сам кузнец своего счастья.

frielender
Offline
Зарегистрирован: 26.11.2014

Накодил) только не работает вывод на экран через прерывание.

bwn
Offline
Зарегистрирован: 25.08.2014

frielender пишет:

Накодил) только не работает вывод на экран через прерывание.

А вывод на экран через прерывание, это знаете ли моветонс. В прерываниях делаются минимально необходимые операции, а уже обсчет, экран и прочие действия в loop()/

bwn
Offline
Зарегистрирован: 25.08.2014

В целом это у вас и сделано, только если loop()20 секунд, то как выведет дисплей?

Ошибся, сделано не у вас, у maksim.

Делать по этому же принципу, только дисплей должен выводить информацию регулярно с минимально допустимым интервалом.

bwn
Offline
Зарегистрирован: 25.08.2014

и еще, вы упомянули "меню". Конкретизируйте, на каком этапе для вас становится критичной частота вывода на экран?

frielender
Offline
Зарегистрирован: 26.11.2014

Как вообще делается меню на устройстве, мб я координально что то делаю не так?

Сейчас вот так, но если программа в loop() будет длинной, и долгой в исполнении , то общение с экраном будет через большие промежутки времени,что не есть хорошо.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
byte intPin = 0;     
byte ledPin = 13;    
volatile int x = 0; 
volatile int y = 0;                     
volatile int A = 10;                      
boolean LED=true;    
boolean klick = 0; 
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() 
{
  attachInterrupt(intPin,pause,RISING); 
  pinMode(ledPin, OUTPUT);
  pinMode(7, INPUT); 
  pinMode(9, INPUT); 
  Serial.begin(9600); 
  // initialize the LCD
  lcd.begin();
  lcd.backlight();
  lcd.setCursor(4,0);
  lcd.print("WELCOME!");
       
}


void pause() 
{     
int i=0;
while(i <70) {i++; }            
////////////////////////////////////////////////////////////////////////
    if ((digitalRead(7)==LOW)&&(digitalRead(9)==HIGH))// OK
    { if((A==10)||(A==20)) A++;
      else
      if((A==11)||(A==21)) A--;
      Serial.println(A); 
    }
    if ((digitalRead(7)==HIGH)&&(digitalRead(9)==LOW))// -
    {
      if(A==20) {A=10;}
      if(A==11){ x--;}
      if(A==21){ y--;}
      Serial.println(A); 
    }
    if ((digitalRead(7)==HIGH)&&(digitalRead(9)==HIGH))// +
    {
      if(A==10) {A=20;}
      if(A==11){ x++;}
      if(A==21){ y++;}
      Serial.println(A); 
    }
    klick = 1; 

}

void button(){
if(klick == 1) 
{  
 if (A==10)
 { lcd.clear();
   lcd.print("Main Menu");
   lcd.setCursor(13,0);
   lcd.print(">1<");
   lcd.setCursor(0,1);
   lcd.print("Speed");  

 }
 if(A==20)
 { lcd.clear();
   lcd.print("Main Menu");
   lcd.setCursor(13,0);
   lcd.print(">2<");
   lcd.setCursor(0,1);
   lcd.print("Animation");
 }
 if(A==11)
 { lcd.clear();
   lcd.print("Speed=");
   lcd.setCursor(7,0);
   lcd.print(x);
   lcd.setCursor(4,1);
   lcd.print("-  OK  +"); 
 }
 if(A==21)
 { lcd.clear();
   lcd.print("Animation#");
   lcd.setCursor(11,0);
   lcd.print(y);
   lcd.setCursor(4,1);
   lcd.print("-  OK  +");
 }
 klick = 0;  
 }
}
void loop() 
{
button();

}

 

bwn
Offline
Зарегистрирован: 25.08.2014

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

По уму, вам надо в прерывании определять сработку кнопки, а все остальное вне его.

frielender
Offline
Зарегистрирован: 26.11.2014

Да , я могу наделать ужасных вещей) только начал разбираться в этом.

Да вроде нету у меня цикла в прерывании...

Так тоже не правильно?

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
byte intPin = 0;     // Номер прерывания, которое будет вызыватся.Контакт которому соответствует прерывание 2.
byte ledPin = 13;    // Светодиод
volatile int x = 0;  // volotile означает возможность внезапного, для основной программы, изменения переменной.
                     // В данном случае изменение произойдет в прерывании
volatile int y = 0;                     
volatile int A = 10;   
volatile int B = 0; 
boolean klick = 0; 
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Основные параметры платы.
void setup() 
{
  attachInterrupt(intPin,pause,RISING); // Параметры прерывания.
  pinMode(ledPin, OUTPUT);
  pinMode(7, INPUT); 
  pinMode(9, INPUT); 
  Serial.begin(9600); 
  // initialize the LCD
  lcd.begin();
  lcd.backlight();
  lcd.setCursor(4,0);
  lcd.print("WELCOME!");
       
}

// Функция которая будет выполнятся при нажатии на кнопку.
void pause() 
{  
    //Определеине какая кнопка была нажата
    if ((digitalRead(7)==HIGH)&&(digitalRead(9)==LOW)) B=1;// -
    if ((digitalRead(7)==LOW)&&(digitalRead(9)==HIGH)) B=2;// OK
    if ((digitalRead(7)==HIGH)&&(digitalRead(9)==HIGH))B=3;// +
    klick = 1; 

}
// Основная программа.
void button(){
  
if(klick == 1) 
{  
 // Определения в каком месте меню нахожусь
 if (B==1)
 {
   if(A==20) {A=10;}
   if(A==11){ x--;}
   if(A==21){ y--;}
 } 
 if (B==2)
 { if((A==10)||(A==20)) A++;
   else
   if((A==11)||(A==21)) A--;
 }
 if (B==3)
 {
   if(A==10) {A=20;}
   if(A==11){ x++;}
   if(A==21){ y++;}
 }
//Вывод на дисплей пункт меню, в котором нахожусь 
 if (A==10)
 { lcd.clear();
   lcd.print("Main Menu");
   lcd.setCursor(13,0);
   lcd.print(">1<");
   lcd.setCursor(0,1);
   lcd.print("Speed");  

 }
 if(A==20)
 { lcd.clear();
   lcd.print("Main Menu");
   lcd.setCursor(13,0);
   lcd.print(">2<");
   lcd.setCursor(0,1);
   lcd.print("Animation");
 }
 if(A==11)
 { lcd.clear();
   lcd.print("Speed=");
   lcd.setCursor(7,0);
   lcd.print(x);
   lcd.setCursor(4,1);
   lcd.print("-  OK  +"); 
 }
 if(A==21)
 { lcd.clear();
   lcd.print("Animation#");
   lcd.setCursor(11,0);
   lcd.print(y);
   lcd.setCursor(4,1);
   lcd.print("-  OK  +");
 }
 klick = 0;  
 }
}
void loop() 
{
button();
//...
}

Для наглядности, по такому принципу организовано перемещение по меню.

bwn
Offline
Зарегистрирован: 25.08.2014

А что у вас делает строка 33? 033    int i=0; while(i <70) {i++; }

В целом лучше, но if-ы тоже не есть хорошо.

И еще, вы с кнопками на прерываниях заморочились для тренировки или есть некий сакральный смысл?

 

frielender
Offline
Зарегистрирован: 26.11.2014

*33 строчка, это я нашел на этом форуме, так советовали с дребезгом бороться, но так как у меня уже стоит тригер шмитта, это уже не нужно.

*А как же без ифов определить какая из кнопок нажата?

*И как возможно без прерывания? Если у меня в loop( ) будут выполняться какие-то действия, то нажатия на кнопки контроллер не "увидит".

bwn
Offline
Зарегистрирован: 25.08.2014

frielender пишет:

*33 строчка, это я нашел на этом форуме, так советовали с дребезгом бороться, но так как у меня уже стоит тригер шмитта, это уже не нужно.

*А как же без ифов определить какая из кнопок нажата?

*А как возможно без прерывания? Если у меня в loop( ) будут выполняться какие-то действия, то нажатия на кнопки контроллер не "увидит".

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

bwn
Offline
Зарегистрирован: 25.08.2014

Сейчас по вашей логике получается, что при каждом проходе loop() вы запрыгиваете в меню. Зачем? Меню это отдельная функция и должна вызыватся по какому то действию (пара кнопок нажато, длинное нажатие и т.д.). Если кнопки будут использоватся только для меню, то и исходите из этого. loop() отдельно, меню отдельно. Когда работаете с меню, loop() все равно стоит. Многозадачность это конечно круто, но вряд ли реализуемо.