Archivi tag: interrupt

Esercitazione 2 – Tastiera 4×4 non bloccante con codice di accesso e feedback di errore

Allenamento per l’esame di maturità
Percorso di laboratorio con Arduino per studenti di quinta ITIS

Obiettivo didattico

Scansionare una tastiera 4×4 senza usare una libreria esterna, acquisire una sequenza di tasti, confrontarla con un codice corretto e fornire un feedback di esito positivo o errore. L’attività allena gestione di matrici di pin, scansione righe/colonne, buffer di input e logica non bloccante.

Materiali suggeriti

  • Arduino UNO R3 o UNO R4;
  • tastiera 4×4;
  • LED verde;
  • LED rosso,
  • buzzer opzionale;
  • 2 resitori da 220 Ohm (per i LED);
  • breadboard;
  • cavetti jumper.

Schema di collegamento

Richiamo teorico

Una tastiera a matrice riduce il numero di fili usando righe e colonne. Il microcontrollore attiva una riga alla volta e legge le colonne. Se una colonna va a livello attivo, il tasto corrispondente a quella riga e colonna è premuto. Anche qui bisogna evitare il rimbalzo e gestire l’ingresso come sequenza di eventi.

Schema logico dell’attività

Il programma scansiona una riga alla volta. Quando rileva un tasto stabile, lo aggiunge al buffer. Se viene premuto # confronta il buffer con il codice atteso. Se il codice è corretto accende il LED verde. Se è errato accende il LED rosso, svuota il buffer e ricomincia.

Diagramma di flusso

Diagramma di flusso Mermaid

flowchart TD
    A[Inizio] --> B[Configura righe, colonne e LED]
    B --> C[Scansione tastiera]
    C --> D{Tasto valido trovato?}
    D -- No --> C
    D -- Sì --> E{Tasto uguale a # ?}
    E -- No --> F[Aggiungi carattere al buffer]
    F --> C
    E -- Sì --> G[Confronta buffer con codice]
    G --> H{Codice corretto?}
    H -- Sì --> I[Feedback verde e reset buffer]
    H -- No --> J[Feedback rosso e reset buffer]
    I --> C
    J --> C

Programma

/*
  Prof. Maffucci Michele
  Esercizio 2: Tastiera 4x4 non bloccante con codice di accesso e feedback di errore
*/

// ---------------------------
// Pin delle 4 righe
// ---------------------------
const int righe[4] = { 2, 3, 4, 5 };

// ---------------------------
// Pin delle 4 colonne
// ---------------------------
const int colonne[4] = { 6, 7, 8, 9 };

// ---------------------------
// Mappa dei tasti
// ---------------------------
char mappaTasti[4][4] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

// ---------------------------
// LED di feedback
// ---------------------------
const int PIN_LED_VERDE = 10;
const int PIN_LED_ROSSO = 11;

// ---------------------------
// Codice corretto da inserire
// ---------------------------
char codiceCorretto[] = "2580";

// ---------------------------
// Buffer di ingresso utente
// ---------------------------
char bufferInput[8];
int indiceInput = 0;

// ---------------------------
// Variabili per antirimbalzo
// ---------------------------
char ultimoTastoLetto = '\0';
char ultimoTastoConfermato = '\0';
unsigned long istanteCambio = 0;
const unsigned long TEMPO_DEBOUNCE = 40;

void setup() {
  // Le righe vengono pilotate in uscita.
  for (int i = 0; i < 4; i = i + 1) {
    pinMode(righe[i], OUTPUT);
    digitalWrite(righe[i], HIGH);
  }

  // Le colonne sono ingressi con pull-up.
  for (int i = 0; i < 4; i = i + 1) {
    pinMode(colonne[i], INPUT_PULLUP);
  }

  pinMode(PIN_LED_VERDE, OUTPUT);
  pinMode(PIN_LED_ROSSO, OUTPUT);

  Serial.begin(9600);
  svuotaBuffer();
}

void loop() {
  // Leggo il tasto corrente tramite scansione.
  char tastoCorrente = leggiTastiera();

  // Se è cambiato il valore grezzo, aggiorno il tempo.
  if (tastoCorrente != ultimoTastoLetto) {
    ultimoTastoLetto = tastoCorrente;
    istanteCambio = millis();
  }

  // Se il valore è stabile da abbastanza tempo, lo confermo.
  if ((millis() - istanteCambio) >= TEMPO_DEBOUNCE) {
    if (tastoCorrente != ultimoTastoConfermato) {
      ultimoTastoConfermato = tastoCorrente;

      // Elaboro il tasto solo quando è reale e non nullo.
      if (ultimoTastoConfermato != '\0') {
        gestisciTasto(ultimoTastoConfermato);
      }
    }
  }
}

// ----------------------------------------------------------
// Scansione manuale della tastiera.
// Attivo una riga alla volta e leggo tutte le colonne.
// ----------------------------------------------------------
char leggiTastiera() {
  for (int r = 0; r < 4; r = r + 1) {

    // Prima porto tutte le righe alte.
    for (int i = 0; i < 4; i = i + 1) {
      digitalWrite(righe[i], HIGH);
    }

    // Poi attivo solo la riga corrente.
    digitalWrite(righe[r], LOW);

    // Leggo tutte le colonne.
    for (int c = 0; c < 4; c = c + 1) {
      if (digitalRead(colonne[c]) == LOW) {
        return mappaTasti[r][c];
      }
    }
  }

  // Se non trovo alcun tasto, restituisco nullo.
  return '\0';
}

// ----------------------------------------------------------
// Gestione del carattere ricevuto.
// * cancella, # conferma, altri tasti vengono memorizzati.
// ----------------------------------------------------------
void gestisciTasto(char tasto) {
  Serial.print("Tasto ricevuto: ");
  Serial.println(tasto);

  if (tasto == '*') {
    svuotaBuffer();
    Serial.println("Buffer cancellato");
  } else if (tasto == '#') {
    verificaCodice();
  } else {
    if (indiceInput < 7) {
      bufferInput[indiceInput] = tasto;
      indiceInput = indiceInput + 1;
      bufferInput[indiceInput] = '\0';
    }
  }
}

// ----------------------------------------------------------
// Confronto del buffer con il codice corretto.
// ----------------------------------------------------------
void verificaCodice() {
  bool corretto = true;

  for (int i = 0; codiceCorretto[i] != '\0'; i = i + 1) {
    if (bufferInput[i] != codiceCorretto[i]) {
      corretto = false;
    }
  }

  // Verifico anche la lunghezza.
  if (indiceInput != 4) {
    corretto = false;
  }

  if (corretto == true) {
    Serial.println("CODICE CORRETTO");
    digitalWrite(PIN_LED_VERDE, HIGH);
    delay(500);
    digitalWrite(PIN_LED_VERDE, LOW);
  } else {
    Serial.println("CODICE ERRATO");
    digitalWrite(PIN_LED_ROSSO, HIGH);
    delay(500);
    digitalWrite(PIN_LED_ROSSO, LOW);
  }

  svuotaBuffer();
}

// ----------------------------------------------------------
// Ripulisce il buffer e rimette l'indice a zero.
// ----------------------------------------------------------
void svuotaBuffer() {
  for (int i = 0; i < 8; i = i + 1) {
    bufferInput[i] = '\0';
  }
  indiceInput = 0;
}

Continua a leggere

Allenamento alla maturità con Arduino

Esercitazioni progressive di laboratorio per studenti di quinta ITIS e professionale

Nota importante: questa pagina è in continuo aggiornamento. I link alle diverse esercitazioni saranno aggiunti progressivamente nell’indice, man mano che i contenuti verranno pubblicati.

La preparazione alla seconda prova dell’Esame di Stato passa anche attraverso l’attività pratica in laboratorio in modo che l’azione possa essere utile per focalizzare lo studio teorico e lo svolgimento delle tracce degli anni precedenti.

Per affrontare bene la maturità serve allenamento operativo, metodo e continuità.
Per questo motivo ho deciso di proporre una serie di esercitazioni pratiche con Arduino, pensate per studenti del quinto anno dell’ITIS – Elettronica e Automazione, utilizzabili sia a scuola in laboratorio con il supporto dei docenti sia a casa in autonomia come attività di ripasso e consolidamento.

Sono in realtà attività pratiche che possono essere svolte in circa 90 minuti di lavoro e che prendono in considerazione argomenti del triennio dell’ITIS che ritengo possano servire per lo svolgimento di problemi che potrebbero essere presenti nel tema d’esame di TPSEE.

Quindi questa raccolta nasce con un obiettivo molto preciso:

aiutare gli studenti a prendere confidenza con quelle strutture di programmazione che, molto spesso, risultano più difficili da capire e da usare in modo corretto, ad esempio:

  • temporizzazioni non bloccanti con millis()
  • multitasking cooperativo
  • array e matrici
  • interrupt
  • puntatori
  • macchine a stati
  • gestione ordinata degli eventi

Due percorsi distinti ma complementari

All’interno del lavoro di preparazione alla maturità sto sviluppando due percorsi paralleli.

Il primo è questa serie di esercitazioni trasversali di programmazione, focalizzate sulle strutture software più importanti e spesso più ostiche.

Il secondo, invece, è già in corso con un mia classe e nel breve pubblicherò le attività di laboratorio, si tratta di una reinterpretazione per il laboratorio di Sistemi elettronici e TPSEE della prova di maturità della sessione ordinaria 2018 di TPSEE, che richiede di affrontare un processo articolato con preallarme, attuatori ON/OFF, acquisizione di sensori, scelta dell’intervallo di campionamento, progettazione delle interfacce e descrizione dell’algoritmo di gestione.

Partiamo però dalle 20 attività.

Come sono costruite le esercitazioni

La struttura delle schede di lavoro rispecchia quelle che in genere consegno ai miei studenti.

Ogni attività sarà presentata con una struttura costante, così da aiutare anche gli studenti che hanno competenze di programmazione ancora deboli.

In ogni post troverete:

  • richiamo teorico iniziale delle istruzioni usate
  • analisi semplificata del problema
  • materiali necessari
  • schema logico di funzionamento
  • diagramma di flusso
  • codice Arduino completo e commentato nel dettaglio
  • spiegazione guidata del programma
  • errori tipici
  • possibili estensioni

Attività 0: prima di programmare, capire bene il testo

Prima ancora di partire con la costruzione del circuito e la programmazione iniziamo con l’Attività 0: la comprensione del testo tecnico, è un problema che si riscontra sempre, soprattutto nella comprensione del testo dell’esame di maturità.

Molto spesso gli studenti si bloccano non perché non sanno programmare, ma perché il testo della prova appare lungo, denso e complesso.
Per questo motivo, prima di scrivere codice, è fondamentale allenarsi a:

  • riconoscere ingressi, uscite, sensori e attuatori
  • distinguere dati misurati e condizioni logiche
  • individuare la sequenza del processo
  • separare la parte hardware dalla parte software
  • … e non ultimo progettare in modo ordinato

Pubblicazione delle soluzioni

Le attività saranno pubblicate progressivamente e in ogni attività lo studente troverà un esercizio aggiuntivo di complessità leggermente superiore rispetto a quello proposto.

Ogni esercizio sarà corredato da diagramma di flusso e codice Mermaid per replicare il diagramma di flusso.

Per favorire il ragionamento autonomo, la soluzione completa verrà resa disponibile dopo qualche giorno dalla pubblicazione dell’esercizio, così da lasciare agli studenti il tempo di provare davvero.

Nota importante

Questa serie di attività è attualmente in costruzione. Nel momento stesso in cui pubblico questo post sto ancora sviluppando e affinando le singole esercitazioni; per questo vi chiedo un po’ di pazienza e comprensione se dovessero essere presenti alcune imprecisioni o aspetti da migliorare. Ogni segnalazione, osservazione o suggerimento sarà quindi molto utile per rendere le schede di lavoro più chiare, efficaci e funzionali.

Naturalmente, quanto propongo non ha la pretesa di esaurire tutti i contenuti necessari per affrontare l’Esame di Stato. Si tratta di una mia selezione di attività, costruita a partire dagli argomenti ricorrenti nelle prove degli anni passati e pensata soprattutto per studenti con competenze di livello medio-base. Per questo motivo le esercitazioni possono essere integrate, adattate o modificate in base alle esigenze della classe, al livello di preparazione degli studenti e alle scelte didattiche del docente.

Di seguito trovate l’indice della serie, che al momento può essere considerato una versione beta, anche se con ogni probabilità resterà molto vicino alla struttura definitiva.
Questa pagina verrà aggiornata progressivamente, aggiungendo di volta in volta i link diretti alle singole esercitazioni.

Indice delle esercitazioni che verranno pubblicate

  • Esercitazione 1 – Pulsante singolo con antirimbalzo, doppio clic, pressione lunga e timeout
  • Esercitazione 2 – Tastiera 4×4 non bloccante con codice di accesso e feedback di errore
  • Esercitazione 3 – Scheduler cooperativo con tre task e supervisione dei tempi
  • Esercitazione 4 – Macchina a stati per un ciclo automatico con START, pausa, allarme e reset
  • Esercitazione 5 – Acquisizione analogica calibrata, filtrata e convertita in grandezza fisica
  • Esercitazione 6 – Controllo a finestra con isteresi, allarme latched e reset
  • Esercitazione 7 – Confronto tra filtro a media mobile e filtro esponenziale
  • Esercitazione 8 – Campionamento temporizzato con array di struct: tempo, valore e stato
  • Esercitazione 9 – Analisi statistica di una sequenza con rilevamento anomalie
  • Esercitazione 10 – Buffer circolare con trend, velocità di variazione e soglia dinamica
  • Esercitazione 11 – Matrice bidimensionale per organizzare campioni di più sensori nel tempo
  • Esercitazione 12 – Frame buffer per matrice LED: icone, animazioni e scorrimento testo
  • Esercitazione 13 – Conteggio impulsi con interrupt e validazione evento
  • Esercitazione 14 – Misura di periodo, frequenza, duty cycle e tempo alto di un segnale PWM
  • Esercitazione 15 – Encoder rotativo con menù parametrico semplificato
  • Esercitazione 16 – Funzioni con parametri passati per indirizzo e restituzione di più risultati
  • Esercitazione 17 – Ordinamento di misure e scambio di valori tramite puntatori
  • Esercitazione 18 – Macchina a stati per un menù su display LCD
  • Esercitazione 19 – Parser di comandi seriali con parametri e risposta strutturata
  • Esercitazione 20 – Mini progetto finale: stazione di monitoraggio completa

Quale sarà la periodicità delle attività? Probabilmente giornaliera, da domani o lunedì prossimo.

Siete pronti per ripassare? 🙂

Arduino – Interrupts – lezione 2

Ormai sono passati diversi anni da quando scrissi il primo post sull’uso degli interrupt e durante gli anni a scuola puntualmente svolgo diversi esercizi, pertanto ho deciso di riprendere ed ampliare quanto detto nel precedente post e sicuramente più avanti ci saranno occasioni per aggiungere ulteriori esempi.

La maggior parte dei microcontrollori permette la gestione degli interrupt. Possiamo pensare all’intterrupt come a un sistema che ci permette di rispondere agli eventi “esterni” mentre facciamo qualcos’altro.

Riprendo quindi il post: Appunti su Arduino: interrupts da cui partire per comprendere cosa sono e come usare gli interrupts con Arduino UNO e con questa lezione voglio proporre alcuni esempi in modo che l’argomento spero possa essere più chiaro.

Per comprendere meglio cos’è un interrupt immaginate la seguente situazione:

tornate a casa dopo la scuola e desiderate cucinarvi un piatto di spaghetti. Fate bollire l’acqua ed inserite gli spaghetti in pentola fissando un tempo di 8 minuti, aspettate che gli spaghetti cucinino e poi mangiate. Possiamo paragonare questa azione all’esecuzione del codice nel loop() di Arduino.

In modo diverso potreste avviare un timer che scade ad 8 minuti e in questo intervallo di tempo potreste guardate il notiziario in TV. Quando il timer suona interromperà (interrupt) la vostra visione che vi ricorderà che dovrete porre attenzione alla pasta in pentola. Quindi verrà eseguito un interrupt (il suono del timer) che permetterà di eseguire il programma: “scola la pasta”.

In altro modo, riprendendo ciò che avevo scritto in passato:

Supponete di dover rilevare lo stato di alcuni sensori esterni. All’interno del loop() tutte le istruzioni sono eseguite in modo sequenziale e quindi anche la rilevazione del cambiamento di stato dei sensori collegati ad Arduino avviene in modo sequenziale. Supponete di dover eseguire il controllo della variazione di stato di 3 sensori, il vostro sketch eseguirà il controllo sul primo sensore, sul secondo e poi sul terzo.

Supponete che tutti i sensori si trovino al medesimo stato iniziale che chiameremo S1 e che il controllo di Arduino sia in un determinato istante sul secondo sensore, potrebbe capitare nello stesso istante una variazione repentina di stato sul primo sensore che passa da S1 a S2 e poi a S1, Arduino non si accorgerà di nulla perché la sua attenzione è sul secondo sensore; ecco che in questa situazione potrebbe essere utile l’utilizzo degli interrupt.

In ambito elettronico possiamo avere due tipi di interrupt:

  • Interrupt hardware: si verificano in risposta ad un evento esterno, come un pin che assume uno stato HIGH o LOW
  • Interrupt software: si verificano in risposta a un’istruzione software.

In questo post ci concentreremo sugli interrupt hardware.

Sulla scheda Arduino UNO, Nano, Mini e altre schede basate sul microcontrollore ATmega328 due sono i pin su cui realizzare un interrupt hardware:

pin digitale 2: interrupt 0
pin digitale 3: interrupt 1

Mentre per altre schede Arduino:

  • Uno WiFi Rev.2, Nano Every
    tutti i pin sono utilizzabili per l’interrupt
  • Mega, Mega2560, MegaADK
    2, 3, 18, 19, 20, 21
  • Micro, Leonardo e schede basate sull’ATmega32u4
    0, 1, 2, 3, 7
  • Zero
    tutti i pin eccetto il 4
  • Tutta la famiglia MKR
    0, 1, 4, 5, 6, 7, 8, 9, A1, A2
  • Nano 33 IoT
    2, 3, 9, 10, 11, 13, 15, A5, A7
  • Nano 33 BLE, Nano 33 BLE Sense
    tutti i pin
  • Due
    tutti i pin
  • 101
    tutti i pin e solo i pin 2, 5, 7, 8, 10, 11, 12, 13 lavorano in modalità CHANGE

Ma come funziona un interrupt?
In estrema sintesi quando si verifica l’interrupt (interruzione), il microcontrollore salva il suo stato di esecuzione attuale ed esegue una piccola porzione di codice che l’utente desidera venga eseguita in presenza di un interrupt, questa porzione di codice prende anche il nome di: interrupt handler o interrupt service routine (in italiano gestore di interrupt o routine di servizio interrupt).

Il programmatore quindi definisce il codice del gestore di interrupt che deve essere eseguito quando si verifica un particolare interrupt all’interno del programma stesso e per fare ciò in Arduino, come indicato nella mia precedente lezione, utilizziamo una funzione chiamata attachInterrupt():

attachInterrupt(digitalPinToInterrupt(PIN), ISR, modo);
  • digitalPinToInterrupt(PIN)
    è la funzione che consente di convertire il numero del pin su cui effettuare l’interrupt con il numero dell’interrupt quindi nel caso di Arduino UNO si avrà:
    digitalPinToInterrupt (2) > 0
    digitalPinToInterrupt (3) > 1
    PIN è quindi il pin abilitato all’interrupt, che indica al microcontrollore qual è il PIN da monitorare e come indicato nell’elenco sopra Il PIN dipende dal microcontrollore utilizzato.
  • ISR è la porzione di codice che deve essere eseguito se l’interrupt viene attivato
  • modo
    è il tipo di trigger che attiva l’interrupt che, come indicato nella precedente lezione, può essere di 4 tipi:

    • LOW l’interrupt viene eseguito quando il livello del segnale è basso
    • CHANGE l’interrupt viene eseguito quando avviene un cambiamento di stato sul pin
    • RISING l’interrupt viene eseguito quando si passa da un livello LOW ad un livello HIGH
    • FALLING l’interrupt viene eseguito quando si passa da un livello HIGH ad un livello LOW

Nota: all’interno della funzione utilizzata in attachInterrupt:

  • delay() non funziona;
  • il valore restituito dalla funzione millis() non verrà incrementato.
  • i dati seriali ricevuti durante l’esecuzione della funzione di interrupt possono essere sbagliati.
  • qualsiasi variabile modificabile all’interno della funzione attached (chiamata all’interno attachInterrupt) devono essere dichiarare come volatili.
  • le funzione non può avere parametri di ingresso

Esempio 01

Realizziamo uno sketch che al verificarsi di un interrupt sul pin 2 cambia lo stato in cui si trova il LED sulla scheda di Arduino

Circuito

/*
   Prof. Michele Maffucci
   Data 03.03.2021

   Oggetto: utilizzo degli interrupt

   Esempio 01

*/

int pinLed = 13;
volatile int stato = LOW;
int pulsante = 2;

/* dichiariamo volatile la variabile
  state usata nella funzione usata all'interno di attachInterrupt */

void setup()
{
  pinMode(pinLed, OUTPUT);       // definiamo pin output
  pinMode(pulsante, INPUT);      // pulsante collegato al pin 2
  attachInterrupt(digitalPinToInterrupt(2), cambiaStato, FALLING);

  // usiamo l'interrupt 0 che è associato al pin digitale 2
  // attachInterrupt chiamerà la funzione collegata, cambiaStato
  // il modo per la rilevazione del cambiamento di stato
  // sarà di tipo: FALLING
  // cioè l’interrupt viene eseguito quando si passa
  // da un livello HIGH ad un livello LOW

}

void loop()
{
  digitalWrite(pinLed, stato);
  // il pin digitale 13 viene impostato a "state"
  // che può essere LOW o HIGH
  // all'avvio di Arduino il LED sarà spento
}

void cambiaStato()
// la funzione cambiaStato() esegue la funzione NOT di "stato" cioè
// se stato = LOW viene cambiato in HIGH, se stato = HIGH viene cambiato in LOW

{
  stato = !stato;
}

quindi il cambiamento di stato avviene solo e soltanto se cambiamo lo stato sul pin 2.

Non si confonda l’azione del cambiamento di stato del LED con l’azione che si potrebbe avere con un semplice controllo sul pin 2 fatto con un’istruzione IF: “se il pulsante è premuto allora cambia stato al LED”.
In questo caso il funzionamento è totalmente diverso, non è presente un’istruzione di controllo di flusso, ma solamente il cambiamento di stato su un pin di interrupt, quando presente questo cambiamento viene invocata la funzione cambiaStato().

Si noti che le variabili utilizzate nella routine di servizio interrupt devono sempre essere globali e volatili.
Nel nostro caso la variabile “stato” utilizzate nella routine di servizio interrupt che abbiamo chiamato cambiaStato() è una variabile di tipo globale e volatile.

Ma cosa vuol dire dichiarare una variabile volatile? Come si può notare la variabile “stato” non è presente all’interno del loop(). Se il compilatore si accorge che sono presenti variabili non usate nel setup() e nel loop(), allora in fase di compilazione, per risparmiare memoria queste variabili vengono cancellate.

Dichiarare quindi “stato” volatile garantisce che il compilatori non elimini questa variabile perchè ci servirà all’interno della routine di servizio interrupt che abbiamo chiamato cambiaStato().
Una variabile volatile indicherà al compilatore di non memorizzare il contenuto della variabile in uno dei registri del microcontrollore, ma di leggerlo quando necessario dalla memoria. Attenzione però che questa modalità di azione rallenterà l’elaborazione del programma, pertanto non bisogna mai rendere volatile ogni variabile, ma l’operazione è da fare solamente quando necessario.

In generale una variabile dovrebbe essere dichiarata come volatile solamente se è utilizzata sia all’interno dell’ISR (interrupt service routine, in italiano gestore di interrupt) che all’esterno dell’ISR.

Esempio 02

Per comprendere meglio quanto scritto nell’esempio precedente facciamo alcune modifiche allo sketch precedente, il circuito rimane invariato.

/*
   Prof. Michele Maffucci
   Data 03.03.2021

   Oggetto: utilizzo degli interrupt

   Esempio 02

*/

int pin = 13;
volatile int stato = LOW;
int pulsante = 2;

/* dichiariamo volatile la variabile
  state usata nella funzione usata all'interno di attachInterrupt */

void setup()
{
  pinMode(pin, OUTPUT);       // definiamo pin output
  pinMode(pulsante, INPUT);   // pulsante collegato al pin 2
  attachInterrupt(digitalPinToInterrupt(2), cambiaStato, FALLING);

  // usiamo l'interrupt 0 che è associato al pin digitale 2
  // attachInterrupt chiamerà la funzione collegata, cambiaStato()
  // il modo per la rilevazione del cambiamento di stato
  // sarà di tipo: FALLING
  // cioè l’interrupt viene eseguito quando si passa
  // da un livello HIGH ad un livello LOW

}

void loop()
{
  delay(5000); // intervallo di 5 secondi
}

void cambiaStato()
// la funzione cambiaStato() esegue la funzione NOT di "stato" cioè
// se stato = LOW viene cambiato in HIGH, se stato = HIGH viene cambiato in LOW

{
  stato = !stato;
  digitalWrite(pin, stato);
  // il pin digitale 13 viene impostato a "stato"
  // che può essere LOW o HIGH
  // all'avvio di Arduino il LED sarà spento
}

L’unica variazione al codice è stato l’inserimento di:

delay(5000); // intervallo di 5 secondi

che interrompe l’esecuzione del loop per 5 secondi.

Si noti però che non appena premiamo il pulsante avviene una modifica istantanea del codice indipendentemente dal delay di 5 secondi

Attenzione che il LED rosso poteva essere controllato internamente al loop, ma in questo caso il codice, eseguito sequenzialmente, avrebbe prima eseguito il blink del LED verde e successivamente controlla se il pulsante è premuto per poter accendere il LED verde, ciò non accade con l’uso dell’interrupt.

Esempio 03

Nell’esempio che segue potrete notare il ritardo con cui si manifesta la variazione di stato se il controllo viene effettuato all’interno del loop().

Circuito

/*
   Prof. Michele Maffucci
   Data 03.03.2021

   Oggetto: controllo di stato subordinato dai delay

   Esempio 03

*/

int pinRosso = 5; // pin a cui è connesso il LED rosso
int pinVerde = 4; // pin a cui è connesso il LED verde
int pulsante = 2;
int stato = LOW;

void setup()
{
  pinMode(pinRosso, OUTPUT);
  pinMode(pinVerde, OUTPUT);
  pinMode(pulsante, INPUT);   // pulsante collegato al pin 2
}

void loop()
{
  digitalWrite(pinVerde, HIGH);
  delay(3000);
  digitalWrite(pinVerde, LOW);
  delay(3000);

  if (digitalRead(pulsante)) {
    digitalWrite(pinRosso, !stato);
    stato = !stato;
  }
}

// Il controllo sul LED rosso potrà avvenire solamente dopo 6 secondi
// La pressione ripetuta del pulsante nei primi 6 secondi non modifica
// lo stato del LED fino a quando il controllo non giunge all'IF.

Esempio 04

Realizziamo ora un quarto sketch in cui abbiamo un LED verde che lampeggia controllato dal codice all’interno del loop ed un secondo LED rosso che cambia stato se premiamo il pulsante connesso al pin 2. Noterete che la pressione del pulsante, che varia lo stato del LED rosso, non influirà sulla normale esecuzione del loop().

Il circuito è il medesimo dell’esercizio precedente.

/*
   Prof. Michele Maffucci
   Data 03.03.2021

   Oggetto: utilizzo degli interrupt

   Esempio 04

*/

int pinRosso = 5;
int pinVerde = 4;
int pulsante = 2;
volatile int stato = LOW;

/* dichiariamo volatile la variabile
  state usata nella funzione usata all'interno di attachInterrupt */

void setup()
{
  pinMode(pinRosso, OUTPUT);
  pinMode(pinVerde, OUTPUT);
  pinMode(pulsante, INPUT);   // pulsante collegato al pin 2

  attachInterrupt(digitalPinToInterrupt(2), cambiaStato, FALLING);

  // usiamo l'interrupt 0 che è associato al pin digitale 2
  // attachInterrupt chiamerà la funzione collegata, cambiaStato()
  // il modo per la rilevazione del cambiamento di stato
  // sarà di tipo: FALLING
  // cioè l’interrupt viene eseguito quando si passa
  // da un livello HIGH ad un livello LOW

}

void loop()
{
  digitalWrite(pinVerde, HIGH);
  delay(3000);
  digitalWrite(pinVerde, LOW);
  delay(3000);
}

// Durante i 6 secondi di accensione e spegnimento del LED verde
// non saremo bloccati dai delay presenti nel loop, potremo modificare
// lo stato del LED rosso ogni volta nei 6 secondi di pausa del loop

void cambiaStato()
// la funzione cambiaStato() esegue la funzione NOT di "stato" cioè
// se stato = LOW viene cambiato in HIGH, se stato = HIGH viene cambiato in LOW

{
  stato = !stato;
  digitalWrite(pinRosso, stato);
  // il pin digitale 5 viene impostato a "stato"
  // che può essere LOW o HIGH
  // all'avvio di Arduino il LED sarà spento
}

Esempio 05

Vediamo ora un’altro esempio dove con due pulsanti controlliamo l’incremento e il decremento di una variabile che viene stampata sulla Serial Monitor.
Notate all’interno del loop() che viene implementato un blink lento e ll’incremento e il decremento della variabile non è influenzata dai delay() del blink, ma risulta immediata.

Circuito

/*
   Prof. Michele Maffucci
   Data 03.03.2021

   Oggetto: utilizzo degli interrupt

   Esempio 05

*/

volatile int valore = 10;

int pulsanteIncrementa = 2;
int pulsanteDecrementa = 3;

/* dichiariamo volatile la variabile
  state usata nella funzione usata all'interno di attachInterrupt */

void setup()
{
  Serial.begin(9600);

  pinMode(pulsanteIncrementa, INPUT);   // pulsante collegato al pin 2
  pinMode(pulsanteDecrementa, INPUT);   // pulsante collegato al pin 3

  attachInterrupt(digitalPinToInterrupt(2), incrementa, FALLING);
  attachInterrupt(digitalPinToInterrupt(3), decrementa, FALLING);

  // usiamo l'interrupt 0 e 1 che sono associati ai pin digitali 2 e 3
  // attachInterrupt chiamerà la funzione collegata, cambiaStato
  // il modo per la rilevazione del cambiamento di stato
  // sarà di tipo: FALLING
  // cioè l'interrupt viene eseguito quando vi è un passaggio da HIGH a LOW

}

void loop()
{
  Serial.println(valore);
}

void incrementa() {
  valore++;
}

void decrementa() {
  valore--;
}

Attenzione che durante le prove potreste notare qualche problema nella gestione dei pulsanti su cui dovrebbe essere effettuato un controllo di antirimbalzo, in questo caso però potrà essere realizzato solamente in modo hardware (in modo elettronico) e non per via software in cui viene richiesto un controllo del tempo di pressione del pulsante mediante il delay() o di calcoli di intervalli di tempo e tutto ciò non è permesso all’interno dell’interrupt service routine .

Buon coding a tutti 🙂

Appunti su Arduino: interrupts


Dopo l’estenuante periodo di fine anno scolastico passato tra interrogazioni, recuperi, esami di qualifica, esami di alternanza scuola lavoro, preparazioni tesine e svariate riunioni/scrutini… riesco a rimettere mani sui miei appunti realizzati durante l’anno scolastico e questa volta colgo l’occasione di rispondere al commento di Raffaele che vuole avere ulteriori informazioni sull’uso degli interrupt in Arduino:

salve sono un vecchio appassionato di elettronica (analogica )- Essendo che ci siamo dovuti convertire sui microcontrollori solo 5 mesi fà sono riuscito finalmente dopo aver tentato con i PIC ,sono passato ad Arduino
duemilaenove e finalmente ho incominciato a capirlo e sviluppare tutti i tipi di esempi che dava nel softwere, dal led ai motori passo passo
e i servo perchè il tipo di programmazzione e soprattutto molti esempi erano spiegati
pure in video e come dicevo sono riuscito a capirci qualcosa.Ovviamente nel mio caso
riuscivo a modificare quelli già presenti ma in sostanza ero abbastanza sodisfatto perchè su di uno sviluppavo le modifiche creandone dei nuovi. Adesso però mi sono fermato un pò perchè ho una richiesta da farle.Pur avendo letto e capito l’istruzione
interrupt cosa significa avendola vista in altri programmi qualè il suo scopo non riesco a capire come inserirla avrei bisogno quindi se possibile di qualche esempio o modo di capire. Ci conto molto

A risentirci e Grazie

Di seguito vi riporto la parte (provvisoria) del manuale che ho realizzato per i miei studenti che si riferisce agli interrupt (sto realizzando alcuni esempi pratici che chiarificano questa funzionalità), sono tratte dal reference on-line di Arduino.cc con alcune mie rielaborazioni.

Con il termine interrupt (interruzione) intendiamo un segnale asincrono che indica la necessità di “attenzione” da parte di una periferica collegata ad Arduino.
L’interrupt viene generato quando si verifica una variazione di stato su uno dei piedini di Arduino. Normalmente il microcontrollore esegue all’interno del loop() in modo sequenziale e ripetitivo le istruzioni in esso inserite, ma quando si verifica un interrupt viene interrotto il flusso delle istruzioni all’interno del loop() ed invocate altre routine (create dall’utente). Quando le routine terminano il flusso del programma prosegue normalmente.
L’utilizzo dell’interrupt è particolarmente utile quando abbiamo la necessità di gestitire in background alcune routine ovvero eseguire istantaneamente un’operazione nel caso si manifesti un evento asincrono esterno.

Capita spesso di avere la necessità di controllare lo stato di diversi pin di input, in questa situazione potrebbe capitare che il programma non si accorga del cambiamento di stato su uno dei pin, utilizzando gli interrupt evitiamo questo tipo di errore.

Continua a leggere