Interrupt con Arduino UNO

Arduino Uno può gestire due interrupt (due vettori di interrupt) su due piedini il pin 2 e il pin 3. Altri modelli di Arduino hanno più ampie possibilità.
All'accensione del processore l'interrupt è abilitato, all'occorrenza si può disabilitare.  Di seguito vengono riportate alcune funzioni per gestire l'interrupt ed esempi:

  • attachInterrupt(intvec, funzione, evento). Definisce la funzione che gestirà l'interrupt, a quale piedino è abbinata (interrupt vector) e a quale evento deve reagire. Gli eventi contemplati sono 4. RAISING il segnale, al pin di ingresso, passa da 0 a 1. FALLING il segnale passa da 1 a 0. CHANGE il segnale cambia o da 0 a 1 o da 1 a 0. LOW il segnale è 0.
    Quando l'evento si presenta, sul piedino definito con intvec, il controllo del programma passa a funzione.
    Arduino UNO gestisce due vettori di interrupt: lo zero, abbinato al pin 2 e l'uno abbinato al pin 3.
  • digitalPinToInterrupt(pin). Ritorna il vettore di interrupt in funzione del numero di pin. es digitalPinToInterrupt(3) ritorna il valore 1.
  • detachInterrupt(intvec). Disabilita lo specifico interrupt.
  • noInterrupts(). Disabilita tutti gli interrupt.
  • interrupts(). riabilita gli interrupt.

Si rimanda alla documentazione ufficiale per maggiori informazioni.

La gestione dell'interrupt del processore ATMEGA328 in realtà è più articolata, ed una più ampia esposizione è disponibile a questo indirizzo: http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf

Per questa esposizione ci si limita a un semplice ma significativo (speriamo) esempio; l'accensione di un led alla pressione di un tasto.

Lo schema  è riportato al link: Schema

La gestione via programma.

Arduino con LEDIl programma è altrettanto semplice. Viene utilizzato il LED L già presente sulla scheda Arduino UNO. Il pulsante è collegato al piedino 2. L'ingresso viene mantenuto alto da una resistenza. La pressione del tasto lo manda a massa.

Il programma principale (loop) testa in continuazione lo stato dell'ingresso (polling). Se premuto (LOW) si accende il LED.

 

 

  1. #define LED 13                // LED al pin 13
  2.                               // Al pin 13 è collegato il led L giallo
  3.                               // sulla scheda di ARDUINO UNO
  4. #define PULSANTE 2            // Il pulsante collegato al pin 2  
  5. int  stato = 0;               // usato per gestire lo stato del pulsante
  6.                               // Pulsante NON premuto = 1 (alto)
  7.                               // Pulsante premuto = 0 (basso)
  8.                                
  9. void setup() {
  10.   // Inizializzo ingressi ed uscite
  11.   pinMode(LED, OUTPUT);       // il pin del LED come uscita  
  12.   pinMode(PULSANTE, INPUT);   // imposta il pin del pulsante come ingresso
  13. }
  14.  
  15. void loop() {
  16.   // Programma principale
  17.    stato = digitalRead(PULSANTE);  // legge il valore dell'input
  18.  
  19.   // controlla che l'input sia LOW = pulsante premuto  
  20.   if (stato == LOW)          
  21.     digitalWrite(LED, HIGH);  //accende il LED                                  
  22.   else  
  23.     digitalWrite(LED, LOW);   //spegne il LED  
  24.  
  25.   // delay(1000);
  26. }

Il listato è facilmente comprensibile leggendo i commenti.

L'ultima riga è commentata. Se si toglie il commento il programma passa la maggior parte del suo tempo nella funzione delay.Il delay vuole simulare una parte di programma che richiede tempo per essere esegiuta, durante il quale non è possibile  prestare attenzione allo stato del pulsante. In questo caso veloci pressioni del pulsante verranno ignorate ed occorrerà tenere premuto il pulsante per almeno il tempo di delay per vederne l'effetto.

La gestione via interrupt

Di seguito lo stesso programma attraverso l'uso di interrupt.

Occorre definire il vettore di interrupt, nel nostro caso 0, abbinato al pin 2. La routine di gestione dell'interrupt si occupa di gestire il LED. In questo caso, volutamente semplice, si evidenzia come il programma principale loop() resta vuoto.

  1. #define LED 13                // LED al pin digitale 13
  2.                               // Al pin 13 è collegato il led L giallo
  3.                               // sulla scheda di ARDUINO UNO
  4. #define PULSANTE 2            // Il pulsante collegato al pin 2                  
  5.  
  6.  
  7. void setup() {
  8.   // put your setup code here, to run once:
  9.   pinMode(LED, OUTPUT);       // il pin del LED come uscita  
  10.   pinMode(PULSANTE, INPUT);   // imposta il pin del pulsante come ingresso
  11.  
  12.   attachInterrupt(0, interruptGiallo, FALLING);
  13.  
  14. }
  15.  
  16. void loop() {
  17.  
  18.   //delay(1000);  
  19.  
  20. }
  21. void interruptGiallo()  {  
  22.    // leggo lo stato del pulsante, ne faccio la negazione (!)
  23.    // e scrivo il rusultato sull'uscita
  24.    digitalWrite(LED,!digitalRead(PULSANTE));  
  25.   }

La gestione dell'interrupt si riduce a digitalWrite(LED,!digitalRead(PULSANTE));. Viene letto lo stato del pulsante digitalRead(PULSANTE));. il simbolo ! nega quanto letto: 0 diventa1 e 1 diventa 0. Il risultato è scritto sull'output: digitalWrite(LED,...);

Se si toglie il commento alla funzione delay si può verificare come la risposta alla pressione del pulsante è immediata.

In questo esempio la gestione dell'interrupt è semplice ma, se le azioni conseguenti l'interrupt, sono più complesse e lunghe è bene ridurre al minimo la routin dell'interrupt e spostare nel programma principale le azioni da fare.

Inoltre, per la chiamata di attachInterrupt(), si è usato un metodo sconsigliato dalla documentazioen. E' sempre bene leggere la documentazione.

Di seguito lo stesso programma rivisto in base a queste due osservazioni.

  1. #define LED 13                // LED  al pin digitale 13
  2.                               // Al pin 13 è collegato il led L giallo
  3.                               // sulla scheda di ARDUINO UNO
  4. #define PULSANTE 2            // Il pulsante collegato al pin 2
  5.  
  6. volatile int stato = 0;       // Stato viene usato per memorizzato lo lo stato del pulsante.
  7.                               // la dichiarazione voltatile è richiesta
  8.                               // per assicurarsi la lettura di stato all'interno
  9.                               // di tutto il progrramma in C              
  10.  
  11. void setup() {
  12.   // put your setup code here, to run once:
  13.   pinMode(LED, OUTPUT);       // il pin del LED come uscita  
  14.   pinMode(PULSANTE, INPUT);   // imposta il pin del pulsante come ingresso
  15.  
  16.   attachInterrupt(digitalPinToInterrupt(PULSANTE), interruptGiallo, FALLING);
  17. }
  18.  
  19. void loop() {
  20.  
  21.   //delay(1000);  
  22.  
  23.   digitalWrite(LED,stato);    // Accendo o spengo il LED in funzione dello stato
  24.  
  25. }
  26. void interruptGiallo()  {  
  27.    // leggo lo stato del pulsante,
  28.    stato = !stato;  
  29.    
  30.   }

In questo esempio viene usata la funzione digitalPinToInterrupt(PULSANTE) come suggerito dalla documentazione. Il risultato è anche una più facile lettura del programma e una maggiore indipendenza di questo dalle versioni di Arduino.

La variabile definita come volatile.

Il linguaggio 'C' cerca ottimizzare il codice per risparmiare memoria e velocizzare il programma. In un caso come in esempio, all'interno di loop() viene usata la variabile stato e non c'è nessun elemento, all'interno di loop() che ne modifica il valore. Molti compilatori C riconoscono questa situazione e ottimizzano il codice mettendo il valore di stato in un registro interno, e il valore di stato non verrà aggiornato durante i cicli di loop successivi.

In realtà stato può cambiare, ma con una gestione esterna a loop(), comandata dall'interrupt. La definizione volatile obbliga il compilatore C a rinunciare ad ottimizzazioni per la variabile stato e a rileggerne il valore ad ogni uso della variabile. In questo modo, le modifiche del valore dovute alla routine di interrupt si possono riflettere nella funzione loop().

Ancora, la chiamata a delay, serve a verificare che il bottone viene letto sempre. Una veloce pressione del tasto, durante il tempo di delay, non viene persa e il led si accenderà o spegnerà anche se dopo il ritardo.

Ultima osservazione.

Rimbalzo pulsanteI pulsanti meccanici sono soggetti a rimbalzi, che possono produrre alla pressione o al rilascio, segnali spuri. Questo potrebbe generare diverse chiamate alla routine di interrupt creando comportamenti anomali.
In un progetto occorre valutare l'effetto di questi rimbalzi ed eventualmente porvi rimedio con tecniche chiamate di debouncing.

Per le tecniche per sopperire al problema si rimandano ad altro articolo.

 

 

 

Categoria: 

Tags: 

Mi piace: 

0
No votes yet