← Índice del curso

Microcontroladores · Tema 18

Taller: LED, pulsadores y teclado matricial

Cierre del bloque 4. Reunimos todo lo visto sobre GPIO en un proyecto: un teclado de 16 teclas leído con solo 8 pines. La técnica, el escaneo de filas y columnas, es la misma de las pantallas de matriz de LED y de muchos buses; aquí se aprende con las manos en la masa y con sus trampas: teclas fantasma, rebote y cortocircuitos entre salidas.

01 · Punto de partida

Qué vamos a construir

Un teclado numérico de 16 teclas conectado al microcontrolador, que muestra el código de la tecla apretada en cuatro LED en binario. Es un proyecto pequeño, pero ejercita lo que aprendiste en el bloque: configurar pines como entradas con pull-up y como salidas, escribir un puerto entero, manejar la alta impedancia, leer niveles, detectar flancos y filtrar el rebote.

Los materiales son un teclado matricial de membrana de 4×4 (con un conector de 8 pines), cuatro LED con resistencias de 330 Ω, la placa Uno, una protoboard y el multímetro. Opcionalmente, diodos 1N4148 si querés estudiar las teclas fantasma.

Retomamos los temas 15 a 17: salidas, estados de los pines, pull-up, alta impedancia y antirrebote por muestreo.

02 · El problema de los pines

La idea de la matriz

Conectar cada tecla a su propio pin cuesta un pin por tecla: 16 teclas, 16 pines. Un microcontrolador pequeño se queda sin pines enseguida. La solución es ordenar las teclas en una matriz de filas y columnas, con una tecla en cada cruce. Una tecla cerrada conecta su fila con su columna.

Matriz de teclado de 4 por 4 Cuatro filas horizontales conectadas a salidas y cuatro columnas verticales conectadas a entradas con pull-up. En cada cruce hay una tecla que, al cerrarse, une la fila con la columna. F0 · PD4F1 · PD5F2 · PD6F3 · PD7 C0 · PB0C1 · PB1C2 · PB2C3 · PB3 Filas: salidas.Solo una baja a la vez;las demás, en alta impedancia. Columnas: entradas con pull-up. Una columna lee 0 si su tecla está cerrada en la fila activa. ● = tecla (interruptor)

El algoritmo, llamado escaneo o multiplexado: se baja una fila a la vez, y se lee qué columnas se vuelven 0. Una columna en 0, con una fila en 0, significa que la tecla de ese cruce está cerrada. Con f filas y c columnas se manejan f · c teclas con solo f + c pines: 16 teclas con 8 pines; 64 teclas con 16.

Es el mismo principio de la memoria del curso anterior: un decodificador de filas y una lectura de columnas. Aquí lo hace el programa. Lo vas a encontrar también en displays de siete segmentos y en matrices de LED, que se multiplexan para ahorrar pines.

03 · Paso 1

El cableado

Asignación de pines en la placa Uno. Se evitan D0 y D1 (comunicación serie).
FunciónPin del chipPin de la placaConfiguración
Filas F0 a F3 del tecladoPD4 a PD7D4 a D7Salida en bajo cuando están activas; entrada Hi-Z cuando no
Columnas C0 a C3 del tecladoPB0 a PB3D8 a D11Entradas con pull-up interno
LED del código (bit 0 a 3)PC0 a PC3A0 a A3Salidas, cada una con 330 Ω a masa

Los pines de un mismo puerto se agrupan en un nibble: las filas en la mitad alta de PORTD y las columnas en la mitad baja de PORTB. Así se manejan con máscaras de 4 bits, en el estilo del tema 10.

Los teclados de membrana tienen un conector plano de 8 contactos, pero el orden de las filas y las columnas varía según el modelo. Antes de cablear, identificalas con el multímetro en continuidad: apretá una tecla y buscá entre qué dos contactos suena. Las teclas de una misma fila comparten un contacto, y las de una misma columna comparten el otro.

04 · Paso 2

El escaneo

La configuración de los pines es el truco: las filas se manejan como un colector abierto emulado (tema 16). Cada fila tiene su bit de PORTD en 0 de forma permanente, y se activa poniendo su bit de DDRD en 1 (la fila baja a 0). Cuando DDRD vale 0, la fila queda en alta impedancia.

void teclado_init(void) {
    PORTD &= 0x0F;        // filas: PORT en 0 (si DDR=1, sale 0; con DDR=0 es Hi-Z)
    DDRD  &= 0x0F;        // todas las filas inactivas (alta impedancia)
    DDRB  &= ~0x0F;       // columnas: entradas
    PORTB |=  0x0F;       // con pull-up
}

// Devuelve un bit por tecla: bit (4·fila + columna) en 1 si está cerrada
uint16_t escanear(void) {
    uint16_t cerradas = 0;
    for (uint8_t fila = 0; fila < 4; fila++) {
        DDRD = (DDRD & 0x0F) | (0x10 << fila);   // bajar solo esta fila
        _delay_us(10);                                // dejar que las columnas se asienten
        uint8_t columnas = (~PINB) & 0x0F;            // 1 = columna en 0 = tecla cerrada
        cerradas |= (uint16_t)columnas << (4 * fila);
    }
    DDRD &= 0x0F;                                     // soltar las filas
    return cerradas;
}

Por qué la pausa de 10 µs: cuando una fila deja de estar activa, la columna que estaba en 0 mediante una tecla queda en alta impedancia y tiene que volver a subir por el pull-up de 20 a 50 kΩ, cargando la capacidad del cable y del teclado (algunas decenas de picofaradios). La constante de tiempo es del orden de 1 a 5 µs. Si se lee antes, la columna aún está baja y aparece una tecla que no existe en la fila nueva.

Con (~PINB) & 0x0F se invierte la lógica activa en bajo: ahora un 1 significa «tecla cerrada». Y devolver las 16 teclas en un entero de 16 bits tiene una ventaja: el estado completo del teclado cabe en una variable, que se compara, se filtra y se recorre con operaciones de bits.

05 · Paso 3

Teclas fantasma y diodos

Con tres teclas apretadas en los vértices de un rectángulo (por ejemplo F0·C0, F0·C1 y F1·C0), el escaneo detecta una cuarta tecla que nadie apretó (F1·C1). Al bajar la fila F1, la columna C0 baja por la tecla F1·C0; esa columna está unida a F0 por la tecla F0·C0, y F0 a C1 por F0·C1, de modo que C1 también baja y parece que F1·C1 está cerrada. La corriente encuentra un camino alternativo por tres teclas.

El problema es de la matriz, no del programa. Tres soluciones:

  • Un diodo en serie con cada tecla (cátodo del lado de la fila). La corriente solo puede ir de la columna a la fila activa, y los caminos alternativos quedan bloqueados. Cuesta un diodo por tecla y permite apretar cualquier combinación.
  • Aceptar la limitación: detectar la situación (tres teclas en un rectángulo) e ignorar la combinación. Es lo que hacen los teclados económicos y por eso, en un teclado de computadora sin diodos, a veces tres teclas a la vez no responden.
  • Limitar el uso a una tecla a la vez, como en un teclado numérico de un cajero.

06 · Paso 4

Antirrebote de la matriz

Cada tecla rebota (tema 17). Como el escaneo devuelve el estado de las 16 teclas en un entero, el filtro es sencillo: se acepta un estado nuevo solo cuando se repite en varios escaneos consecutivos. Es el método de «N lecturas iguales», aplicado a los 16 bits a la vez.

static uint16_t candidato = 0, estable = 0;
static uint8_t  cuenta = 0;

// Se llama una vez cada 5 ms. Devuelve las teclas recién presionadas.
uint16_t teclas_nuevas(void) {
    uint16_t ahora = escanear();
    uint16_t nuevas = 0;
    if (ahora != candidato) {                // cambió algo: empezar a contar de nuevo
        candidato = ahora;
        cuenta = 0;
    } else if (cuenta < 3 && ++cuenta == 3) {   // tres escaneos iguales (15 ms): estable
        nuevas = candidato & ~estable;       // bits que ahora valen 1 y antes valían 0
        estable = candidato;
    }
    return nuevas;
}

La línea candidato & ~estable es la detección de flancos del tema 17 aplicada a los 16 bits: devuelve solo las teclas que acaban de cerrarse, una vez por pulsación. Con un período de 5 ms y tres escaneos, la latencia es de 15 ms. El filtro cubre un rebote de hasta unos 10 ms.

07 · Paso 5

El programa completo

Con las partes anteriores, el programa principal muestra el índice de la primera tecla nueva en los cuatro LED:

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

static const char TECLAS[16] = {'1','2','3','A', '4','5','6','B', '7','8','9','C', '*','0','#','D'};

static uint8_t indice(uint16_t mascara) {        // posición del bit menos significativo
    uint8_t i = 0;
    while (!(mascara & 1)) { mascara >>= 1; i++; }
    return i;
}

int main(void) {
    teclado_init();
    PORTC &= ~0x0F;                               // LED apagados antes de volverlos salidas
    DDRC  |=  0x0F;

    while (1) {
        uint16_t nuevas = teclas_nuevas();
        if (nuevas) {
            uint8_t k = indice(nuevas);          // tecla 0 a 15
            PORTC = (PORTC & ~0x0F) | k;         // mostrar el código en binario
            /* TECLAS[k] es el carácter: lo usaremos con la UART */
        }
        _delay_ms(5);                            // base de tiempo simple
    }
}

Dos notas de diseño. El _delay_ms(5) es una base de tiempo simple y bloqueante: sirve en este proyecto porque el programa no hace otra cosa. En un programa real, el escaneo se llamaría desde la marca de 5 ms de un temporizador (tema 11), y el resto del tiempo estaría libre. Y TECLAS[k] traduce el índice al carácter de la tecla; cuando tengamos la UART (tema 32) lo enviaremos a la computadora.

08 · Paso 6

Verificar

  • Cada tecla da su código. Apretá las 16 y comprobá que los LED muestran 0 a 15 (la tecla «1» es 0000; la «D», 1111). Si una tecla da el código de otra, hay un cable cambiado.
  • Una pulsación, un evento. Mantené una tecla apretada un segundo: el código no debe cambiar ni repetirse. Apretá varias veces rápido: no debe perderse ninguna.
  • La forma de la señal. Con el osciloscopio en una columna, mirá lo que ocurre al cambiar de fila: debe verse la subida por el pull-up con una constante de tiempo de microsegundos. Si ves que no llega al nivel alto en 10 µs, alargá la pausa.
  • El tiempo del escaneo. Con una marca en un pin (tema 14), medí la duración de escanear(): debería rondar los 40 a 50 µs con la pausa de 10 µs por fila, es decir, menos del 1 % de un período de 5 ms.
  • Las teclas fantasma. Apretá tres teclas en un rectángulo: sin diodos, el código de una cuarta puede aparecer. Con diodos, no.

Si algo falla, el orden de sospecha es: la identificación de filas y columnas (continuidad), el pull-up de las columnas, el orden de las instrucciones de teclado_init(), la pausa de asentamiento y, al final, el filtro.

09 · Analogía con programación

Simular la matriz y sus fantasmas

Una matriz de teclas es un grafo: filas y columnas son nodos, y las teclas cerradas son aristas. Una columna lee 0 cuando está en la misma componente conexa que la fila activa. Este programa calcula el escaneo con y sin diodos para cuatro combinaciones de teclas:

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

TECLAS = [["1", "2", "3", "A"],
          ["4", "5", "6", "B"],
          ["7", "8", "9", "C"],
          ["*", "0", "#", "D"]]


def escanear(cerradas, diodos=False):
    """Filas inactivas en alta impedancia. Una columna se lee en 0 si está conectada a la fila activa."""
    detectadas = set()
    for fila in range(4):
        if diodos:
            alcanzadas = {c for (f, c) in cerradas if f == fila}      # solo el camino directo
        else:
            filas, columnas, cambio = {fila}, set(), True
            while cambio:                                                # propagar por teclas cerradas
                cambio = False
                for f, c in cerradas:
                    if f in filas and c not in columnas:
                        columnas.add(c)
                        cambio = True
                    if c in columnas and f not in filas:
                        filas.add(f)
                        cambio = True
            alcanzadas = columnas
        detectadas |= {(fila, c) for c in alcanzadas}
    return detectadas


def nombres(teclas):
    return sorted(TECLAS[f][c] for f, c in teclas)


casos = {
    "dos teclas (1 y 2)": {(0, 0), (0, 1)},
    "tres teclas en un rectángulo (1, 2 y 4)": {(0, 0), (0, 1), (1, 0)},
    "tres teclas en línea (1, 2 y 3)": {(0, 0), (0, 1), (0, 2)},
    "cuatro teclas (1, 3, 7 y 9)": {(0, 0), (0, 2), (2, 0), (2, 2)},
}

for descripcion, cerradas in casos.items():
    sin = escanear(cerradas)
    con = escanear(cerradas, diodos=True)
    print(descripcion)
    print(f"   sin diodos: {nombres(sin)}   fantasmas: {nombres(sin - cerradas)}")
    print(f"   con diodos: {nombres(con)}   fantasmas: {nombres(con - cerradas)}")

Resultado: con dos teclas, tres en línea o cuatro en un rectángulo completo, el escaneo es exacto; con tres teclas en los vértices de un rectángulo (1, 2 y 4) aparece el fantasma «5» sin diodos, y desaparece con diodos. Probá (1,1), (1,3) y (3,1): el fantasma es (3,3), la tecla «D».

El modelo es el caso ideal: no incluye el tiempo de asentamiento ni la resistencia de los pines. En el hardware real, un rectángulo de cuatro teclas completo no tiene fantasma, porque no hay una quinta posición que se confunda.

10 · Exploración interactiva

Teclado virtual

Cerrá teclas con el mouse y mirá lo que detecta el escaneo. El fondo amarillo marca las teclas realmente cerradas; el naranja, las detectadas que nadie apretó. Podés activar los diodos o cambiar la forma de manejar las filas inactivas. La segunda herramienta calcula cuánto cuesta escanear.

Banco del teclado matricial

1. Escaneo de una matriz de 4×4

    Teclas realmente cerradas0
    Teclas detectadas0
    Valor devuelto por escanear()0x0000

    2. Costo del escaneo y del antirrebote

    Pines necesarios—
    Latencia de confirmación—
    Costo del escaneo—

    El simulador modela la matriz como un grafo (el mismo cálculo del bloque de Python). Con los diodos, una columna lee 0 solo si su tecla está cerrada en la fila activa. El costo por fila de la herramienta 2 (unos 30 ciclos más la pausa) es una estimación a verificar con el método del tema 14.

    Recorrido guiado

    1. Cerrá la tecla «5»: el escaneo la detecta y muestra 0x0020. Cerrá también la «9»: son dos teclas y no hay problema.
    2. Soltá todo y cerrá «1», «2» y «4» (tres vértices de un rectángulo): aparece la tecla «5» en naranja. Es el fantasma.
    3. Activá «Un diodo en serie con cada tecla»: el fantasma desaparece.
    4. Desactivá los diodos y cambiá las filas inactivas a «Salida en alto»: aparece el aviso de cortocircuito entre salidas. Esto no ocurre con la alta impedancia.
    5. En la herramienta 2, cambiá a una matriz de 8×8: 64 teclas con 16 pines, y mirá el costo del escaneo. Subí la pausa de asentamiento a 100 µs y observá cómo crece.
    Desafío: la combinación que se pierde

    Con los diodos desactivados, cerrá las teclas «1», «2», «4» y «5» (los cuatro vértices de un rectángulo completo). El escaneo detecta exactamente esas cuatro, sin fantasmas. Pero si el programa ignorara las combinaciones ambiguas (tres vértices), ¿cómo distinguiría este caso de uno con tres teclas? No puede hacerlo con solo mirar el escaneo, por eso los teclados sin diodos limitan cuántas teclas simultáneas se garantizan.

    11 · Método de trabajo

    Lo aprendido, para cualquier matriz

    1. Identificá filas y columnas con continuidad; no te fíes del orden del conector.
    2. Un lado activo, el otro leído. Una fila baja a 0 (como colector abierto: PORT en 0, DDR conmuta), las columnas se leen con pull-up.
    3. Inactivo significa alta impedancia, nunca nivel alto.
    4. Esperá el asentamiento entre cambiar la fila y leer las columnas (unos 10 µs).
    5. Devolvé el estado completo en una variable y filtralo como un todo (N escaneos iguales); detectá los eventos con nuevo & ~estable.
    6. Decidí qué hacer con las ambigüedades (diodos, ignorarlas o una tecla a la vez) y verificalo con tres teclas en rectángulo.

    El mismo esquema, con las funciones de entrada y salida invertidas, es la forma de manejar una matriz de LED o un display multiplexado: se activa una fila a la vez y se escriben las columnas con el patrón de esa fila, tan rápido que el ojo no ve el parpadeo.

    Con este taller termina el bloque 4. Sabés configurar un pin, conocés su electrónica y lees entradas con criterio. El bloque 5 aborda las interrupciones: cómo reaccionar a un evento sin tener que preguntar todo el tiempo.

    12 · Comprensión

    Confusiones frecuentes

    «El teclado de 16 teclas necesita 16 pines»

    Con una matriz de 4×4 bastan 8. La contrapartida es que hay que escanear y filtrar por software, y que las teclas simultáneas pueden dar fantasmas.

    «Las filas inactivas deben estar en alto para no molestar»

    Al contrario: en alto, una tecla cerrada puede unirlas con la fila activa en bajo y provocar un cortocircuito entre salidas. Se dejan en alta impedancia.

    «Con leer una vez cada columna alcanza»

    Se lee cada columna una vez por fila activa. Sin recorrer las filas no se sabe cuál tecla del cruce está cerrada.

    «Los fantasmas se arreglan en el programa»

    No: es un problema eléctrico de la matriz. El programa puede detectar situaciones ambiguas y descartarlas, pero la solución real son los diodos.

    «Si no hay pausa entre bajar la fila y leer, igual funciona»

    Muchas veces sí, y otras no: la columna que acaba de dejar de estar en bajo tiene que subir por el pull-up, que es débil. Un fallo intermitente de este tipo es muy difícil de encontrar, así que conviene asegurar la pausa.

    13 · Práctica guiada

    Predecí antes de comprobar

    1. Contar pines

    Querés 25 teclas. ¿Cuántos pines necesita una matriz de 5×5 y cuántas teclas máximas puede manejar una matriz con 8 pines?

    Ver solución

    Una matriz de 5×5 usa 5 + 5 = 10 pines. Con 8 pines el producto filas × columnas es máximo con 4 y 4: 16 teclas.

    2. Del cruce al carácter

    La tecla de la fila 2 y la columna 3 tiene el índice 4·2 + 3. ¿Qué índice y qué carácter es en el teclado del taller?

    Ver solución

    Índice 11. En la tabla TECLAS, la fila 2 es «7 8 9 C»: el carácter es «C». En los LED se vería 1011.

    3. Prever un fantasma

    Sin diodos, se aprietan las teclas de los cruces (1,1), (1,3) y (3,1). ¿Qué tecla fantasma aparece?

    Ver solución

    Faltaría el cuarto vértice del rectángulo: el cruce (3,3), que es la tecla «D». La fila 3 baja la columna 1 (por (3,1)), la columna 1 está unida a la fila 1, y la fila 1 a la columna 3, que también baja.

    4. Costo del escaneo

    Con 4 filas, 30 ciclos por fila más 10 µs de pausa por fila, y 20 ciclos fijos, a 16 MHz: ¿cuánto dura un escaneo y qué parte de un período de 5 ms ocupa?

    Ver solución

    La pausa de 10 µs son 160 ciclos. Por fila: 30 + 160 = 190. Total: 4 × 190 + 20 = 780 ciclos = 48,75 µs. De 5 ms es el 0,975 %.

    5. Latencia de confirmación

    Escaneás cada 5 ms y exigís 3 escaneos iguales. ¿Qué latencia tiene la detección y qué rebote máximo cubre?

    Ver solución

    3 × 5 ms = 15 ms de latencia. Cubre un rebote de hasta unos 10 ms (el filtro necesita que el estado se mantenga durante las tres lecturas, que abarcan 10 ms entre la primera y la última).

    14 · Síntesis

    Ideas para recordar

    • Una matriz de f filas y c columnas maneja f·c teclas con f+c pines; 16 teclas con 8 pines.
    • El escaneo baja una fila a la vez y lee las columnas; una columna en 0 indica la tecla cerrada en el cruce.
    • Las filas se manejan como colector abierto emulado: PORT en 0 y DDR conmutando; las inactivas quedan en alta impedancia.
    • Hace falta una breve pausa de asentamiento entre cambiar la fila y leer las columnas.
    • Con tres teclas en los vértices de un rectángulo aparece una cuarta fantasma; los diodos en serie con cada tecla lo evitan.
    • El estado de la matriz cabe en un entero de 16 bits que se filtra con N escaneos iguales y se compara con nuevo & ~estable.
    • El mismo principio de multiplexado sirve para displays y matrices de LED.

    ¿Listo para el bloque 5?

    Antes de avanzar, explicá cuántos pines ahorra una matriz de 4×4, por qué las filas inactivas van en alta impedancia y cómo aparece una tecla fantasma. Si podés justificar las tres respuestas, cerraste el bloque de entradas y salidas digitales.

    En el próximo tema empieza el bloque de interrupciones con vectores y rutinas de servicio: cómo hace el microcontrolador para dejar lo que está haciendo, atender un evento y volver.