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.
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.
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!
}