Come utilizzare un keypad a 12 tasti (tastiera 4×3) con Arduino UNO

Gestire una tastiera 4×3 o comunemente tastierino numerico, permette di sviluppare applicazioni nelle quali l’utente ha la possibilità di interagire con Arduino UNO immettendo codici numerici, come ad esempio password per accedere ad aree riservate o impostare parametri per far funzionare in modo corretto un apparecchio.
La lettura dello stato dei pulsanti del tastierino avviene eseguendo un continuo scansionamento (pooling) degli stessi.

Un tipico 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.

Tastiera

Per capire quale sia il pulsante premuto, dovremmo alimentare in sequenza le 3 colonne e contemporaneamente controllare il livello di tensione in ogni singola riga. La figura seguente mostra cosa avviene quando premiamo uno dei tre pulsanti della prima riga.
Ogni colonna viene messa al livello logico alto (5Vdc) in modo continuo.

Tastiera1

Se premiamo il primo pulsante della prima riga (mentre è presente la tensione di 5Vdc nella prima colonna), otterremo che all’uscita della riga sarà disponibile un livello logico alto.
Questo avviene anche premendo il secondo ed il terzo pulsante rispettivamente quando la seconda e la terza colonna sono alimentate.

Quindi, cosa dobbiamo fare se vogliamo capire quale sia il pulsante premuto, dato che all’uscita della riga otteniamo lo stesso valore?
La logica del software deve considerare il valore della riga in base alla colonna alimentata.
In parole semplici, so che quando alimento la prima colonna ed in uscita dalla prima riga ho un valore logico alto il pulsante premuto è certamente il primo. Quando alimento la seconda colonna e ottengo anche in questo caso un valore logico alto in uscita dalla prima riga, capisco che il pulsante premuto è il numero 2, questo perché l’attivazione delle colonne deve avvenire sempre in modo univoco.

Questo procedimento deve essere fatto in modo ciclico per tutte le colone e per tutte le righe.
Quindi avendo quattro righe il codice deve alimentare la prima colonna e controllare l’uscita delle quattro righe per verificare dove si trova il livello logico alto (nel caso si prema un pulsante):

Tastiera3

Il circuito elettrico

Lo schema seguente mostra come collegare una comune tastiera 4×3 ad Arduino UNO.

Pin Arduino Pin Tastiera 4×3
 2  R1
 3  R2
 4  R3
 5  R4
 6  C1
 7  C2
 8  C3

Tastiera 4x3

 

Il codice di gestione

Il codice per poter gestire la tastiera 4×3 è riportato di seguito. Viene utilizzata la classe Serial per poter inviare al monitor seriale il valore del tasto premuto.

Possiamo notare che per ridurre i componenti necessari alla realizzazione del circuito, ho sfruttato la funzione che permette di attivare le resistenza di pull-up. Attivando questa funzione ottengo che sulle righe R1, R2, R3 e R4 è presente uno stato logico alto (dovuto appunto alle resistenze).
Per capire se un tasto è premuto, al contrario della teoria vista precedentemente, devo verificare quando la riga va a livello logico basso.
Per far ciò i livelli logici sulle colonne devono essere invertiti di segno nel senso che se nella teoria ponevo ciclicamente un livello logico alto, ora dovremmo metterne uno basso.

Tastiera 4x3

con l’attivazione delle resistenze di pull-up il circuito risulta in questo modo:

Pullup Tastiera 4x3

di conseguenza dato che i pin 2, 3, 4 e 5 sono degli ingressi, per capire se un pulsante è premuto dovremmo verificare che il livello logico sia basso in quanto la pressione di un pulsante permetterà di collegare la resistenza di pull-up a massa.

Ad esempio se in un dato istante sulle colonne imposto C1 livello basso, C2 livello alto, C3 livello alto e premo il primo pulsante della riga R1, sul pin2 di Arduino UNO avrò un livello logico basso e quindi capirò che il pulsante premuto è il numero 1 della tastiera 4×3.

 

Esempio tastiera 4x3

[c]

void setup()
{  
  Serial.begin(9600);
  //questi ingressi servono per collegare
  //le righe della tastiera
  //notare l’attivazione delle resistenze di pull-up
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
   
  //prima colonna
  pinMode(6, OUTPUT);
  //setto alto il livello
  //per disabilitare la colonna
  digitalWrite(6, HIGH);
 
  //seconda colonna
  pinMode(7, OUTPUT);
  //setto alto il livello
  //per disabilitare la colonna
  digitalWrite(7, HIGH);  
 
  //terza colonna
  pinMode(8, OUTPUT);
  //setto alto il livello
  //per disabilitare la colonna
  digitalWrite(8 , HIGH);
}

void loop()
{
  //antirimbalzo
  delay(40);
 
  //Seleziono la prima colonna
  digitalWrite(6, LOW);
  //Controllo il primo pulsante
  //della prima riga  
  if(digitalRead(2) == LOW)
    Serial.println("1");
  //Controllo il primo pulsante
  //della seconda riga
  if(digitalRead(3) == LOW)
    Serial.println("4");
  //Controllo il primo pulsante
  //della terza riga
  if(digitalRead(4) == LOW)
    Serial.println("7");
  //Controllo il primo pulsante
  //della quarta riga
  if(digitalRead(5) == LOW)
    Serial.println("*");
  //Deseleziono la prima colonna
  digitalWrite(6, HIGH);
 
  //Seleziono la seconda colonna
  digitalWrite(7, LOW);
  //Controllo il secondo pulsante
  //della prima riga  
  if(digitalRead(2) == LOW)
    Serial.println("2");
  //Controllo il secondo pulsante
  //della seconda riga
  if(digitalRead(3) == LOW)
    Serial.println("5");
  //Controllo il secondo pulsante
  //della terza riga
  if(digitalRead(4) == LOW)
    Serial.println("8");
  //Controllo il secondo pulsante
  //della quarta riga
  if(digitalRead(5) == LOW)
    Serial.println("0");
  //Deseleziono la seconda colonna
  digitalWrite(7, HIGH);
 
  //Seleziono la prima colonna
  digitalWrite(8, LOW);
  //Controllo il primo pulsante
  //della prima riga  
  if(digitalRead(2) == LOW)
    Serial.println("3");
  //Controllo il primo pulsante
  //della seconda riga
  if(digitalRead(3) == LOW)
    Serial.println("6");
  //Controllo il primo pulsante
  //della terza riga
  if(digitalRead(4) == LOW)
    Serial.println("9");
  //Controllo il primo pulsante
  //della quarta riga
  if(digitalRead(5) == LOW)
    Serial.println("#");
  //Deseleziono la prima colonna
  digitalWrite(8, HIGH);
}
[/c]

Il codice funziona ma ha un problema che rende quasi inutilizzabile la tastiera. Infatti aprendo il serial monitor e premendo un tasto avremmo una serie di letture. Questo perché nel momento in cui pigiamo il tasto, il codice è talmente veloce che riesce a scansionare il pulsante premuto varie volte:

Pressione Pulsante Tastiera 4x3

Esistono più soluzioni per ovviare a questa situazione, la più banale è quella di far eseguire ad Arduino un ciclo infinito fintanto che il pulsante è premuto.
In questo modo avremmo che sul serial monitor verrà inviato solo un carattere anche tenendo a lungo premuto il pulsante relativo:

[c]

void setup()
{  
  Serial.begin(9600);
  //questi ingressi servono per collegare
  //le righe della tastiera
  //notare l’attivazione delle resistenze di pull-up
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
   
  //prima colonna
  pinMode(6, OUTPUT);
  //setto alto il livello
  //per disabilitare la colonna
  digitalWrite(6, HIGH);
 
  //seconda colonna
  pinMode(7, OUTPUT);
  //setto alto il livello
  //per disabilitare la colonna
  digitalWrite(7, HIGH);  
 
  //terza colonna
  pinMode(8, OUTPUT);
  //setto alto il livello
  //per disabilitare la colonna
  digitalWrite(8 , HIGH);
}

void loop()
{
  //antirimbalzo
  delay(40);
 
  //Seleziono la prima colonna
  digitalWrite(6, LOW);
  //Controllo il primo pulsante
  //della prima riga  
  if(digitalRead(2) == LOW)
  {
      Serial.println("1");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(2) == LOW)
        ;
  }
    
  //Controllo il primo pulsante
  //della seconda riga
  if(digitalRead(3) == LOW)
  {  
      Serial.println("4");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(3) == LOW)
        ;
  }
    
  //Controllo il primo pulsante
  //della terza riga
  if(digitalRead(4) == LOW)
  {  
      Serial.println("7");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(4) == LOW)
        ;
  }
  //Controllo il primo pulsante
  //della quarta riga
  if(digitalRead(5) == LOW)
  {  
      Serial.println("*");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(5) == LOW)
        ;
  }
  //Deseleziono la prima colonna
  digitalWrite(6, HIGH);
 
  //Seleziono la seconda colonna
  digitalWrite(7, LOW);
  //Controllo il secondo pulsante
  //della prima riga  
  if(digitalRead(2) == LOW)
  {
      Serial.println("2");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(2) == LOW)
        ;
  }
  //Controllo il secondo pulsante
  //della seconda riga
  if(digitalRead(3) == LOW)
  {
      Serial.println("5");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(3) == LOW)
        ;
  }
  //Controllo il secondo pulsante
  //della terza riga
  if(digitalRead(4) == LOW)
  {
      Serial.println("8");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while      
      while(digitalRead(4) == LOW)
        ;
  }
  //Controllo il secondo pulsante
  //della quarta riga
  if(digitalRead(5) == LOW)
  {
      Serial.println("0");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(5) == LOW)
        ;
  }
  //Deseleziono la seconda colonna
  digitalWrite(7, HIGH);
 
  //Seleziono la prima colonna
  digitalWrite(8, LOW);
  //Controllo il primo pulsante
  //della prima riga  
  if(digitalRead(2) == LOW)
  {
      Serial.println("3");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(2) == LOW)
        ;
  }
  //Controllo il primo pulsante
  //della seconda riga
  if(digitalRead(3) == LOW)
  {
      Serial.println("6");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(3) == LOW)
        ;
  }
  //Controllo il primo pulsante
  //della terza riga
  if(digitalRead(4) == LOW)
  {
      Serial.println("9");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(4) == LOW)
        ;
  }
  //Controllo il primo pulsante
  //della quarta riga
  if(digitalRead(5) == LOW)
  {
      Serial.println("#");
      
      //fintanto che il pulsante è premuto non
      //uscire dal ciclo while
      while(digitalRead(5) == LOW)
        ;
  }
  //Deseleziono la prima colonna
  digitalWrite(8, HIGH);
}
[/c]

Il codice può essere decisamente più compatto utilizzando dei cicli for e delle matrici di variabili, o addirittura utilizzando una libreria esterna già pronta. Questo articolo serve soprattutto a quelle persone che vogliono capire cosa succede effettivamente quali sono i collegamenti interni della tastiera e quali sono le problematiche nella gestione software.

tastiera 4x3

 

WP_20140718_002