DÍA 128 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Truco de "Todas las Filas a Cero"

¿Cómo puede el hardware saber que se presionó una tecla si no estamos escaneando activamente fila por fila? La industria utiliza un truco de topología eléctrica muy ingenioso.

Mientras el sistema está en reposo (Idle), configuramos todas las Filas (Salidas) a 0V al mismo tiempo usando nuestro modo Open-Drain. Las Columnas (Entradas) están conectadas al hardware de interrupción externa (EXTI) y sostenidas a 3.3V por las resistencias de Pull-up.

Dado que todas las filas son `0V`, no importa cuál de los 16 botones presione el usuario; instantáneamente, la columna correspondiente se conectará a 0V. Esta caída abrupta de voltaje (Flanco de Bajada / Falling Edge) activa la línea EXTI y el hardware despierta al procesador de inmediato.

Manejo del Rebote y "Tormentas de Interrupciones"

El botón mecánico rebotará docenas de veces en el primer milisegundo. Si dejamos el EXTI encendido, el procesador será bombardeado con 50 interrupciones inútiles (Interrupt Storm), colapsando el sistema. La táctica Bare-Metal correcta es:

  1. El primer toque dispara el EXTI.
  2. Dentro de la interrupción (ISR), apagamos (enmascaramos) temporalmente las interrupciones del teclado.
  3. Levantamos una bandera (Flag) que dice `hay_tecla_pendiente = true`.
  4. En el Súper Bucle, al ver la bandera, hacemos nuestro escaneo normal asíncrono (Día 127) para confirmar qué tecla fue y aplicar el *debounce*.
  5. Al soltar la tecla, volvemos a poner todas las filas a 0V y re-encendemos las interrupciones.
EVENT-DRIVEN MATRIX: TODAS LAS FILAS EN BAJO (0V) STM32 EXTI Falling Edge OUTPUTS R1 = 0V R2 = 0V R3 = 0V R4 = 0V Todas jalando a GND +3.3V (PullUp) +3.3V +3.3V +3.3V PRESS El pin C3 cae de 3.3V a 0V ! EXTI FIRES !

⚙️ Ejercicios Prácticos

Ejercicio 1: Reconfigura las entradas de columnas (PB0 a PB3) para enrutarlas al hardware SYSCFG y a las líneas de interrupción EXTI0 a EXTI3 con detección de flanco de bajada (Falling Edge). Crearemos una bandera volátil y gestionaremos el enmascaramiento.


/**
 * @file keypad_exti.c
 * @brief Teclado Matricial por Interrupciones y Estado de Reposo
 */

#include "stm32f401xe.h"
#include <stdbool.h>

extern volatile uint32_t milisegundos_globales;

// Bandera global para avisar al main que despierte
volatile bool bandera_teclado_activado = false;

// Dependencia de la función de escanear del Día 127
extern char escanear_teclado_async(void); 

/**
 * @brief Pone todas las filas en 0V simultáneamente para armar la trampa
 */
void armar_trampa_exti(void) {
    // Las filas son PB4 a PB7. Escribiendo 1 en el registro BSRR Reset 
    // forzamos la salida física Open-Drain a GND (0V).
    GPIOB->BSRR = (0xF << (4 + 16)); 
    
    // Desenmascarar (Re-habilitar) las interrupciones en las líneas EXTI 0 al 3
    EXTI->IMR |= (EXTI_IMR_MR0 | EXTI_IMR_MR1 | EXTI_IMR_MR2 | EXTI_IMR_MR3);
}

void inicializar_teclado_exti(void) {
    
    // 1. Relojes (GPIOB y SYSCFG para interrupciones externas)
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // Pines GPIO idénticos al Día 127
    // Columnas PB0-PB3 (Entradas Pull-up), Filas PB4-PB7 (Salidas Open-Drain)
    GPIOB->MODER &= ~(0xFFFF << 0); 
    GPIOB->MODER |=  (0x55 << (4*2)); 
    GPIOB->PUPDR &= ~(0xFF << 0);
    GPIOB->PUPDR |=  (0x55 << 0);
    GPIOB->OTYPER |= (0xF << 4);
    
    // 2. ENRUTAR PINES PB0-PB3 AL EXTI
    // Usamos el registro EXTICR1 del SYSCFG para mapear PB0-PB3
    // El puerto B corresponde al valor '0001' (1)
    SYSCFG->EXTICR[0] &= ~0xFFFF;
    SYSCFG->EXTICR[0] |= (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12);
    
    // 3. CONFIGURAR EXTI (Detección de Caída / Falling Edge)
    EXTI->FTSR |= (EXTI_FTSR_TR0 | EXTI_FTSR_TR1 | EXTI_FTSR_TR2 | EXTI_FTSR_TR3);
    EXTI->RTSR &= ~(EXTI_RTSR_TR0 | EXTI_RTSR_TR1 | EXTI_RTSR_TR2 | EXTI_RTSR_TR3);
    
    // 4. CONFIGURAR PRIORIDADES EN NVIC
    // Las interrupciones EXTI0, 1, 2, y 3 tienen vectores separados en STM32F4
    NVIC_SetPriority(EXTI0_IRQn, 5);
    NVIC_SetPriority(EXTI1_IRQn, 5);
    NVIC_SetPriority(EXTI2_IRQn, 5);
    NVIC_SetPriority(EXTI3_IRQn, 5);
    
    NVIC_EnableIRQ(EXTI0_IRQn);
    NVIC_EnableIRQ(EXTI1_IRQn);
    NVIC_EnableIRQ(EXTI2_IRQn);
    NVIC_EnableIRQ(EXTI3_IRQn);
    
    // 5. Dejar el sistema armado y listo
    armar_trampa_exti();
}

/**
 * @brief Rutina común llamada por todas las ISRs del teclado
 */
void procesar_interrupcion_teclado(void) {
    // 1. Apagar temporalmente el EXTI para evitar tormenta de rebotes mecánicos
    EXTI->IMR &= ~(EXTI_IMR_MR0 | EXTI_IMR_MR1 | EXTI_IMR_MR2 | EXTI_IMR_MR3);
    
    // 2. Avisar al Súper Bucle
    bandera_teclado_activado = true;
    
    // 3. Limpiar las banderas físicas
    EXTI->PR |= (EXTI_PR_PR0 | EXTI_PR_PR1 | EXTI_PR_PR2 | EXTI_PR_PR3);
}

// Implementación de Vectores de Interrupción físicos
void EXTI0_IRQHandler(void) { procesar_interrupcion_teclado(); }
void EXTI1_IRQHandler(void) { procesar_interrupcion_teclado(); }
void EXTI2_IRQHandler(void) { procesar_interrupcion_teclado(); }
void EXTI3_IRQHandler(void) { procesar_interrupcion_teclado(); }


int main(void) {
    
    // SysTick_Init();
    inicializar_teclado_exti();
    
    while(1) {
        
        // El CPU no hace absolutamente nada relacionado con el teclado 
        // durante el 99.99% del tiempo de vida de la máquina.
        
        if (bandera_teclado_activado) {
            
            // Si el hardware avisó, ejecutamos la máquina de estados del Día 127
            // que sí escanea fila por fila y hace el Debounce asíncrono.
            char tecla = escanear_teclado_async();
            
            if (tecla != 0) {
                // lcd_escribir_caracter(tecla);
            }
            
            // Cuando la máquina de estados de escaneo (del Día 127) vuelve al estado 0 
            // (lo que significa que el dedo ya soltó el botón), re-armamos la trampa.
            if (obtener_estado_escaner() == 0) {
                bandera_teclado_activado = false;
                armar_trampa_exti(); // Vuelve a poner las 4 filas en GND y activa el EXTI
            }
        }
        
        // ¡100% del CPU libre para Procesamiento de Red RS485 o Sleep Mode!
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Disponibilidad de Procesamiento y Bajo Consumo (Event-Driven).

En el comercio real, un Despachador de Gas LP pasa horas en estado de reposo esperando a un cliente. Si obligas a tu CPU a escanear un teclado vacío durante 10 horas seguidas, generas desgaste térmico y le robas tiempo a las comunicaciones de red (donde el equipo central le está preguntando precios y cortes de caja continuamente).

Al implementar el esquema **EXTI con Filas a GND**, has transformado tu Interfaz de Usuario en un sistema Event-Driven (Basado en Eventos). El procesador no busca la pulsación; la pulsación embosca al procesador. Esta es una optimización de diseño maestro: utilizas el hardware analógico-digital (caída de voltaje en Pull-Ups) para delegar el 100% de la carga de monitoreo, liberando a tu Cortex-M para que gobierne la red Modbus como un verdadero servidor embebido.

📝 Resumen del Día