Assembliamo i push button sulla Breadboard
Step 1
Il tutorial (Tutorial 74HC595 v1) 8 led con uno Shift Register dovrebbe avervi guidato fino a questo schema.
Step 2
Aggiungiamo due pulsanti; per entrambi, da un lato avremo un cavetto connesso a Vcc e dall’altro lato avremo sia una resistenza da 10k che il cavetto verso un pin digitale di arduino, usate i pin 12 e 13. Le resistenze andranno poi connesse a GND. Fate attenzione però che il rail inferiore ha il potenziometro in serie, quindi non è un GND! Collegatevi invece al GND del rail superiore o a valle del potenziometro (come mostrato nello schema).
Lo sketch
//Connesso al Pin DS del 74HC595 int dataPin = 8; //Connesso al Pin ST_CP del 74HC595 int latchPin = 9; //Connesso al Pin SH_CP del 74HC595 int clockPin = 10; //connesso ad uno pulsante int button1Pin = 12; //connesso ad uno pulsante int button2Pin = 13; //flag di stato dei pulsanti bool b1Status=LOW; bool old_b1Status=LOW; bool b2Status=LOW; bool old_b2Status=LOW; void setup() { //configuriamo i 3 pin di output pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(button1Pin, INPUT ); pinMode(button2Pin, INPUT); } //procedura che ci consente di mandare da un singolo bit al 74HC595 void push(bool aBit) { digitalWrite(clockPin, 0); //mi assicuro che clock si LOW digitalWrite(latchPin, 0); //abbasso il latch per iniziare a programmare il 74HC595 digitalWrite(dataPin,aBit ); // alzo il pin data in base al bit corrente in aByte; digitalWrite(clockPin, 1); //alzo il clock -> abbiamo "spinto" il valore 1 nel primo registro del 74HC595. digitalWrite(dataPin, 0); //rimetto a zero il data pin e clock pin per non portare valori HIGH oltre questo punto. digitalWrite(clockPin, 0); digitalWrite(latchPin, 1); //latch HIGH per confermare gli output. } void loop() { b1Status=digitalRead(button1Pin); //leggiamo lo stato dei due pulsanti b2Status=digitalRead(button2Pin); if ( old_b1Status!=b1Status && b1Status== HIGH) //il primo pulsante è premuto, e non lo era prima push(1); if ( old_b2Status!=b2Status && b2Status== HIGH) //il secondo pulsante è premuto, e non lo era prima push(0); delay(100); //anti bounce minimale. old_b1Status=b1Status; old_b2Status=b2Status; }
Bounce, il rimbalzo del pulsante.
Cos’è il rimbalzo del push button?
In questo sketch abbiamo usato un delay(100) per evitare il rimbalzo del pulsante, ma di che cosa si tratta?
Molto semplice, il pulsante è un dispositivo fisico ed il suo comportamento non è ideale e va incontro sia a problemi di natura meccanica che fisica.
Ci sono problemi meccanici perché, per quanto sia veloce il movimento esercitato su di esso, i due contatti inizieranno dapprima toccandosi timidamente per poi stringersi via via più saldamente, e quando iniziano solo a toccarsi appena appena potrebbero dare un contatto estemporaneo che subito si interrompe; inoltre le due lamine, sfregandosi durante movimento, potrebbero sovrapporre parti leggermente ossidate che interrompono solo momentaneamente il contatto.
Bisogna anche ricordare che non si può andare contro la fisica, sappiamo come funziona il circuito aperto e come funziona il circuito chiuso, tuttavia abbiamo trascurato il transitorio in cui le correnti non sono continue, ma passano da un equilibrio ad un altro. Con correnti variabili, anche un semplice cavetto, presenterà una certa induttanza così come ogni componente interessato al cambiamento; semplificando, il nostro circuito si opporrà al cambio di equilibrio che non verrà raggiunto immediatamente bensì solo al termine di un concitato transitorio durante il quale le tensioni lette dal pin4 potrebbero oscillare attorno al livello logico che separa HIGH da LOW.
Debounce con delay()
Senza accorgimenti, il nostro Arduino nel leggere il potenziale sul pin potrebbe considerare ogni piccolo sbalzo del transitorio come una nuova pressione del tasto. Mettere un delay(100) è un modo molto semplice per evitare il problema. In pratica, ogni volta che rileviamo la pressione di un tasto ci blocchiamo per 100ms, al termine dei quali il transitorio sarà cessato.
Non è certo il modo migliore, ma forse il modo migliore per sperimentare cosa sia il bounce: commentate il delay e riprovate a premere i pulsanti; sono certo che questa volta non sempre ad una pressione corrisponderà un solo shift del registro!
Un lato negativo dell’uso di un delay per evitare il rimbalzo è che stiamo bloccando l’MCU per tutto il tempo del transitorio, precludendo così qualsiasi gestione “multitask” del dispositivo: se ad esempio dei sensori ci stanno mandando dei segnali, rischiamo di perderli. Vedremo nel prossimo sketch la tecnica corretta per gestire il debounce.
Debounce con millis()
Possiamo usare la funzione <code>millis()</code> per ignorare ogni pressione che avvenga troppo vicina ad una precedente senza mettere mai in pausa l’MCU.
Nello sketch sono state aggiunte due variabili, una che viene aggiornata con il tempo dell’ultima pressione valida ed una con il numero di millisecondi in cui si presume il transitorio si sia esaurito. Il tempo presunto è arbitrario, e dipende dal tipo di interruttore (o sensore) e dal tipo di utilizzo.
Ho usato un’unica gestione del transitorio per entrambi i pulsanti a dimostrare che non c’è bisogno di gestire il transitorio per ogni pulsante collegato ad arduino.
Ovviamente se gli input sono molto diversi o indipendenti, potrebbe essere opportuno differenziarne la gestione: immaginate di avere un interruttore a galleggiante che si chiude quando il livello di un serbatoio raggiunge un certo livello: è facile immaginare che per un tale interruttore il transitorio possa durare anche diversi secondi se non addirittura minuti visto che ogni movimento del liquido potrebbe far scattare ripetutamente l’interruttore. Se avete avuto macchine molto vecchie, vi ricorderete di quando la spia della riserva si accendeva in curva e si spegneva in rettilineo!
Sketch finale
Qui sotto vi riporto lo sketch finale con la gestione del debounce con l’uso di millis()
//Connesso al Pin DS del 74HC595 int dataPin = 8; //Connesso al Pin ST_CP del 74HC595 int latchPin = 9; //Connesso al Pin SH_CP del 74HC595 int clockPin = 10; //connesso ad uno pulsante int button1Pin = 12; //connesso ad uno pulsante int button2Pin = 13; //flag di stato dei pulsanti bool b1Status=LOW; bool old_b1Status=LOW; bool b2Status=LOW; bool old_b2Status=LOW; unsigned long lastDebounceTime = 0; // tempo dell'ultimo click unsigned long debounceDelay = 100; // millisecondi del transitorio void setup() { //configuriamo i 3 pin di output pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(button1Pin, INPUT ); pinMode(button2Pin, INPUT); } //procedura che ci consente di mandare un singolo bit void push(bool aBit) { digitalWrite(clockPin, 0); //mi assicuro che clock si LOW digitalWrite(latchPin, 0); //abbasso il latch per iniziare a programmare il 74HC595 digitalWrite(dataPin,aBit ); // alzo il pin data in base al bit corrente in aByte; digitalWrite(clockPin, 1); //alzo il clock -> abbiamo "spinto" il valore 1 nel primo registro del 74HC595. digitalWrite(dataPin, 0); //rimetto a zero il data pin e clock pin per non portare valori HIGH oltre questo punto. digitalWrite(clockPin, 0); digitalWrite(latchPin, 1); //latch HIGH per confermare gli output. } void loop() { b1Status=digitalRead(button1Pin); //leggiamo lo stato dei due pulsanti b2Status=digitalRead(button2Pin); if ( (millis() - lastDebounceTime) > debounceDelay && old_b1Status!=b1Status && b1Status== HIGH) //nessun pulsante è stato premuto di recente ed il primo pulsante è premuto, e non lo era prima { push(1); lastDebounceTime = millis(); } if ( (millis() - lastDebounceTime) > debounceDelay && old_b2Status!=b2Status && b2Status== HIGH) //nessun pulsante è stato premuto di recente ed il secondo pulsante è premuto, e non lo era prima { push(0); lastDebounceTime = millis(); } old_b1Status=b1Status; old_b2Status=b2Status; }