noInterrupts() all'interno della funzione chiamata dall'Interrupt

Salve a tutti,
ho creato un programma tipo Rischiatutto, il primo dei due concorrenti che si prenota per rispondere impedisce che l'avversario possa prenotarsi a sua volta.

const byte ledPin1 = 12;
const byte ledPin2 = 13;
const byte interruptPin1 = 2;
const byte interruptPin2 = 3;
const byte Reset = 4;
volatile byte stato = 0;

void setup() {
  Serial.begin(9600);
  pinMode(Reset, INPUT_PULLUP);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  digitalWrite(ledPin1, LOW);
  digitalWrite(ledPin2, LOW);

  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin1), Pulsante1, FALLING );
  attachInterrupt(digitalPinToInterrupt(interruptPin2), Pulsante2, FALLING );
}

void loop() {
  if (stato){
//    noInterrupts();
    Serial.println(stato);
    if (stato == 1)
  		digitalWrite(ledPin1, HIGH);
    else
  		digitalWrite(ledPin2, HIGH);
    while(digitalRead(Reset));
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    stato = 0;
    interrupts();
  }
}

void Pulsante1() {
  noInterrupts();
  stato = 1;
}

void Pulsante2() {
  noInterrupts();
  stato = 2;
}

È corretto mettere all'interno della funzione chiamata dall'interrupt { Pulsante1() / Pulsante2() } la funzione noInterrupts()?
Oppure è una di quelle funzioni vietate al loro interno?
L'avevo messa nel loop dove è commentata ma poi mi è venuto lo scrupolo: e se i due eventi di interrupt sono talmente ravvicinati che ...

Grazie a tutti
Claudio

Ti basta verificare dentro le ISR se stato è zero, se non lo è allora l'altro concorrente è arrivato prima.
Tanto una volta entrati in un ISR nessuno interviene finché non è terminata e valutare un espressione all'interno non inserisce nessun rallentamento in pratica

per impostazione predefinita su piccoli Arduino (UNO, MEGA, ...) gli interrupt sono disabilitati all'interno di un ISR

Nota che INT0 ha priorità su INT1, quindi uno dei tuoi giocatori sarà favorito.

La cosa migliore sarebbe non usare gli interrupt, leggere il registro PIND e vedrai lo stato di entrambi gli ingressi esattamente allo stesso tempo campionato

const byte giocatore1Pin = 2;
const byte giocatore2Pin = 3;
const byte giocatoriMask = 0b1100;

void preparati() {
  delay(15); // anti bounce
  while ((PIND & giocatoriMask) != giocatoriMask); // attendi che entrambi i pulsanti non vengano premuti
  delay(15); // anti bounce
  Serial.println(F("Giocare a !"));
}

void setup() {
  Serial.begin(115200);
  pinMode(giocatore1Pin, INPUT_PULLUP);
  pinMode(giocatore2Pin, INPUT_PULLUP);
  preparati();
}

void loop() {
  byte stato = (PIND & giocatoriMask) ;
  if (stato != giocatoriMask) {
    switch (stato) {
      case 0b0000: Serial.println(F("uguaglianza")); break;
      case 0b0100: Serial.println(F("Il giocatore 2 vince")); break;
      case 0b1000: Serial.println(F("Il giocatore 1 vince")); break;
    }
    preparati();
  }
}

Come ti hanno già detto ...
... NO, è del tutto inutile, quando entri in una ISR automaticamente gli interrupts sono disabilitati fino a quando non esci.

Guglielmo

Credo che la tua soluzione funzioni a patto che il loop non faccia altro perché se qualcosa interviene ad inserire un rallentamento cadresti nel caso uguaglianza la maggior parte delle volte.
La precedenza degli interrupt avvantaggia un utente ma forse usando un approccio misto si ottiene un risultato ancora migliore.
Collegando i due pulsanti a due pin differenti rispetto a quello dell'INT0 e collegando in parallelo anche il pin dell'interrupt alla prima pressione si entrerebbe nell'ISR e valutando li lo stato del registro probabilmente si ottengono risultati più accurati. Ma sto ipotizzando senza poter provare sul campo la cosa, magari non si ottengono benefici con quest approccio

si infatti se il loop() deve gestire qualcos'altro e si blocca a lungo, allora la lettura non è immediata (ma nel codice originariamente proposto non c'era nient'altro).

Basta fare un while() che attende la pressione di un pulsante, rilevandola con la lettura continua tramite PORT & maschera: appena uno dei due pulsanti viene premuto, esce dal while, visualizza il risultato e fa ogni altra cosa si desideri. In questo modo le letture dei pulsanti sono perfettamente contemporanee e velocissime.

#3

Poi ci sono anche i Pin Change Interrupts...
https://www.google.com/search?q=pin+change+interrupts+atmega328p
però la soluzione con la lettura veloce nel while() mi sembra la migliore.

[quote="crc57, post:1, topic:925255"]
È corretto mettere all'interno della funzione chiamata dall'interrupt { Pulsante1() / Pulsante2() } la funzione noInterrupts()?
Oppure è una di quelle funzioni vietate al loro interno?
[/quote(

È corretto solo se sai quello che fai e quali sono le conseguenze. La funzione millis(), la Serial.print() necessitano degli interrupt attivi globalmente, quindi con noInterrupt() e equivalente a cli() (clear interrupt) spegni gli interrupt globali e quindi millis() non conta più i millesimi di secondo e Serial.print() riempe il buffer ma nulla viene spedito.

Se si vuole continuare con gli interrupt sarebbe bene scendere di livello e parlare dei registri, dei vettori di interrupt, ad esempio cosa è una ISR e cosa una IRQ, rimanendo a questo livello e pagando il prezzo di due chiamate a funzione deatachInterrupt continua ad avere valenza se salviamo i bit accesi o meno di entrambe gli ingressi all'interno della funzione collegata su INT0, e se entrambe gli ingressi valgono 1 c'è parità, mentre se si entra per primo nella funzione collegata su INT1 non serve controllare lo stato del primo ingresso (INT0) poiché INT1 non ha precedenza su INT0 e quindi ha premuto per primo INT1.

In breve condivido in pieno ciò che ha espresso @fabpolli.
Però a quel punto fiscale per fiscale voglio resistenze e condensatori di debounce allo 0.1% di tolleranza. :crazy_face:

PS: come si fa quando i concorrenti sono più di due?

Ciao.

questo non è più il caso. è risolto.

Fino ad un numero di concorrenti tale da poter essere gestito tramite la lettura di un solo registro (il numero gestibile varia dalla tipologia di MCU e dal PORT interrogato) si riesce sempre con il medesimo meccanismo. Oltre credo che per non avvantaggiare nessuno occorra inserire HW esterno. Anche perché non credo proprio che gli equivalenti commerciali usino MCU per gestire la cosa :slight_smile:

Vero, ieri ho guardato il codice del metodo write di HardwareSerial ed è li il trucchetto di verificare se il settimo bit di SREG è 1 o 0.

Ma anche per il pulsante non credo ci si possa affidare a contatti meccanici e quindi già con un sensore IR a forcella, una molla e un fungo (con stampante 3D) possiamo fare la differenza e ci buttiamo alle spalle il debounce non più necessario.

PS: il codice deve tenere conto del fatto che il concorrente può premere, rilasciare e premere nuovamente.

Ciao.

Uh?... Una volta che ha premuto, può fare quello che vuole: verrà ignorato fino al turno successivo.

Debounce HW e festa finita senza troppe complicazioni.
Per quanto riguarda il premi/rilascia se preme e rilascia in una manciata di millisecondi e qualcun altro gli passa avanti (se il senso era quello) al turno dopo vedi che tiene premuto finché non si accende la sua lucina :slight_smile:
Se invece il senso è quello evidenziato da @Datman allora non appena si entra nella ISR si memorizza lo stato di turno finito e come diceva lui finché non parte il turno dopo lo stato dei pulsanti e ininfluente

Esattamente. il codice verifica se gli interrupt sono disabilitati e in questo caso emetterà un nuovo byte se necessario