| |
PROYECTOS
: TECLADO MATRICIAL
4x4
con DRIVER ANALÓGICO
|
|
|
|
|
Teclado Matricial 4 x 4 con Driver Analógico
|
|
Primera
Parte:
|
|
Objetivo: Leer un
teclado matricial de 4 x 4 teclas mediante un único pin del PIC.
Conceptos involucrados: Suma de resistencias en serie, divisor de
tensión y conversión Analógico-Digital del PIC.
Necesitamos: Un teclado matricial de 4 x 4 teclas, 9 resistencias y
un PIC.
Antecedentes: En el documento "Hardware Techniques for PICmicro
Microcontrollers"
AN234 de Microchip se describe someramente lo que vamos a intenta
realizar. De ahí he sacado la idea y a continuación os describiré lo que
he desarrollado a partir de ella.
Desarrollo: Como todos sabéis, y si no lo sabéis este es un buen
momento para aprenderlo, un teclado matricial 4 x 4 es un artilugio
compuesto por 4 x 4 teclas con 4 + 4 líneas que conectan entre si las
teclas, una línea por cada fila de teclas mas una línea por cada columna
de teclas. Al ser pulsada una cualquiera de ellas une entre sí una de las
líneas, la de su columna, con otra de ellas, la de su fila. Así al pulsar
una tecla quedan unidas solo dos de las ocho que tiene.
Tradicionalmente se ha conectado un teclado de estos a un PIC usando 8
pines de éste, 4 para las filas y 4 para las columnas y se leían poniendo
en alto las filas, o las columnas, y leyendo las columnas, o las filas,
para detectar qué tecla se había pulsado. Esta técnica es muy fácil de
implementar pero tiene el costo de usar muchos pines del PIC.
Lo que aquí vamos a desarrollar es la idea de poder hacer esto mismo pero
haciendo uso de un solo pin del PIC, pero que tenga la especial función de
Conversor Analógico a Digital. Para ello tenemos que conseguir que al
pulsar cada una de las teclas obtengamos un voltaje distinto en una única
línea. Leyendo este voltaje con el Conversor AD del PIC podemos llegar a
saber qué tecla es la que se ha pulsado.
Una imagen vale mas que mil palabras:

Como veis en ella cada círculo en la rejilla central del teclado del
dibujo representa una de las teclas, que al pulsar une una de las
resistencias de R1 a R4 conectadas a VDD con otra de R5 a R8 conectadas al
PIC. Así si pulsamos en la tecla situada en la esquina superior izquierda
tendremos que VDD le llega al PIC tras atravesar R1+R5. Si por el
contrario pulsamos la tecla inferior derecha la corriente nos llegará a
través de la unión entre R4+R8. Siempre que pulsemos una tecla cualquiera
obtendremos un voltaje de caída entre la suma de dos resistencias Rcolumna+Rfila
Otro detalle a tener en cuenta es que si no pulsamos ninguna tecla nuestro
pin del PIC estaría conectado a nada, la línea que une el PIC con las
resistencias R5 a R8 y tras ella el vacío. Esto podría, y sería con total
seguridad, una verdadera antena que recogería todo lo que pasase cerca de
allí, dándonos todo tipo de lecturas falsas mientras no pulsásemos ninguna
tecla. Para evitar ese efecto colocamos R9 que mantendrá el pin del
conversor conectado a GND mientras nos pulsemos nada sobre el teclado.
Pero esta configuración es lo que conocemos como un
Divisor de Tensión en la que tenemos una resistencia conectada a VDD y
otra a GND y nosotros tomamos el valor del voltaje en la unión que hay
entre ellas.

Este divisor de tensión en el que tenemos un Vin o voltaje de
entrada y un Vout o voltaje de salida tras él, que es
perfectamente calculable mediante la fórmula que aparece a la derecha.
Como vemos en esta configuración lo que llamamos aquí Rtop es
lo que en nuestro teclado hemos llamado Rcolumna+Rfila
o sea la suma de las dos resistencias correspondientes al pulsar una tecla
en él. Y Rbottom es nuestra R9 del teclado.
La gran falta del documento de Microchip es que no nos aporta ni valores
de R1 a R9, ni comportamiento aproximado de cómo podríamos elegir dichos
valores. Pero con lo que hemos visto hasta aquí estamos en condiciones de
poder calcular con bastante precisión el comportamiento de nuestro
circuito, sabiendo que Rtop es Rc+Rf y que VDD es 5V podemos concluir que
Vout = R9 / R9+Rc+Rf * 5V y así tendremos un valor de Vout para
cada pareja de resistencias Rc+Rf.
Con esta información me he construido una tabla Excel en la que he puesto
primero la tabla de resistencias de columnas y filas y las distintas sumas
de cada una de ellas. Después otra con los distintos voltajes que se
generan en el divisor de tensión con cada una de las parejas anteriores. Y
por último otra tabla en la que hago corresponder cada uno de estos
voltajes con el valor de la conversión AD del PIC con precisión de 10 bits
(1024 -> 5V lo que Vout es a X)
Jugando con las combinaciones entre valores de unas y otras resistencias
he llegado a obtener uno valores que veo correctos.
Los resultados:

Nota1
Como podéis ver tenemos abajo los valores que vamos a obtener en la
conversión AD para cada tecla pulsada. Son valores razonablemente
separados unos de otros y que nos pueden permitir leer nuestro teclado con
un único pin del PIC (con AD) que es lo que queríamos hacer.
Conclusión: R1=0, R2=470R, R3=1K, R4=2K2, R5=220R, R6=330R,
R7=470R, R8=560R y R9=1K2 con VDD a 5V Nota2.
Y con esto completamos el documento de
Microchip poniéndole valores a lo propuesto por los amables señores de
nuestro proveedor favorito.
Nota1
Teclado_Resistivo_CAD.xls
Nota2 Todas las resistencias son
Comerciales |
|
|
Segunda
Parte:
|
|
|
|

Bueno ... tras unas cuantas horas perdidas debido a que el cable negro, el
que tira a masa la resistencia R9, no hacía buen contacto y por
consiguiente no había divisor de tensión ni nada que se le parezca, lo he
solucionado y ya funciona.
He implementado una forma de leer que intenta minimizar los efectos
aleatorios, y perversos, de las interferencias, transitorios y otras
zarandajas.
Simplemente lo que hago es acumular n lecturas consecutivas y después
calcular la media de todas ellas. Cuanto mas lecturas realice menor
impacto pueden tener lecturas aleatorias esporádicas que puedan
producirse.
Como leo con 10 bits de precisión, 1024 valores posibles, pero la menor y
la mayor resistencia que puedo obtener son de 220R y 2760R que generan
voltajes de 4,225V y 1,515V respectivamente, solo puedo obtener lecturas
entre 865 y 310 aproximadamente.
Si es la mayor, 865, y acumulo sobre una variable de 16 bits solo podré
acumular 75 lecturas (2^16/865) y después dividir por 75 para obtener la
media de todas ... pero no es necesario ajustarse tanto al límite, con 32
lecturas tendremos mas que suficiente.
Asi que implementando que es gerundio: |
|
|
| |
|
|
| |
void main(void){
int16 ANval;
int16 ANval_Acumuleitor=0;
int16 ANVal_Result;
int16 ANVAL_Minimun=255;
int8 CAN_count=0;
int8 CAN_maxcount=32;
setup_adc_ports(AN0 | VSS_VDD); // Configuro AN0 para lectura ADC, usando
como referencia VSS y VDD
setup_adc(ADC_CLOCK_INTERNAL); // Configuro velocidad de muestreo la
interna del PIC 2-6 us
set_adc_channel(0); // Configuro siguiente canal de lectura a AN0;
do{
ANVal = read_adc(); // Leo canal ACD y guardo en ANVal
ANval_Acumuleitor += ANVal; // Acumulo sucesivas lecturas
sobre ANval_Acumuleitor
if(++Can_Count==CAN_maxcount){ // Si hago mas de CAN_maxcount
lecturas ...
ANVal_Result = (int16) ANval_Acumuleitor/CAN_maxcount;
// Calculo la media de las lecturas realizadas ...
if(ANVal_Result>ANVAL_Minimun){ // Que si resulta
mayor que ANVAL_Minimun ...
printf("KeyScan %Lu\r\n",ANVal_Result);
// La tomo en consideración, en este caso lo muestro, ya haremos la
conversión a teclas por margen.
}
Can_Count=0; // Reinicio Can_Count, el contador
de lecturas.
ANval_Acumuleitor=0; // Reinicio
ANval_Acumuleitor, el acumulador de lecturas.
}
}while(1);
}
|
|
| |
|
|
|
Asi el funcionamiento es claro y simple. Leo CAN_maxcount veces
(32) sobre ANval, acumulando sobre ANval_Acumuleitor,
incrementando CAN_count cada vez que leo. Cuando CAN_count
alcanza el valor de CAN_maxcount lecturas calculo ANVal_Result
dividiendo ANval_Acumuleitor por CAN_maxcount. Si
ANVal_Result es mayor que ANVAL_Minimun lo muestro, recordad
que en nuestra tabla el menor resultado posible de una lectura
correspondía a la mayor resistencia posible que nos daba una lectura de
unos 310. Reinicio CAN_count y ANval_Acumuleitor y volvemos
a empezar.
El resultado, mas abajo, tras pulsar la tecla Col4/Row4, F4 en el
teclado de la imagen:

Los resultados obtenidos son compatibles, en desviación, con el 5% de
precisión de las resistencias usadas (las de la banda dorada). Con
resistencias mas precisas, del 1% por ejemplo, podríamos afinar la lectura
proporcionalmente.
Ahora ya solo queda identificar cada lectura con cada tecla, cosa que
haremos teniendo en cuenta un margen de error de +-(mas, menos) ese 5%,
disminuido a aproximadamente un 1% por la ponderación realizada, alrededor
de cada valor teórico calculado, de forma que nuestra tecla F4 que
debería generar una lectura de 310 deberemos fijarla como 307>[F4]>313.
|
|
|
Tercera
Parte:
|
|
|
|
En la
Primera Parte discutíamos la
configuración teórica del cómo podríamos leer un teclado matricial 4x4
mediante un único canal de conversión Analógico-Digital:

En la Segunda Parte
implementábamos un sistema estadístico para recoger n valores consecutivos
de una misma lectura con el fin de minimizar los posibles errores por
interferencias, inestabilidad u otras posibles influencias en la lectura:

Vamos ahora a solucionar los últimos flecos de nuestra implementación de
una Lectura de un Teclado Matricial 4x4 con un solo pin del PIC
consiguiendo una lectura segura, estable y unívoca de nuestro teclado.
El nuevo objetivo a conseguir es detectar infaliblemente una tecla
pulsada, que no se confunda con ninguna otra, que solo la obtengamos tras
asegurarnos que lleva un tiempo mínimo siendo pulsada, que no se repita la
lectura independiente del tiempo que la tengamos pulsada y que sea válida
cuando detectemos que la hemos soltado, reiniciando todo el sistema para
obtener la siguiente tecla.
O sea esto:

El mundo Analógico es fundamentalmente distinto del Digital. En éste entre
dos estados distintos y consecutivos no hay ningún estado intermedio, en
aquel hay infinitos estados. Nuestra conversión AD es conocida hasta el
décimo bit de precisión, si usamos un AD de 10 bits ya que si el conversor
que usamos es de 8 bits será esta la precisión de nuestro conocimiento.
Pero independiente de esta precisión para la parte analógica de nuestra
conversión vamos a recibir distintos voltajes en distintos momentos
dependiendo de cientos de factores que van a influir en él: Desde el mismo
error en la construcción de la red de resistencias que hemos montado,
hasta las variaciones del voltaje VCC que utilizamos pasando por la misma
inestabilidad térmica del sistema o la presión y forma de presionar que
ejerzamos sobre cada una de las teclas al pulsarlas.
Al pulsar y soltar una única tecla podemos obtener toda una gama de
distintos valores, lo he comprobado empíricamente, que pueden recorrer
todo el espectro de valores posibles para nuestro teclado.
Desde el momento en que empieza a haber contacto de la tecla, que cierra y
abre el circuito una decenas de veces, pasando por el tiempo que
mantenemos dicho contacto con distintas áreas de contacto y presiones
sobre la tecla, y hasta el momento en que soltamos la tecla volviendo a un
tren de voltajes distintos generados durante la separación de la membrana
de contacto.
La solución que he adoptado es la de recoger todas estas variaciones y
tomar en cuenta aquella que estadísticamente mas se repita.
Para la primera fase, la del valor de la conversión, ya visteis cómo
recogía n veces cada conversión individual y después sacaba el valor medio
de todas las conversiones realizadas.
Ahora doy otro paso más:
Este valor obtenido no deja de ser una propuesta de lectura, recordad que
puedo obtener x valores compatibles con la tecla KEY1
mezclada con y valores de KEY2 e incluso z valores de
una KEY3.
La implementación que he hecho es la de recoger los distintos valores de
Key leídos sobre una matriz (arreglo) de contadores, incrementando cada
contador individual cuando recibo una key leída.
Cuando un contador de éstos alcanza un valor determinado y definido doy
por absolutamente válida dicha key, limpio el resto de contadores y vuelvo
a empezar.
Si obtengo un cierto número de lecturas por debajo de la mínima lectura
posible es que estoy en estado de no tener ninguna tecla presionada, en
dicho caso también limpio la matriz de contadores.
Si por el contrario obtengo insistentemente la misma key válida es que
estoy manteniendo la tecla pulsada, asi que después de devolverla la
primera vez no vuelvo a hacerlo hasta no pasar de nuevo por el estado de
tecla no pulsada.
La matriz de contadores es de 17 contadores, el primero [ 0 ] para contar
las lecturas menores a la mínima que me va a indicar el estado de tecla no
pulsada, y el resto de contadores [ 1 ]..[ 16 ] para cada una de las
teclas.
Y ya por fin, y dejándome de tanta filosofía, aquí tenéis la
implementación en CCS C que he realizado:
|
| |
|
|
| |
int16 ANval;
int16 ANval_Acumuleitor=0;
int16 ANVal_Result;
int8 CAN_count=0;
int8 NumKeys=0;
int8 NumKeysMinimun=0;
int8 Keys[17]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int8 Key_Pressed=0;
int8 Key_Previous=0;
const int16 ANVAL_Minimun=255;
const int8 CAN_maxcount=64;
const int8 NumKeysForValidKey=15;
const int8 NumKeysForDepressed=25;
const int8 WideForKey=5;
const int16 KeyValues[17]={0,310,318,329,339,445,460,486,508,551,574,614,650,698,736,803,855};
const char KeySimbols[17]={'\0','A','B','C','D','3','6','9','u','2','5','8','X','1','4','7','0'};
void main(void){
setup_adc_ports(AN0 | VSS_VDD);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(0);
do{
task_Analog_Keyboard();
}
}
void task_Analog_Keyboard(void){
int1 flag_Depressed=0;
int8 i;
ANVal = read_adc(); // Leo canal ACD y guardo en ANVal
ANval_Acumuleitor += ANVal; // Acumulo sucesivas lecturas sobre
ANval_Acumuleitor
if(++Can_Count==CAN_maxcount){ // Si hago mas de CAN_maxcount lecturas
...
ANVal_Result = (int16) ANval_Acumuleitor/CAN_maxcount; //
Calculo la media de las lecturas realizadas ...
if(ANVal_Result>ANVAL_Minimun){ // Que si resulta mayor que
ANVAL_Minimun ...
for(i=1;i<17;i++){
if(ANVal_Result>(KeyValues[i]-WideForKey)
AND ANVal_Result<(KeyValues[i]+WideForKey)){ // Si el valor está dentro del
margen de error ~ WideForKey
if(++Keys[i]==NumKeysForValidKey){
Key_Pressed=i;
// Si alcanzo NumKeysForValidKey marco definitivamente Key_Pressed
for(i=0;i<17;i++){Keys[i]=0;}
// Limpio los acumulados de Keys hasta el momento
break; //
Aborto ejecución del bucle para tomar en cuenta la tecla pulsada.
}
}
}
}
else{ // Si es menor que ANVAL_Minimun estoy en Key_Depressed
if(++Keys[0]==NumKeysForDepressed){
if(flag_Depressed==0) flag_Depressed=1;
// Si alcanzo NumKeysForDepressed marco definitivamente Key_Depressed
}
}
Can_Count=0; // Reinicio Can_Count, el contador de lecturas.
ANval_Acumuleitor=0; // Reinicio ANval_Acumuleitor, el
acumulador de lecturas.
// Si tengo una Key_Pressed y no es igual a la última
Key_Pressed y ya he detectado que he soltado la telca
// tomo en consideración los resultados obtenidos: son
válidos y estables.
if((Key_Pressed!=0) AND (Key_Pressed!=Key_Previous) AND
flag_Depressed==1){
printf("Key = %2u Symbol=%c\r\n",Key_Pressed,KeySimbols[Key_Pressed]);
// Aqui tenemos disponibles los valores de Key pulsada y/o Simbolo asociado
flash_BUZZER(1,50);
Key_Previous=Key_Pressed; // Asigno valor actual
de tecla al anterior para evitar repeticiones
Key_Pressed=0; // Limpio valor actual para seguir
investigando teclas pulsadas.
}
if(flag_Depressed==1){ // Si recibo estado definitivo de
Key_Depressed ...
flag_Depressed=0; // Marco como leido dicho
estado ...
Key_Previous=0; // Reinicio valor anterior de
tecla pulsada Key_Previous
}
}
}
|
|
| |
|
|
El resultado el que os mostré mas arriba.

Seguro, estable, fiable y confiable.

Hasta otra. |
|
|
Esta página se modificó el
27/12/2008
|