INPUT CON TASTIERA A MEMBRANA (KEYPAD)
Per svolgere il seguente esempio è necessario dotarsi di una tastiera a membrana.
Ogni tasto è inserito quindi in una matrice circuitale di righe e colonne. Quando viene premuto, due segnali (uno identifica la riga e l'altro la colonna) arrivano ad arduino che tramite una mappa dei caratteri è in grado di decodificarli nel simbolo corrispondente.
Il funzionamento di un Keypad a membrana è davvero semplice. Un tastierino 4×3 è composto da una matrice di pulsanti disposti in 4 righe e 3 colonne. Quando premiamo un pulsante della tastiera viene collegata elettricamente una riga con una colonna. Per capire quale pulsante premiamo, bisogna alimentare in sequenza le 3 colonne e contemporaneamente controllare il livello di tensione in ogni singola riga. Ogni colonna è continuamente impostata ad un livello logico alto (5V). Guardando la figura seguente il discorso potrà sembrarvi più chiaro:
Codice Sorgente A
Obiettivo progetto: Costruire un programma che mostri sul serial monitor il tasto del keypad che viene premuto.
soluzione:
Connettiamo i pin seguendo lo schema riportato:
Scriviamo il seguente programma.
/* ----------------------------------------------------------------------------------- Dimostra la lettura di un keypad a matrice a 12 pulsanti senza uso di librerie Scrive il carattere sulla monitor seriale ----------------------------------------------------------------------------------- */ const int numRows = 4; // numero di righe const int numCols = 3; // numero di colonne const int debounceTime = 20; // millisecondi necessari allo switch per diventare stabile // Mappa dei caratteri sul tastierino const char keymap[numRows][numCols] = { { '1', '2', '3' } , { '4', '5', '6' } , { '7', '8', '9' } , { '*', '0', '#' } }; // Definisco i pin connessi alle righe o alle colonne const int rowPins[numRows] = {8, 7, 6, 5};// Pin di arduino connessi ai pin 1,2,3 e 4 delle righe del keypad const int colPins[numCols] = {4, 3, 2}; // Pin di arduino connessi ai pin 5,6,7 delle colonne del keypad void setup() { Serial.begin(9600); for (int row = 0; row < numRows; row++) { pinMode(rowPins[row],INPUT); // Imposta i pun delle righe come input digitalWrite(rowPins[row],HIGH); // Imposta le righe a HIGH (inattiva) - abilita le resistenze di pull-ups } for (int column = 0; column < numCols; column++) { pinMode(colPins[column],OUTPUT); // Setta i pin delle colonne come input digitalWrite(colPins[column],HIGH); // Imposta le colonne a HIGH (inattiva) - abilita le resistenze di pull-ups } } void loop() { char key = getKey(); if( key != 0) { // se il carattere non è 0 allora ho premuto un pulsante Serial.print("Hai premuto: "); Serial.println(key); } } // funzione che restituisce il tasto premuto o 0 se nessun tasto è digitato char getKey() { char key = 0; // 0 indica nessun tasto premuto for(int column = 0; column < numCols; column++) { digitalWrite(colPins[column],LOW); // Attiva la colonna corrente per la lettura. for(int row = 0; row < numRows; row++) // Scorro tutte le righe cercano un tasto premuto { if(digitalRead(rowPins[row]) == LOW) // E' premuto il tasto { delay(debounceTime); // attendo che il segnale si stabilizzi (vedi annotazione) while(digitalRead(rowPins[row]) == LOW); // attendo che il tasto sia rilasciato key = keymap[row][column]; // Memorizzo quale sia il tasto premuto } } digitalWrite(colPins[column],HIGH); // Rendo inattiva la colonna. } return key; // restituisce il tasto premuto 0 altrimenti }
Annotazione: In presenza di un deviatore o un pulsante si verificano vari impulsi spuri tipici dei contatti meccanici. L'origine dei suddetti disturbi è dovuta al fatto che la lamina di contatto non garantisce, una volta commutata, un impulso immediatamente stabile. La semplice chiusura del contatto elettromeccanico genera frequentemente, al posto di un impulso preciso e continuo, un treno di impulsi più stretti e ripidi, dovuti sia agli effetti di rimbalzo della lamina di contatto del componente elettromeccanico che ad una degradazione delle capacità conduttive dei contatti stessi, dovuta ad esempio a sporcizia od ossidazione degli stessi. Un circuito anti-rimbalzo viene utilizzato in elettronica per produrre un solo impulso stabile anche in presenza di un ingresso che risulti elettricamente rumoroso.
La sua esecuzione produce il seguente output sul monitor serial
Nel caso si tratti di una tastiera a membrana 4x4.
Il programma diventa:
/* ----------------------------------------------------------------------------------- Dimostra la lettura di un keypad a matrice a 16 pulsanti senza uso di librerie Scrive il carattere sulla monitor seriale ----------------------------------------------------------------------------------- */ const int numRows = 4; // numero di righe const int numCols = 4; // numero di colonne const int debounceTime = 20; // millisecondi necessari allo switch per diventare stabile // Mappa dei caratteri sul tastierino const char keymap[numRows][numCols] = { { '1', '2', '3', 'A' } , { '4', '5', '6', 'B' } , { '7', '8', '9', 'C' } , { '*', '0', '#', 'D' } }; // Definisco i pin connessi alle righe o alle colonne const int rowPins[numRows] = {9, 8, 7, 6}; // Pin di arduino connessi ai pin 1,2,3 e 4 delle righe del keypad const int colPins[numCols] = {5, 4, 3, 2}; // Pin di arduino connessi ai pin 5,6,7 e 8 delle colonne del keypad void setup() { Serial.begin(9600); for (int row = 0; row < numRows; row++) { pinMode(rowPins[row],INPUT); // Imposta i pun delle righe come input digitalWrite(rowPins[row],HIGH); // Imposta le righe a HIGH (inattiva) - abilita le resistenze di pull-ups } for (int column = 0; column < numCols; column++) { pinMode(colPins[column],OUTPUT); // Setta i pin delle colonne come input digitalWrite(colPins[column],HIGH); // Imposta le colonne a HIGH (inattiva) - abilita le resistenze di pull-ups } } void loop() { char key = getKey(); if( key != 0) { // se il carattere non è 0 allora ho premuto un pulsante Serial.print("Hai premuto: "); Serial.println(key); } } // funzione che restituisce il tasto premuto o 0 se nessun tasto è digitato char getKey() { char key = 0; // 0 indica nessun tasto premuto for(int column = 0; column < numCols; column++) { digitalWrite(colPins[column],LOW); // Attiva la colonna corrente per la lettura. for(int row = 0; row < numRows; row++) // Scorro tutte le righe cercano un tasto premuto { if(digitalRead(rowPins[row]) == LOW) // E' premuto il tasto { delay(debounceTime); // attendo che il segnale si stabilizzi (vedi annotazione) while(digitalRead(rowPins[row]) == LOW); // attendo che il tasto sia rilasciato key = keymap[row][column]; // Memorizzo quale sia il tasto premuto } } digitalWrite(colPins[column],HIGH); // Rendo inattiva la colonna. } return key; // restituisce il tasto premuto 0 altrimenti }
Codice Sorgente B
Obiettivo progetto: Ottenere lo stesso risultato ottenuto con il sorgente A ma utilizzando la libreria keypad.
soluzione:
Per svolgere questo esempio occorre scaricare la libreria: keypad.zip. Per installarla occorre caricare il file zippato tramite il menu "Add Library", presente all'interno dell'IDE di Arduino, come mostrato nella figura sottostante.
Al termine dell'importazione i files contenuti in keypad.zip vengono ricopiati nella cartella C:\Users\<User>\Documents\Arduino\libraries. Se ci sono problemi nell'installazione occorre svuotare la cartella di libreria appena creata (Keypad) e ripetere l'operazione.
Le funzioni disponibili in tale libreria sono:
void begin(makeKeymap(userKeymap)):
inizializza il mappaggio simboli/connessioni interno con quello effettivo
implementato nel nostro progetto
char waitForKey(); Questa
funzione resta in attesa finchè non viene premuto un tasto. Attenzione: questa
istruzione blocca qualsiasi altro codice fino a che non viene premuto un tasto.
Pertanto non potremo far lampeggiare alcun LED, aggiornare un display LCD ne
altro ad eccezione delle istruzioni inserite in eventuali routine di interrupt.
char getKey(): restituisce il pulsante premuto. NO_KEY
altrimenti. Non determina alcun attesa per cui non rappresenta un punto di
blocco del nostro programma
KeyState getState(): ritorna lo
stato corrente del keypad. I 4 stati sono: IDLE, PRESSED,
RELEASED e HOLD.
boolean keyStateChanged() Nella versione 2.0 permette di
sapere se il keypad ha cambiato il suo stato. In altre parole posso testare
l'uso del keypad semplicemente quando trovo che il suo stato è cambiato in
PRESSED.
setHoldTime(unsigned int time): Setta il numero di
millisecondi di pressione del tasto prima che venga innescato lo stato HOLD.
setDebounceTime(unsigned int time): Setta il numero di
millisecondi di attesa necessari per considerare valida la pressione di un tasto
ed attivare l'evento keyEvent
addEventListener(keypadEvent):
Innesca un gestore eventi se il keypad viene utilizzato. Eccone un esempio di
utilizzo
keypad.addEventListener(keypadEvent);
...
void keypadEvent(KeypadEvent key)
{
switch (keypad.getState())
{
case PRESSED:
switch (key)
{
case '#': digitalWrite(ledPin,!digitalRead(ledPin));
break;
case '*': digitalWrite(ledPin,!digitalRead(ledPin));
break;
}
break;
case RELEASED:
switch (key)
{
case '*': digitalWrite(ledPin,!digitalRead(ledPin));
blink = false;
break;
}
break;
case HOLD:
switch (key)
{
case '*': blink = true;
break;
}
break;
}
}
Scriviamo ora il seguente codice che come si nota è decisamente più corto:
/* ----------------------------------------------------------------------------------- Esempio creato partendo dal codice di Alexander Brevig Dimostra l'utilizzo della libreria KeyPad ----------------------------------------------------------------------------------- */ #include <Keypad.h> const byte ROWS = 4; //4 righe const byte COLS = 3; //3 colonne char keys[ROWS][COLS] = { {'1','2','3'}, {'4','5','6'}, {'7','8','9'}, {'*','0','#'} }; byte rowPins[ROWS] = {8, 7, 6, 5}; // Pin di arduino connessi ai pin 1,2,3 e 4 delle righe del keypad byte colPins[COLS] = {4, 3, 2}; // Pin di arduino connessi ai pin 5,6,7 delle righe del keypad Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); void setup() { Serial.begin(9600); } void loop() { char key = keypad.getKey(); if (key != NO_KEY) { Serial.print("hai premuto: "); Serial.println(key); } }