Arduino – Gestione degli Interrupt

Gli interrupt sono dei segnali asincroni (letteralmente interruzioni) che rappresentano una “richiesta di attenzione” da parte di una periferica. Esistono diversi tipi di interrupt, e diversi modi di gestione, ma in generale alla richiesta di interruzione il processore interrompe il flusso di programma per eseguire una specifica routine di interruzione (routine che può variare per le diverse interruzioni). Alla conclusione della routine il flusso del programma riprende. Il processo che  porta all’interruzione del flusso di programma è piuttosto complesso (dovendo gestire valori dei registri, stack pointer, stato della macchina ecc…) e diciamo trasparente al programmatore.

Sull’arduino possiamo usare gli interrupt per svolgere una certa routine in background. Essi si dividono in interrupt interni ed interrupt esterni. Dei primi non ce ne occupiamo in quanto necessitano di una conoscenza dell’ATMega328 molto approfondita. Mentre gli interrupt esterni sono quelli che useremo. Nella maggior parte delle board i pin utilizzabili come interrupt sono due, il pin 2 e 3. Vediamo come usarli.

I comandi a nostra disposizione per la gestione degli interrupt sono 2:

  • attachInterrupt(interrupt, function, mode)
  • detachInterrupt(interrupt)

Il primo serve per impostare un’interrupt, il secondo per toglierlo. Vediamone insieme la sintassi.

[arduino]volatile int Stato = LOW;
void setup() {
pinMode(13, OUTPUT);
attachInterrupt(0, Cambia, FALLING);
}
void loop() {
digitalWrite(13, Stato);
}
void Cambia() {
Stato = !Stato;
}[/arduino]
xxx interrupt
Download

con questo sketch ed il cablaggio indicato siamo in grado di far accendere il LED premendo il pulsante. Notiamo che ci si riferisce al pin2 come interrupt 0 ed al pin3 come interrupt 1.

Mentre usando il comando detachInterrupt() disattiviamo l’interrupt.

Function indica quale funzione viene chiamata al verificarsi dell’interrupt.

Mode invece indica con quale modalità acquisire l’interrupt. Le possibili modalità sono:

  • LOW attiva l’interrupt ogni volta che il pin è al valore logico basso;
  • CHANGE attiva l’interrupt quando il pin cambia stato;
  • RISING attiva l’interrupt quando il pin passa da LOW a HIGH;
  • FALLING attiva l’interrupt quando il pin passa da HIGH a LOW;

Nota: Abbiamo usato il qualificatore volatile. Tutte le variabili che modifichiamo all’interno della funzione “attaccata” all’interrupt devono essere dichiarate con questo attributo. Questo serve a memorizzare il valore della variabile nella RAM e non nei registri dell’ATMega328: durante l’esecuzione di un interrupt il valore dei registri viene modificato, ed i dati ivi memorizzati possono andare persi.

Precisazioni (grazie a Marco):

Dai commenti ricevuti ci siamo resi conto che manca una nota importante,.

All’interno della funzione chiamata dall’ interrupt attach() non funzionano tutti quei comandi che hanno a che fare  con le temporizzazioni, in particolare i delay() e i millis() non funzionano correttamente: il primo non garantisce il ritardo indicato, il secondo non viene incrementato (ritorna sempre lo stesso valore).  Inoltre le comunicazioni seriali ricevute nel periodo in cui stiamo eseguendo l’interrupt verranno scartate.

15 commenti su “Arduino – Gestione degli Interrupt”

  1. Buongiorno, mettendo un pulsante a massa e resistenza da 10kohm a +5v al pin 2 non va completamente bene: ottengo un treno di interrupt invece di uno solo ogni volta che uso il pulsante.
    Cosa posso fare? mettere un condensatore in parallelo al pulsante? ho già messo un dalay sulla subrutine chiamata dall’interrupt ma con poca efficacia (es: invece di 10 chiamate ne ottengo 2 o 3)
    Grazie allego programmino:

    int speackerPin = 11;
    int ledpin = 13;
    int ledpin1 = 12;
    volatile int Stato = 900;

    void setup() {
    pinMode(ledpin, OUTPUT);
    pinMode(ledpin1, OUTPUT);
    pinMode(speackerPin, OUTPUT);
    attachInterrupt(0, Cambia, RISING);
    attachInterrupt(1, Cambia1, RISING);
    Serial.begin(9600);
    }
    void loop() {
    for (long i = 0; i<100; i = i + 1) {
    digitalWrite(speackerPin, HIGH);
    delayMicroseconds(Stato); // 1136 nota La
    digitalWrite (speackerPin, LOW);
    delayMicroseconds(Stato);
    }
    digitalWrite(ledpin, HIGH);
    delay(100);
    digitalWrite (ledpin, LOW);
    delay(100);
    digitalWrite(ledpin1, HIGH);
    delay(200);
    digitalWrite (ledpin1, LOW);
    delay(200);
    Serial.println(Stato);
    }

    void Cambia() {
    Stato = Stato – 30;
    delay(1000);
    }

    void Cambia1() {
    Stato = Stato + 30;
    delay(1000);
    }

    1. Ciao Marco,
      grazie al tuo commento mi sono reso conto che mancava una piccola nota per completare l’argomento: l’uso dei delay(1000) all’interno delle due funzioni (Cambia e Cambia1) non è consigliato oltre che essere inefficace.
      Purtroppo il treno di impulsi che ricevi è un problema relativo alla realizzazione fisica del pulsante che all’atto di chiudere il contatto ha una serie di falsi contatti: in un normale sketch questo si evita proprio con i delay(), ma in un interrupt???
      Ho risolto usando un debounce hardware (un circuito che frena la risposta del pulsante):
      Debounce hardware
      Le due resistenze sono da 4,7K mentre la capacità è da 100 nF.
      Spero di esserti stato utile.

  2. urgente… devo collegare dei tasti di pressione ad arduino che quando vengono premuti parte una traccia audio e una volta lasciati smette come posso fare come devo collegarli come funziona … venerdì ho l’esame e devo fare questa cosa potrete aiutarmi?? p.s metto in chiaro che ne capisco poco di elettronica e di arduino quasi zero

    1. Ciao Pietro,
      Hai due giorni per fare un progetto non molto complicato, ma che necessita di un certo hardware.
      Per prima cosa la traccia audio come la fai “suonare”? ci vorrebbe un shield Audio.
      Il controllo sul pulsante è facile, basta che fai due funzioni, una per far partire la musica e l’altra per fermarla. Poi assegni l’interrupt in mode RISING(o FALLING) ad una e FALLING (o RISING) all’altra.
      La scelta dipende da come colleghi il pulsante se con resistenza di pull-up o pull-down (guarda questo).
      Francamente però come ti dicevo in due giorni, essendo completamente a digiuno di tutto, meglio se trovi qualcuno lì vicino a casa tua che possa aiutarti. Un paio di volte ho fatto progetti a distanza con i miei amici, e puntualmente non funzionava niente al momento di mettere insieme le cose.
      Facci sapere!

  3. Ho un problemino da poco conto “per chi ne sa tanto di arduino”vorrei inserire una funzioncina in interrupt che mi modifichi il valore di una variabile es:

    if ((Setup==HIGH) and (sw==1)){lcd.clear(); sw=9;}
    ecco premendo un tasto collegato al pin interrupt0, mi modifichi il valore sw, cosi’ nel ciclo successivo il valore sw e’ cambiato, e la striga di comando non e’ piu’ esatta e mi ferma l’esecuzione in arresto rapido.

    qualcuno gentilmente e’ in grado di darmi una mano?
    Grazie in anticipo per chi interverrà.

    1. Ciao Enzo,
      Come vedi nel codice di esempio nell’articolo la variabile Stato, modificata ed utilizzata sia nel loop() che nella funzione sotto interrupt, è dichiarata con il modificatore volatile questo permette appunto di mantenere il suo valore senza che venga cancellata all’uscita dalle funzioni.
      Basta che quando dichiari sw usi la sintassi:
      volatile int sw;
      Se hai ancora problemi posta tutto lo sketch così posso aiutarti meglio.

  4. urgente
    ho un problema , devo far lampeggiare un led ogni due secondi con l’istruzione interrupt come posso fare sono alle prime armi per favore se rispondete potete essere chiari ?? Un grandissimo grazie a chi risponde.

    1. Ciao Alexander,
      Per realizzare una chiamata di interrup ogni too secondi devi usare i timer hardware dell’Arduino.
      Per far ciò devi usare la libreria timer1.
      Una bozza di codice può essere questa:
      [arduino]
      #include TimerOne.h;

      int i = HIGH;
      int DELAY = 1000000;
      void setup()
      {

      pinMode(13, OUTPUT);
      Timer1.initialize(DELAY); // indica ogni quanti microsecondi generara la chiamata ad interrupt
      Timer1.attachInterrupt(change_frame); // Indica quale funzione eseguire quando si verifica l’interrupt
      }

      void change_frame()//funzione indicata in Timer1.attachInterrupt();
      {
      digitalWrite(13, !i);//accende o spegne il led ogni DELAY microsecondi
      }
      void loop()
      {
      //Tuo codice
      }[/arduino]

      Il codice non l’ho provato, e l’ho scritto di fretta (sono fuori casa e non posso provarlo).
      Fammi sapere come va!

      1. ciao , ho scritto il tuo software ma quando do il comando di verifica mi da un errore su ” Timer1.initialize(DELAY);” come posso fare ?

  5. Salve,
    ho un semplice programma che conta quanti click vengono fatti con un bottone
    ____________________________________________________
    int x;
    void setup() {
    attachInterrupt(0, Cambia, RISING);////// 0→PIN 2 1→PIN 3
    Serial.begin(9600);
    }
    //————————————-//
    void Cambia() {

    x=x+1;

    }

    void loop() {

    Serial.println(x);

    }
    _________________________________________________________________________________

    A volte ad ogni click conta non 1 ma 2 clic, dove potrebbe essere l’errore?

    Daniele

    1. Ciao Daniele,

      Scusaci, ma gli impegni di lavoro ci rendono difficile seguire il sito.
      Cercheremo nei prossimi mesi di migliorare la situazione.

      Venendo al tuo problema:
      Il pulsante al momento della pressione non ha un contatto pulito, ma genera una serie di impulsi prima di assestarsi al valore stabile ( sia se premuto che se rilasciato).
      L’interrupt è talmente veloce che riesce a contare questi impulsi!
      Quello che ti serve è un circuito di debouncing come spiegato nel primo commento a questo articolo.
      Oppure un debounce software riscrivendo cos’ la routine:
      void Cambia() {
      Delay_ms(150);
      if(digitalRead(0) == HIGH)
      x=x+1;
      }
      In questo modo dai al pulsante 150ms di tempo per assestarsi e poi controlli se il pulsante è premuto o rilasciato.
      Nel caso in cui l’interrupt è impostato su FALLING al posto di HIGH devi mettere LOW.

      Spero di aver risolto il tuo problema!

Rispondi a Alexander Annulla risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *