un pulsante con interrupt e 3 led

ciao a tutti, mi sto infastidendo con questo problema che forse sara' banale ma non ne esco fuori. con un pulsante ogni volta che viene premuto si deve accendere un led spegnendo il precedente cosi all'infinito. grazie

const int ledPin1 = 3;      // the LED1 pin
const int ledPin2 = 4;      // the LED2 pin
const int ledPin3 = 5;      // the LED3 pin
int stato=0;

void setup() {
      Serial.begin(9600);
      pinMode(ledPin1, OUTPUT);
      pinMode(ledPin2, OUTPUT);
      pinMode(ledPin3, OUTPUT);
      
      digitalWrite(ledPin1, LOW);
      digitalWrite(ledPin2, LOW);
      digitalWrite(ledPin3, LOW);
      attachInterrupt(0, pulsRelay, LOW); // pulsante su pin 2  
}

void loop() {
}


void pulsRelay(){

if (stato==0){
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
      stato=1; 
      Serial.println(stato);
}
if (stato==1){
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, LOW);
      stato=1;
      Serial.println(stato);
}
if (stato==2){
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);
      stato=2;
      Serial.println(stato);
}

  stato=0;
  Serial.println(stato);
  Serial.println();

}

A parte che io non userei l'interrupt per un pulsante, e che se decidi di farlo devi abbondare di debounce hardware, guarda bene come imposti il nuovo valore della variabile stato in ogni if. E perché mai azzerarlo alla fine della ISR?

Oltre a quanto detto da @SukkoPera la variabile che usi nell'ISR deve essere definita volatile

In realtà se la usa solo nella ISR, non è necessario :).

Se non erro, e chiedo quindi conferma, se la variabile la usi sono nell'ISR ok non serve il volatile ma, se come in questo caso, la variabile deve mantenere il valore anche fuori dall'ISR (anche se non usato) poiché al prossimo rientro nell'ISR occorre incrementarlo e quindi il valore deve permanere anche al di fuori dell'ISR la volatile invece dovrebbe essere necessaria?

È corretto dire che sia necessario mantenere il valore di stato fuori dalla ISR.

Nell'esempio in questione, questo è ottenuto tramite il fatto che è stata dichiarata come variabile globale.

Se fosse una variabile locale, questo si otterrebbe tramite il qualificatore static.

volatile ha un altro scopo, ovvero dire al compilatore di ricaricare dalla memoria il valore di una variabile ogni volta che deve essere utilizzata. Questo serve perché, a causa del principio di località del codice, il compilatore potrebbe aver deciso di tenere "per un po'" una variabile in un registro. Se nel frattempo scatta la ISR (che, ricordo, può interrompere l'esecuzione del codice principale in qualunque momento), questa non può che agire sul valore della variabile in memoria, che può non essere quello attuale e, allo stesso modo, quando si torna al flusso di esecuzione principale, il valore nel registro non avrà risentito dell'esecuzione dell'ISR. Per evitare questo, occorre caricare la variabile dalla memoria ogni volta che ne serve il valore, e aggiornare quest'ultimo in memoria ogni volta che cambia.

A questo punto dovrebbe essere evidente che volatile ha senso solo quando una variabile viene utilizzata sia in una ISR, sia fuori da essa.

Grazie mille della spiegazione più che chiara ed esaustiva :)

Tanto per capire, non è sbagliato anche il modo in cui gestisce la variabile stato, mi spiego meglio, inizio programma stato è =0, quando si preme il pulsante esegue la void pulsrelay e quindi entra nella prima if essendo vera, poi però sempre nella if assegna a stato il valore 1 uscendo dalla prima if anche la seconda è ormai vera e la esegue poi azzera stato con 0 e via dicendo in un loop infinito ad ogni rimbalzo del pulsante, stato 2 non sarà mai vera

Certo, è ciò a cui alludevo nel post #1!

SukkoPera: A parte che io non userei l'interrupt per un pulsante, e che se decidi di farlo devi abbondare di debounce hardware, guarda bene come imposti il nuovo valore della variabile stato in ogni if. ....

E a parte questo (e tutto il resto detto), è buona norma che le ISR siano il più [u]veloci[/u] e [u]corte[/u] possibili, quindi ... ... nella ISR alzare solo una flag per avvertire che l'evento è avvenuto, mentre nel loop() controllare la flag e reagire di conseguenza ;)

Guglielmo

SukkoPera: Certo, è ciò a cui alludevo nel post #1!

ops vero, non avevo letto bene la tua risposta prima :grin:

bene, grazie a tutti per le risposte, vediamo cosa posso fare. un saluto A.

ok, diciamo che funziona, ovviamente ho tolto la gestione interrupt che propio con i pulsanti non riesco a farmela digerire… con il telecomando funziona con il pulsante ci vuole il doppio click…

/*
OK FUNZIONA (circa....) MANCA SOLO DA SISTEMARE ENCODER
*/
#include <LiquidCrystal_I2C.h>
#include <stdlib.h>
#include <IRremote.h>


LiquidCrystal_I2C lcd(0x20,16,2);

char cGain[17];
String gain;
char icGain[6];
float iGain = -95.5;
float nGain = 1;


boolean mute = false;
// custom LCD characters for the volume bar
byte v0[8] = {B00000,B00000,B00000,B00000,B00000,B00000,B00000,B11111};
byte v1[8] = {B00000,B00000,B00000,B00000,B00000,B00000,B11111,B11111};
byte v2[8] = {B00000,B00000,B00000,B00000,B00000,B11111,B11111,B11111};
byte v3[8] = {B00000,B00000,B00000,B00000,B11111,B11111,B11111,B11111};
byte v4[8] = {B00000,B00000,B00000,B11111,B11111,B11111,B11111,B11111};
byte v5[8] = {B00000,B00000,B11111,B11111,B11111,B11111,B11111,B11111};
byte v6[8] = {B00000,B11111,B11111,B11111,B11111,B11111,B11111,B11111};
byte v7[8] = {B11111,B11111,B11111,B11111,B11111,B11111,B11111,B11111};
// costanti e var pulsante
const int buttonPin = 7;    // the pushbutton pin
const int ledPin1 = 3;      // the LED pin
const int ledPin2 = 4;      // the LED pin
const int ledPin3 = 5;      // the LED pin
// Variables will change:
int ledState = HIGH;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

int countP=0;
long lastDebounceTime = 0;   // the last time the output pin was toggled
long debounceDelay = 10;     // the debounce time; increase if the output flickers




//const int slaveSelectPin = 11;
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  //attachInterrupt(0, pulsRelay, LOW);
  
  Serial.begin(9600);
  irrecv.enableIRIn();
  //lcd.begin(16, 2);
  lcd.init();
  lcd.setBacklight(1);
    
  lcd.createChar(0, v0);
  lcd.createChar(1, v1);
  lcd.createChar(2, v2);
  lcd.createChar(3, v3);
  lcd.createChar(4, v4);
  lcd.createChar(5, v5);
  lcd.createChar(6, v6);
  lcd.createChar(7, v7);
  
  lcd.setCursor(0,0);  //first line
  lcd.print(" HiFi IR Volume ");
  delay(2000);
  lcd.clear();
  setVolumeBar(nGain);
  
   // Inizio impostazioni pulsante 
    pinMode(buttonPin, INPUT);
    pinMode(ledPin1, OUTPUT);
    pinMode(ledPin2, OUTPUT);
    pinMode(ledPin3, OUTPUT);
  
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
   // Fine impostazioni pulsante 
}

void setVolumeBar(int nGain) {
  // convert gain to a decibal string, and write to the lcd
  iGain = 31.5 - 0.5*(255 - float(nGain));       
  gain.toCharArray(icGain, 6);
  dtostrf(iGain,4,1,icGain);
  gain = String(icGain);
  lcd.clear();
  ("Volume: " + gain + " dB").toCharArray(cGain,17);
  lcd.setCursor(0, 0);  //first line
  lcd.print(cGain);
  // write the volume bar
  lcd.setCursor(0, 1);  //second line
  for (int i =  0; i < 16; i++) {
    if (nGain/2 > i*8) {
      lcd.setCursor(i, 1);
      lcd.write(round(i/2));  
    }
  }  
}



void setGain(int nGain) {
  //digitalWrite(slaveSelectPin,LOW);
  //SPI.transfer(nGain);  // right channel
  //SPI.transfer(nGain);  // left channel
  //digitalWrite(slaveSelectPin,HIGH);
}


void decodeIR(int value) {
  switch (value) {
  case 0xFFA25D:
    mute = !mute;
    lcd.setCursor(0, 0);
    if (mute == true) {
      lcd.clear();  
      lcd.print("     MUTED!     ");
      delay(1000); 
      setGain(1);
    }
    if (mute == false) {  
      setGain(nGain);
      setVolumeBar(nGain);
    }
    delay(1000);
    break;
  case 0xFF629D:
    if (nGain < 255) {  
      nGain = nGain + 5;
      setGain(nGain);
      setVolumeBar(nGain);
    }
    else {
      lcd.setCursor(0, 0);
      lcd.clear();
      lcd.print("Maximum Reached");
      delay(1000);
    }  
    break;
  case 0xFFE21D:
    if (nGain > 1) {
      nGain = nGain - 5;
      setGain(nGain);
      setVolumeBar(nGain);
    }
    else {
      lcd.setCursor(0,0);
      lcd.clear();
      lcd.print("Minimum Reached ");
      delay(1000);     
    }
    break;
    
//***************************

  case 0xFF22DD: // ingresso 1
          digitalWrite(ledPin1, HIGH);
          digitalWrite(ledPin2, LOW);
          digitalWrite(ledPin3, LOW);  	
          lcd.clear();
          lcd.setCursor(0,0); // prima riga primo carattere
          lcd.print("  Input CD-ROM");
          delay(1500);
          setVolumeBar(nGain);
    	break;

  case 0xFF02FD: // ingresso 2
          digitalWrite(ledPin1, LOW);
          digitalWrite(ledPin2, HIGH);
          digitalWrite(ledPin3, LOW);	
          lcd.clear();
          lcd.setCursor(0,0); // prima riga primo carattere
          lcd.print("  Input DAC");
          delay(1500);
          setVolumeBar(nGain);
    	break;        

  case 0xFFC23D: // ingresso 3
          digitalWrite(ledPin1, LOW);
          digitalWrite(ledPin2, LOW);
          digitalWrite(ledPin3, HIGH);	
          lcd.clear();
          lcd.setCursor(0,0); // prima riga primo carattere
          lcd.print("  Input AUX");
          delay(1500);
          setVolumeBar(nGain);
    	break;      

// pulsante on/off da implementare     	
  case 0xFFA857: // pulsante + del telecomando
          lcd.clear();
          lcd.setCursor(0,0); // prima riga primo carattere
          lcd.print("Hi Fi Spento...");
          delay(1500);
    	break;      	  
  }
  
}

void pulsRelay(){
int reading = digitalRead(buttonPin);  // var gestione pulsante relay

    if (reading != lastButtonState) {
      // reset the debouncing timer
      lastDebounceTime = millis();
      Serial.println("2");
    }

    if ((millis() - lastDebounceTime) > debounceDelay && countP==0) {
      if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == HIGH) {
          ledState = !ledState;
          digitalWrite(ledPin1, HIGH);
          digitalWrite(ledPin2, LOW);
          digitalWrite(ledPin3, LOW);
          countP++;
          lcd.clear();
          lcd.setCursor(0,0); // prima riga primo carattere
          lcd.print("  Input CD-ROM");
          delay(1700);
          setVolumeBar(nGain);
        }
      }    
    }
   
    if ((millis() - lastDebounceTime) > debounceDelay && countP==1) {
      if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == HIGH) {
          ledState = !ledState;
          digitalWrite(ledPin1, LOW);
          digitalWrite(ledPin2, HIGH);
          digitalWrite(ledPin3, LOW);
          countP++;
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print("  Input DAC");   
          delay(1700);
          setVolumeBar(nGain);              
        }
      }  
    }
  
    if ((millis() - lastDebounceTime) > debounceDelay && countP==2) {
      if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == HIGH) {
          ledState = !ledState;
          digitalWrite(ledPin1, LOW);
          digitalWrite(ledPin2, LOW);
          digitalWrite(ledPin3, HIGH);
          countP++;          
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print("  Input AUX");       
          delay(1700);
          setVolumeBar(nGain);          
        }
      }  
    }
    lastButtonState = reading;
    if (countP>2)
      {
       countP=0;
      }
    
} // end pulsante relay


void loop() {
  
  // receive an IR signal
  if (irrecv.decode(&results)) {
    decodeIR(results.value);
    irrecv.resume();  
  }
  
  pulsRelay();

  
} // end loop

Il problema fondamentale è che con gli interrupt DEVI pulire molto bene il segnale dato che NON puoi usare il debouncing software, quindi ... devi fare un bel circuito di debouncing hardware in ingresso al pin di interrupt.

Poi, come ho già detto, nella ISR devi SOLO alzare una flag per indicare la pressione del pulsate e verificare lo stato di tale flag nel loop() facendo quello che devi fare e resettandola per preparati al prossimo interrupt.

Guglielmo

grazie, del consiglio vedro' cosa posso fare. A.