Nel nostro precedente articolo “Arduino digitalRead, input digitale e pulsanti” abbiamo visto il funzionamento della funzione digitalRead e di come lavora.
In quell’articolo abbiamo ignorato alcune problematiche che ci può dare il pulsante.
Nei nostri progetti, soprattutto se non proveniamo dal mondo elettronico, pensiamo che se c’è un errore è colpa del software.
Questo è normalissimo, poiché per chi viene dal mondo della programmazione dei computer come me, ha a che fare con dell’hardware ultra-testato.
Inoltre molte volte ci dimentichiamo (o almeno a me succede spesso) che con arduino stiamo letteralmente programmando l’hardware mentre in un PC, generalmente, si programma una astrazione (di livello più o meno basso).
Questa astrazione ci permette di concentrarci solo sul software e ignorando completamente su che macchina lavora il nostro programma.
Tutto questo discorso è stato fatto per dirvi che ci possono essere errori anche nella elettronica del nostro progetto. Un errore di cui parleremo oggi, in particolare, è il problema che affligge tutti i pulsanti, soprattutto se vengono con furore dalla cina! Questo problema è noto con il nome di “Button Boucing” ovvero il rimbalzo del pulsante.
Questo errore lo si può notare molto quando lavoriamo con gli interrupt generati da eventi. La dinamica di questo tipo di errore è semplicissima, quando premiamo il pulsante e successivamente lo rilasciamo, internamente, il pulsante inizia a rimbalzare. Questo genera un ulteriore segnale che viene rilevato dalla nostra scheda e che potrebbe compromettere il funzionamento progetto.
Analisi del problema
Analizziamo il problema facendo una prova! Facciamo finta di essere impreparati al boucing e prendiamo:
- La scheda Arduino (Clicca per vedere quali pin della tua scheda accettano gli Interrupt);
- Un pulsante;
- Una resistenza 10 Kohm
- Una breadboard
- Fili
Una volta preso tutto il materiale, replichiamo il seguente schema.
Ora eseguiamo il nostro codice apparentemente corretto!
#define INTERRUPT_PIN 2 #define BOUNCE_DELAY 50 /* volatile è un qualificatore della variabile che serve per dire al compilatore * che ogni volta che la variabile viene usata, essa viene presa dalla RAM e non * dai registri. */ volatile byte val = 0; unsigned long lastTime = 0; /*Funzione che viene riachiamata ogni volta che sul pin 2 viene registrato un * fronte di salita del segnale */ void count() { val++; } /* attachInterrupt(): la funzione esegue la funzione registrata al relativo evento * nel nostro caso, la funzione avviata si chiama count, l'evento è un fronte di * salita del segnale sul pin 2. * * ATTENZIONE! Non tutti i pin della scheda sono utilizzabili per la gestione degli * eventi, nel caso della scheda Arduino Uno questi eventi si possono registrare sul pin * 2 e 3 ma su altre schede questi pin potrebbero essere diversi */ void setup() { pinMode(INTERRUPT_PIN, INPUT_PULLUP); Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), count, RISING); } void loop() { Serial.println(val); }
Il codice sopra descritto, aumenta il contatore di 1 ogni volta che viene premuto il nostro pulsante. Come possiamo notare, questo non accade, poiché a causa del rimbalzo del pulsante, la variabile viene incrementata di più di una singola unità.
Questo problema lo si può risolvere in due differenti modi: il primo è quello di comprare un componente più affidabile (ATTENZIONE l’affidabilità non permane nel tempo) ma questo non ci piace!
So io cosa vi piace! Vi piace sistemare la cosa in modo veloce ed economico, quindi dovremo scrivere un codice che tenga in considerazione anche questo problema!
Soluzione del problema
Visto che generalmente questi rimbalzi durano tra i 20 e i 30 millisecondi quindi una possibile soluzione potrebbe essere quella di ignorare tutti gli eventi provenienti dal pulsante in quel frangente. Infatti guardando la nuova funzione count() possiamo notare che è stato aggiunto un controllo che ha proprio questo compito.
#define INTERRUPT_PIN 2 #define BOUNCE_DELAY 50 /* volatile è un qualificatore della variabile che serve per dire al compilatore * che ogni volta che la variabile viene usata, essa viene presa dalla RAM e non * dai registri. */ volatile byte val = 0; unsigned long lastTime = 0; /* millis() restituisce il tempo in millisecondi che è trascorso dal momento * della accensione della scheda. */ void count(){ if((millis()-lastTime) > BOUNCE_DELAY){ val++; lastTime = millis(); } } /* attachInterrupt(): la funzione esegue la funzione registrata al relativo evento * nel nostro caso, la funzione avviata si chiama count, l'evento è un fronte di * salita del segnale sul pin 2. * * ATTENZIONE! Non tutti i pin della scheda sono utilizzabili per la gestione degli * eventi, nel caso della scheda Arduino Uno questi eventi si possono registrare sul pin * 2 e 3 ma su altre schede questi pin potrebbero essere diversi */ void setup() { pinMode(INTERRUPT_PIN, INPUT_PULLUP); Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), count, RISING); } void loop() { Serial.println(val); }
Ovviamente questa soluzione come tutte le soluzioni che possiamo prendere, non elimina completamente questo problema ma lo riduce al minimo. Bisogna fare delle prove e testare il programma affinché si riesca a trovare un BOUNCE_DELAY ottimale, che non sia né troppo grande (rischio perdita di eventi) né troppo piccolo (rischio che non tutti i rimbalzi vengano ignorati).
Come sempre vi invito a scrivere commenti se avete qualche problema o dubbio e per farmi sapere se avete tolto tutti i rimbalzi dai vostri progetti.
Ma se si azzera prima di ripetere il percorso ?
dimezzi il bouncing avendo risultati strani in casi di bouncing dispari
Buongiorno
Ci sarebbe una soluzione hardware che elimina completamente qualsiasi rimbalzo
anche di tasti comprati a un euro al chilo.
Servirebbero però un interruttore a tre poli (NO-C-NC) e un doppio NAND schmitt trigger (CD 4093)
Forse un pò troppo “ingombrante” per i nostri scopi.
sempre risolto con un piccolo condensatore da 100n in parallelo al pulsante che chiude a massa.