Keypad 4x4 en una sola conexión

En esta nota traeré una opción sencilla para leer un teclado de 16 teclas (4x4) mediante una sola conexión, es decir, no necesitamos realizar un barrido matricial de 4x4 que requiere un puerto completo de 1byte para el control.
En este caso la lectura del teclado se realiza mediante un canal del ADC del microcontrolador.
El método utilizado es similar a un DAC ponderado con resistencias pero en sentido inverso, en nuestro caso según la configuración de las resistencias en el teclado tendremos un valor diferente de resistencia que actuara con un pulldown como un divisor resistivo entregándonos un valor de tensión diferente para cada tecla que luego sera leído por el ADC y canalizado por firmware para representar cada una de las 16 teclas.

El teclado 4x4 matricial podremos comprenderlo mejor en la siguiente imagen:



Como podemos ver en la imagen de arriba tenemos una matriz de 4x4 dividida en 4 filas y 4 columnas, donde cada intersección sera un botón que al presionarlo unirá esa intersección de la fila y la columna en cuestión. 


En este caso al presionar el botón que se encuentra señalizado de color rojo, podremos ver como le corresponde la fila y la columna en donde el botón hace intersección.
En el método estándar de lectura de teclado se utiliza un 1byte entero para realizar un barrido de columnas y en función al barrido se testea que fila ha cambiado de estado.
En nuestro caso realizamos un arreglo de resistencias que veremos en el siguiente circuito.



Como podremos ver tenemos un arreglo de resistencias que se encuentran conectadas entre si y que en función de cada botón se sumara un valor diferente de resistencia que a su vez se encuentra conectado a R4 que conforma un pull down pero a la vez la resistencia resultante del botón presionado junto a la resistencia R4 conforman un divisor de tensión resistivo, ya que como podremos ver hay una toma de tensión que sera de 5V, quedando así, el nodo de salida que ingresara al ADC del MCU.



En esta nueva imagen podemos ver el primer ejemplo donde presionábamos la misma tecla pero en este caso podremos analizar el recorrido de la corriente desde los +5V hasta el GND pasando por el nodo que sera la salida del divisor de tensión.
En este ejemplo tenemos R1=4k7, R6=1k, R5=1k y R4=10, si sumamos los valores (despreciando la impedancia de contacto del botón), tendremos R1+R5+R6=6700R si aplicamos la ecuación de divisor de tensión suponiendo Vdd=5V, Vsalida=Vdd*(R4/((R1+R5+R6)+R4))=5V*(10k/(6k7+10k))=5V*0.598=2.99V
Entonces en salida tendremos al rededor de 3V que ingresaran en el ADC para luego ser procesados en el firmware.
De esta misma manera realizaremos la combinación de las 16 tecas y los 16 valores de tensión que nos dará el divisor resistivo.



Aquí podremos ver el resto del circuito que en este caso lo he realizado con este microcontrolador porque lo he probado en protoboard y es el que tenia a mano, pero podríamos usar un MCU de 8 o 14 pines y nos alcanzaría para el teclado y alguna otra función siempre que cuente con ADC claramente.

En este gif he realizado una captura del simulador Proteus para que sea mas sencillo de comprender, pero después lo veremos en el vídeo.


Antes de analizar el programa, hay que mencionar que para que este proyecto funcione, hay que tomar los 16 valores de cada tecla antes de asignarle un valor en el programa, es decir, nosotros tendremos por ejemplo para el numero 6 del teclado 3V entonces cuando el ADC lea esos 3V debemos asignarle el valor 6, ya que no es la forma en la que el microcontrolador relaciona la tensión con la tecla.
Para ello, debemos montar el circuito tal cual lo vamos a utilizar después, y presionar tecla por tecla, en el primer renglón del LCD saldrá el valor del ADC (en nuestro caso de 0 a 255, ya que esta trabajando en 8bit).
Una vez que tenemos los 16 valores de cada tecla, podremos asignar cada valor.
Pero recordemos que este es un sistema analógico, por ende, la tensión puede variar, porque simplemente puede variar la tensión de la fuente o algún otro componente.
Entonces para corregir la posible variación de tensión de cada botón, debemos definir un rango para cada asignación. 
En nuestro ejemplo medimos 2.99V para la tecla 6 pero podría ir desde 2.90 hasta 3.10V y ese rango es necesario incluirlo en el programa para que el sistema tenga tolerancia y no se asignen valores erráticos.
Para ello realizaremos un arreglo de vectores.

  1. #include <16F883.h>
  2. #device adc=8
  3. #use delay(int=4000000)
  4. #include <LCD.C>
  5. #define LED PIN_A1
  6. void main(){
  7.    setup_adc_ports(sAN0|VSS_VDD);
  8.    setup_adc(ADC_CLOCK_DIV_2);
  9.    lcd_init();
  10.    int i;
  11.    int8 salida;
  12.    char caracter;
  13.    const int keyValueMinor[17]=
  14. {0  ,54 , 56, 59, 62, 68, 73, 77, 83, 92,102,112,124,150,170,200,240};
  15.    const int keyValueMajor[17]=
  16. {53 ,56 , 58, 61, 67, 72, 76, 82, 90,100,110,120,132,160,190,220,255};
  17.    const char   KeySimbols[17]=
  18. {'_','D','#','0','*','C','9','8','7','B','6','5','4','A','3','2','1'};  
  19.    while(true){
  20.       set_adc_channel(0);
  21.       salida=read_adc();
  22.       delay_us(30);
  23.       for(i=0;i<18;i++){
  24.          if(salida>keyValueMinor[i]&&salida<keyValueMajor[i])
  25.             caracter=KeySimbols[i];
  26.          delay_ms(1);
  27.       }
  28.       lcd_gotoxy(1,1);
  29.       printf(lcd_putc,"Valor: %3u",salida);
  30.       lcd_gotoxy(1,2);
  31.       printf(lcd_putc,"Caracter: %c",caracter);
  32.       output_toggle(LED);
  33.    }
  34. }

Explicare el núcleo del programa ya que el resto es configuración y declaración.
El núcleo del programa se encuentra definido por el lazo for de iteración que va hasta 17 (cuando es 18 sale), pero porque no va hasta 16?, porque cuando no presionamos ningún botón la salida del divisor resistivo tendrá un valor de tensión por defecto, entonces ese valor para el teclado en reposo sera nuestro valor 17.
Tenemos 3 vectores que funcionaran a la par, es decir los tres vectores tendrán siempre el mismo indice que sera a la vez el valor de iteración del lazo for.
keySimbols es el vector que aloja los valores del teclado (los números, letras o símbolos) y luego tenemos otros dos vectores, keyValueMinor y keyValueMajor que serán los dos vectores que comentaba antes, con los valores máximos y mínimos de tolerancia del valor leído. 
Como podremos ver en la practica el número 6 se encuentra entre 102 y 110 (estos valores están en decimal de 0 a 255, que son los valores que entrega el ADC).
Entonces si analizamos el indice 10 del for (cuando este itere hasta llegar al 10) tendremos una pregunta mediante el if, que sera si el valor del adc (nombrado salida) es menor que keyValueMajor[10] y menor que keyValueMinor[10], entonces asignaremos a nuestra variable de salida (carácter) el valor keySimbols[10] que corresponde al numero 6. 

  1.          if(106>102&&106<110)
  2.             caracter=6;

Luego en el programa mostraremos los valores en el LCD, en el primer renglón el valor del ADC y en el segundo valor el carácter que nos devuelve el lazo for.














6 comentarios:

  1. Felicitaciones, excelente material sebas!

    ResponderEliminar
  2. como se utiliza el # en la programación de Arduino? muchas gracias

    ResponderEliminar
    Respuestas
    1. Hola, igual que acá, en C el numeral "#" se suele usar para definir las macros "#define". Por ejemplo en este programa tenes "#define LED PIN_A1", entonces en el programa en todos los lugares donde diga "LED" lo va a reemplazar por "PIN_A1", básicamente hace eso, reemplaza una cosa por otra para facilitar el código al momento de modificar algo.
      Por ejemplo imagina que tenes varios delay en el programa delay(500); y tenes eso en varias partes del programa, entonces después queres cambiarlo, y tendrías que cambiarlo en todos lados...
      Si usas la macro, seria "#define VALOR 500" entonces, después en el programa poner delay(VALOR); en todos los lugares donde tenias el delay(500); y donde el compilador encuentra la palabra VALOR, la reemplaza por 500, entonces si después queres cambiar 500 por 800.. o lo que sea, solamente tocas el "#define VALOR 800" y listo.
      Saludos.

      Eliminar