Gestione di eventi: Polling e Interrupt

Polling

Il polling è un metodo di monitoraggio continuo delle periferiche (pin, timer, ecc.) in attesa di eventuali eventi.
Nella vita reale può essere paragonato, ad esempio, ad una persona che controlla periodicamente la cottura di un cibo, l'arrivo della posta, l'orario di uscita dal lavoro.

Interrupt

Per interrupt (interruzione) si intende un segnale o notifica che una periferica invia al sistema per una richiesta di intervento: in questo caso il sistema interrompe temporaneamente ogni processo in corso per gestire la notifica ricevuta. In Arduino la richiesta di interrupt può essere generata da un evento interno (es. timer) oppure dalla variazione dello stato di un pin.
Nella vita reale l'interrupt può essere paragonato, ad esempio, ad una persona che guarda la posta solo quando riceve un suono di notifica dallo smartphone, uno studente che si prepara ad uscire al suono della campanella.

Il polling, all'interno di sistemi a bassa complessità, è generalmente più semplice da gestire rispetto all'interrupt, ma impiega maggiori risorse di calcolo e non garantisce tempi rapidi e regolari nella gestione degli eventi.

Note

  • Arduino Uno può gestire solo 2 interrupt esterni (altri modelli di Arduino hanno maggiori possibilità)
  • I pin 2 e 3 sono dedicati alla gestione degli interrupt (con opportune librerie è possibile assegnare gli interrupt ad altri pin)
  • All'accensione del microcontrollore gli interrupt sono abilitati, se necessario si possono disabilitare
  • L’IDE di Arduino semplifica l’uso degli interrupt introducendo però alcune limitazioni rispetto alle effettive possibilità dell’ATMega328

Funzioni per la gestione dell’interrupt

Per utilizzare un interrupt è necessario indicare il pin che deve ricevere la notifica, il tipo di segnale (mode) che lo genera e sviluppare la funzione detta ISR (Interrupt Service Routine) che dovrà eseguire quanto richiesto.

Interrupt ISR

L'IDE mette a disposizione particolari istruzioni per la gestione degli interrupt.

attachInterrupt(digitalPinToInterrupt(pin) ISR, mode)   Abilita un determinato pin a ricevere le notifiche di interrupt

pin: il numero del pin che deve ricevere la richiesta di interrupt (valori ammessi 2 o 3)

ISR: la funzione che deve essere eseguita in risposta alla richiesta di interrupt

mode: il tipo di segnale che deve attivare l'interrupt

  • LOW: interviene quando il pin diventa basso
  • CHANGE: interviene quando lo stato del pin cambia
  • RISING: interviene nella transizione da basso a alto
  • FALLING: interviene nella transizione da alto a basso

Per quanto riguarda la funzione ISR, valgono alcune importanti regole:

  • Una ISR non può avere alcun parametro e non deve restituire alcun valore
  • Una ISR dovrebbe essere il più breve e veloce possibile
  • Se il programma utilizza più di una ISR, può essere eseguita solo una alla volta: altri interrupt verranno eseguiti dopo che quello corrente termina, in un ordine che dipende dalla priorità
  • All’interno di una ISR non devono essere usate le funzioni di tempo millis(), delay() e micros() perché fanno uso di contatori gestiti tramite interrupt: come istruzione di tempo si può usare delayMicroseconds() perché non usa alcun contatore
  • Per passare i dati tra un ISR e il programma principale devono essre usate solo variabili globali che devono essere dichiarate volatile.

detachInterrupt(digitalPinToInterrupt(pin))   Disattiva l'interrupt sul pin associato la richiesta di interrupt (int)

interrupts()   Riattiva gli interrupt dopo che sono stati disabilitati da nointerrupts ()

noInterrupts()   Disabilita gli interrupt: da usare con attenzione perché disattiva alcune funzioni di sistema

Il controllore di Arduino UNO, ATmega328 è in grado di gestire vari tipi di interrupt, nella tabella seguente sono indicati i primi 16.

InterrupTable1 16

Nell'esempio seguente, un pulsante è collegato al pin digitale 2: ogni volta che si preme il pulsante, il LED montato sulla scheda, si accende se spento, si spegne se acceso (pulsante bistabile).

Esempio

#define LED_PIN 13 // Oppure LED_BUILTIN
#define DEBOUNCE_TIME 50000 // In microsecondi
#define INTERRUPT_PIN 2 // Pin del pulsante

volatile byte ledStatus = LOW;

void setup() {
   pinMode(LED_PIN, OUTPUT);
   pinMode(interruptPin, INPUT_PULLUP);
   digitalWrite(LED_PIN, ledStatus);
   attachInterrupt(digitalPinToInterrupt(interruptPin), changeLedStatus, FALLING);
}

void loop() { } // Non usata

// La funzione ISR
void changeLedStatus() {
   ledStatus = !ledStatus; // Cambia stato
   digitalWrite(LED_PIN, ledStatus);

   // Ritardo per evitare eventuali "rimbalzi" del pulsante
   delayMicroseconds(DEBOUNCE_TIME); // Non posso usare delay() nella ISR!
}