обработка нажатия кнопки при включении питания arduino

zz-vop
Offline
Зарегистрирован: 05.08.2012

 Здравствуйте.

Комплектация следующая: arduino nano, кнопка и Led (подключено все по стандартным схемам с примеров ардуинных).

Задача следующая (упращенная): если при включении ардуины кнопка была нажата - Led мигает с одной частотатой, если при включении ардуинки кнопка не нажата - Led мигает с совершенно другой частотой. 

Но получается так что даже если при включении кнопка нажата, то ардуина обрабатывает ее как "кнопка не нажата". 

код:

const int buttonPin = 2;     // the number of the pushbutton pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
int ledPin = 9;    // LED connected to digital pin 9
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
int dCount;
int fadeValue = 255;


void setup()  { 
 pinMode(buttonPin, INPUT); // nothing happens in setup 
    
} 

void loop()  { 
 if (debounce() == 1) //если нажата
{leds(1) ;
leds(1); 
leds(1); 
leds(1); 
}
else {leds(5); //если не нажата
leds(5);
leds(5);
leds(5);
}
}
int debounce() {  
  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button 
  // (i.e. the input went from LOW to HIGH),  and you've waited 
  // long enough since the last press to ignore any noise:  

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonState = reading;
  }
  
  // set the LED using the state of the button:
  digitalWrite(ledPin, buttonState);

  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState = reading;
  return buttonState;
}
void leds(int d) {
pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW);
      delay(d*50);
     digitalWrite(ledPin, HIGH);
     delay(d*50);
      digitalWrite(ledPin, LOW);
      delay(d*50);
       digitalWrite(ledPin, HIGH);
     delay(d*50);
}

Подскажите как можно решить эту проблему? 

С уважением,
zz-vop

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

Вам нужно функцию debounce() переместить из loop (17 строка) в setup, предварительно объявив глобальную переменную.
объявляете переменную:

boolean button = 0;

в setup() присваиваем этой переменной состояние кнопки:

button = debounce();

в loop() меняем 17 строку вот так:

if (button == 1) //если нажата

 

Или при объявлении переменной сразу присвоить состояние кнопки:

boolean button = debounce();

 

zz-vop
Offline
Зарегистрирован: 05.08.2012

Сделал, но результат такой же.

 

Для эксперимента я объявил boolean button = 1, потом включил с нажатой кнопкой (по идее должно было сработать), но цикл пошел по "кнопка не нажата". 

 

 

zz-vop
Offline
Зарегистрирован: 05.08.2012

Спасибо. я вообще избавился от функции debounce(), а напрямую digitalRead(buttonPin) использовал и все как надо стало.

 

leshak
Offline
Зарегистрирован: 29.09.2011

 1. debounce, в вашем случае, не имеет смысла так как вы обильно вызываете delay() внутри loop. Смысл проверять прошло ли 50 миллисекунд с прошлого нажатия если саму проверку мы делаем не чаще чем в 200 msec? Конечно прошло. Само использование delay() уже является одним из способов debounce. Два основных подхода "програмнного антидребезга возможны" - или опрашивать кнопку не слишком часто, или опрашивать "часто как только возможно", но следить за тем сколько времени прошло с прошлого нажатия.

2. При включении debounce тем более не имеет смысла. Про какой lastButtonState может идти речь если мы только включились?

Вообщем debouce у нас тут "дважды не нужен". Нужно тупой digitalRead в setup()

Далее. Зачем эти портянки-повторения вызвовов leds(...)? Любую портянку можно заменить на цикл. for(...). Но если вспомнить что loop() это уже цикл, то и этого не нужно. Достаточно, в loop(), один раз вызывать leds с нужным параметром. 

И input можно не включать кнопку. По умолчанию они все на Input. Хотя чистой ошибкой это не является. Для того что-бы легче было читать код можно и явно это указать. Так что это скорее "вопрос  стиля", чем "правильно/не правильно".

Далле ledPin на выход достаточно включить один раз, в setup(). И сами пины лучше объявлять через #define, а не константы

Итого: в setup посмотрели на кнопку и решили с какой частотой мигать будем, в loop - мигаем. все.

#define buttonPin 2   // the number of the pushbutton pin
#define ledPin 9 //

byte ledPeriod; 
void setup()  { 
  pinMode(ledPin, OUTPUT);
 ledPeriod=digitalRead(buttonPin)?50:250; // 50 если нажата, 250 если не нажата
    
} 
void loop(){
  digitalWrite(ledPin,!digitalRead(ledPin)); // переключаем led на противоположное состояние  
  delay(ledPeriod);
}

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

leshak
Offline
Зарегистрирован: 29.09.2011

zz-vop пишет:

Спасибо. я вообще избавился от функции debounce(), а напрямую digitalRead(buttonPin) использовал и все как надо стало.

 

Не успел :) У вас вышло короче чем у меня?

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

 

ledPeriod=digitalRead(buttonPin)?1:5; // 1 если нажата, 5 если не нажата

а это что-то для меня новенькое.... можно поподробнее?

я так понял аналогично:

if(digitalRead(buttonPin)){ledPeriod=1;}else{ledPeriod=5;}

 

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

 

ledPeriod=digitalRead(buttonPin)?1:5; // 1 если нажата, 5 если не нажата

а это что для меня новенькое.... можно поподробнее?

сокращенный синтаксис для if. Грубо говоря можно напискать такую гипотетическую функцию 

ЛЮБОЙ_ТИП ifff(bool cond,ЛЮБОЙ_ТИП valueA,ЛЮБОЙ_ТИП valueB){
  if(cond)return valueA;
  else return valueB;
}

И вызывать ее

ledPeriod=ifff(digitalRead(bottonPin),1,5);

так понятно что происходит? Ну а так как написать такую функцию не просто. Из-за того что трудно объяснить компилятору что такое ЛЮБОЙ_ТИП, и не хочется делать писать такую для кажого типа вроде ifffInt, ifffByte и т.п., а нужда в этом - довольно часта, то эту функцию встроили в компилятор. Добавили "синтансический сахар". Ну и придумали, что-бы отличать от обычных функций особый сопрособ вызова.

УСЛОВИЕ?ЗНАЧЕНИЕ1:ЗНАЧЕНИЕ2

Эта конструкция вернет ЗНАЧЕНИ1 если условие истинно и ЗНАЧЕНИЕ2 если нет.

 

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

я так понял аналогично:

if(digitalRead(buttonPin)){ledPeriod=1;}else{ledPeriod=5;}

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

period= (digitalRead(buttonPin)?1:5) * (digitalRead(buttonPin2)?4:10)

Тогда как? Перебирать 4-ре варинта? Четыре if-а делать? Или два. Но прийдется водить дополнительную переменную. 

P.S. Но компилятор, в любом случае, естествнно развернет это в обычные if-ы. Это просто "что-бы код легче читался".

leshak
Offline
Зарегистрирован: 29.09.2011

 Ладно. Перепишим так что-бы не смущать сокращенным if-ом. Но и полный if-elese я не хочу использовать.  Установим значением переменной под дефолту, и будем метья его сли нопка нажата

#define buttonPin 2   // the number of the pushbutton pin
#define ledPin 9 //

byte ledPeriod=250; // если не нажата
void setup()  { 
  pinMode(ledPin, OUTPUT);
  if(digitalRead(buttonPin))ledPeriod=50; // если нажата
} 
void loop(){
  digitalWrite(ledPin,!digitalRead(ledPin)); // переключаем led на противоположное состояние  
  delay(ledPeriod);
}

 P.S. Хотя, конечно, в споровождении такой код намного хуже. Значения "разбросанны по скетчу". При изменениях нужно два места найти и поменять их "синхронно".

step962
Offline
Зарегистрирован: 23.05.2011

leshak пишет:

[...]

Из-за того что трудно объяснить компилятору что такое ЛЮБОЙ_ТИП, и не хочется делать писать такую для кажого типа

[...]

Ну, если начальник отдела подношений не берет, то ведь есть же еще и референты ;)

Пишем определение

#define IFF(res,expr,vara,varb) if(expr) res=vara; else res=varb;

и готово. Можно использовать любые типы (естественно, удовлетворяющие синтаксису). Препроцессор сделает все за нас:

  float v;
  char *sv;
  Serial.begin(9600);
  pinMode(2, INPUT);
  IFF(v,1>2,1,2.34)
  Serial.println(v, DEC);
  IFF(v,1<2,1,2.34)
  Serial.println(v);
  IFF(sv,1<2,"1<2","qwerty")
  Serial.println(sv);
  IFF(v,1<2,sin(0.5),millis())
  Serial.println(v);
  IFF(v,1>2,sin(0.5),millis())
  Serial.println(v);

 

zz-vop
Offline
Зарегистрирован: 05.08.2012

Спасибо. Много нового узнал. 

leshak
Offline
Зарегистрирован: 29.09.2011

step962 пишет:

Пишем определение

Ну я же сказал "трудно", а не "не возможно". Я же давал на примере функции, а не деректив препроцессора (так еще их объяснять нужно). Можно еще и через шаблоны сделать.

leshak
Offline
Зарегистрирован: 29.09.2011

 К тому же через #define у вас получилось только "присваивать переменной".

И лабуду типа

period= (digitalRead(buttonPin)?1:5) * (digitalRead(buttonPin2)?4:10)

вашим дейфаном уже не напишешь. Он может только "присвоить в зависимости от условия", но не "вернуть значение".

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

Сейчас проверить не начем, а так заработает?

period= (digitalRead(buttonPin)?12:(digitalRead(buttonPin1)?32:(digitalRead(buttonPin3)?4:0));

 

leshak
Offline
Зарегистрирован: 29.09.2011

leshak пишет:

Можно еще и через шаблоны сделать.

Только через шаблоны (что-бы действительно получить функцию) нужно будет ее определение выносить в отдельный .h файл. Из-за глюков IDE который в скетче не понимает такой синтаксис C++.

Так что, лучше оставить эту функцию как "гипотетическую", просто для объяснения идеи сокращенного синтаксиса.

Кстати так же самая проблема с использованием enum или struct в качестве параметров функций. "Прямо в скетче" - не работает, а вот если вынести их определение в отдельный файл - нет проблем. И в скетче будет работать.

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

Сейчас проверить не начем, а так заработает?

period= (digitalRead(buttonPin)?12:(digitalRead(buttonPin1)?32:(digitalRead(buttonPin3)?4:0));

 

Нет. Открывающих круглых скобок 6-ть штук, закрывающих -5ть. Не скомпилится. Где-то накосячили. 

Но "как идея" - сработает. Раз "это функция", то ничто не мешает использовать саму себя в качестве аргументов.

 

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

 

period= (digitalRead(buttonPin))?12:(digitalRead(buttonPin1)?32:(digitalRead(buttonPin3)?4:0));

 

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

 

period= (digitalRead(buttonPin))?12:(digitalRead(buttonPin1)?32:(digitalRead(buttonPin3)?4:0));

 

Я же сказал "как идея" - сработает. Проверять конкретно эту строчку. Дописывать сейчас "все окружающие" (определение пинов), сидеть "жакать  кнопки, ребуть плату, отписываться о результатах" - у мня нет времени.

Если хотите - напишите скетч целиком. Вместо digitalRead(buttonPin) используйтеся какие-нибудь bool переменные. Устанавливайте их. Результат - выводите в Serial.

Если проблема только "нет под руками на чем проверить" - запущу и отпощу его вывод. Но сам "рабочий скетч", который можно просто copy-past и запустил  - пишите сами.

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

Я просто поправил.

leshak
Offline
Зарегистрирован: 29.09.2011

 Код:

void setup(){
  bool btn1;
  bool btn2;
  bool btn3;
  int period=0;
  
  Serial.begin(57600);
  
  for(byte i=0;i<8;i++){
      btn1=bitRead(i,0);
      btn2=bitRead(i,1);
      btn3=bitRead(i,2);
      
      period= (btn1)?12:(btn2?32:(btn3?4:0));
      
      Serial.print("btn1=");Serial.print(btn1);
      Serial.print(",btn2=");Serial.print(btn2);
      Serial.print(",btn3=");Serial.print(btn3);
      Serial.print(",period=");Serial.print(period);
      Serial.println();
      

  }

}

void loop(){
}

Вывод:

btn1=0,btn2=0,btn3=0,period=0
btn1=1,btn2=0,btn3=0,period=12
btn1=0,btn2=1,btn3=0,period=32
btn1=1,btn2=1,btn3=0,period=12
btn1=0,btn2=0,btn3=1,period=4
btn1=1,btn2=0,btn3=1,period=12
btn1=0,btn2=1,btn3=1,period=32
btn1=1,btn2=1,btn3=1,period=12

 

zz-vop
Offline
Зарегистрирован: 05.08.2012

 Не стал создавать новую тему. 

Вот код:

const int buttonPin = 2;     // the number of the pushbutton pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
int ledPin = 9;    // LED connected to digital pin 9
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
int dCount;
int fadeValue = 255;
boolean button = 0;
boolean Fsett = 0;
boolean Dsett = 0;

void setup()  { 
 pinMode(buttonPin, INPUT); // nothing happens in setup 
  button = digitalRead(buttonPin); 
  
} 

void loop()  { 
  Dsett = 0;
  if (button == 1){
    
  if (debounce() == 1 && Dsett == 0){
  pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, HIGH);
      delay(100);
        dCount +=1;
       
  }
  else {digitalWrite(ledPin, LOW);
digitalWrite(ledPin, LOW);
      delay(dCount*100);
     digitalWrite(ledPin, HIGH);
     delay(dCount*100);
     leds(1);
     leds(1);
     leds(1);
     leds(1);
     Dsett = 1;
     
   if (debounce() == 1 && Dsett == 1)
     {
        fadeValue -=5;
        analogWrite(ledPin, fadeValue);         
        delay(100); 
     }
     else if (Dsett == 1) {analogWrite(ledPin, fadeValue);}
  }        
     
}
  else {leds(1);}
}
}

Я не исправлял функцию лед() она тут просто чтоб визуализировать процесс.

Как по моему мнению должен был бы исполняться код, представленный выше: при включеннии питания кнопка нажата и переменная Dsett = 0; -> переходим в условие и начинаем отсчитывать сколько горел LED. Отпускаем кнопку светодиод гаснет на время "сколько горел LED" потом загарается на такое же время. Дальше светодиод судорожно мигает и переменная Dsett становится равной 1. Потом когда я нажимаю кнопку начинается уменьшение частоты ШИМ (наглядно показывается на светодиоде). когда отпускаю - светодиод просто горит с "нужным" ШИМ.

Но на самом деле ШИМ нифига не настраивается. все зацикливается на 

else {digitalWrite(ledPin, LOW);
digitalWrite(ledPin, LOW);
      delay(dCount*100);
     digitalWrite(ledPin, HIGH);
     delay(dCount*100);
     leds(1);
     leds(1);
     leds(1);
     leds(1);
     Dsett = 1;

Я уже испробовал и "else if" чет вообще никак. У  меня ощущение, что на условие  (debounce() == 1 && Dsett == 1) вообще наплевать ардуинке/компилятору. Где я накосячил? может я не понимаю логику работы else/if/else if... (я осознаю говнокодовость выше написанного).

Отдельно без наворотов ШИМ без проблем настраивается вот так вот простым удержанием кнопки и по "отпусканию" кнопки ШИМ такой как надо...

Заранее спасибо.

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

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

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

2. Такое впечатление, что вы забываете, что loop() это цикл. Эта функция вызывается раз-за разом бесконечно. Вам нужно смотреть ЧТО у вас происходит ПОСЛЕ того как вы включили шим. Всего на 100 милисекунд. Далее, скорее всего, loop заканчивается и начинается сначала, там попадает в какой-то вариант где вы опять вызываете leds(). Который, естественно отменяет действие шим, и останавливает скетч кучей своих delay().  В результате шим у вас имеет шанс проявить себя всего 100 милисекунд.

Поставте там delay() ощутимей, что-бы точно увидеть что "вы туда попадаете" или Serial.print сделайте. Можете вообще натыкать Serial.print-тов во все ветки и посмотрите куда ваша логика бегает в реальности, а не "как вы думаете".

zz-vop
Offline
Зарегистрирован: 05.08.2012

 Спасибо. натыкаю Serial.print'ов. посмотрю куда он там бегает. Вы правы. я все время забываю, что void loop() это бесконечный цикл.

leshak
Offline
Зарегистрирован: 29.09.2011

zz-vop пишет:

 я все время забываю, что void loop() это бесконечный цикл.

Можете написать так:

void loop(){
  while(1){
    .....
       ВАША ЛОГИКА
    ....
  }
}

С точки зрение логики это будет идентично. Расплатитесь несколькими байтами памяти, зато будет "перед глазами" и точно не забудете :)

zz-vop
Offline
Зарегистрирован: 05.08.2012

 так и сделал)

zz-vop
Offline
Зарегистрирован: 05.08.2012

так и сделал. нашел все косяки. во всем виновата невнимательность и спешка. все переписал красиво. заработало

спасибо