DÍA 325 DE 365

Fase 5: Diseño Industrial y Hardware

📖 Teoría a Estudiar

El Detector de Voltaje Programable (PVD)

El microcontrolador STM32 no se apaga suavemente; cuando el voltaje de alimentación (VCC) cae por debajo del umbral de BOR (Brown-Out Reset, usualmente 2.0V), el hardware entra en coma instantáneo para proteger la memoria de escrituras corruptas. Pero el chip incluye un periférico preventivo: el PVD (Programmable Voltage Detector).

El PVD vigila constantemente la línea de 3.3V. Nosotros lo programaremos para que se dispare si el voltaje cae a 2.8V. Cuando esto ocurre, el PVD lanza la interrupción de más alta prioridad del sistema operativo (EXTI Línea 16). Esto nos otorga una ventana crítica de unos pocos milisegundos (dependiendo de la capacitancia de tu fuente de poder) antes de que el voltaje llegue a 2.0V y la CPU muera.

Los Backup Registers y la Batería RTC

En esos escasos 2 milisegundos de "Dying Breath" (Último Aliento), no hay tiempo de encender el bus I2C y escribir lentamente a la memoria EEPROM. Usaremos los Backup Registers (BKP) del STM32. Son unos pocos bytes de memoria SRAM especial que pertenece al dominio de hardware del Reloj en Tiempo Real (RTC). Si la placa tiene una pila de botón de 3V (CR2032), estos registros conservarán sus datos de volumen y venta actual durante años, sin importar el apagón principal.

CRONOLOGÍA DE APAGÓN: PVD Y "DYING BREATH" EN MILISEGUNDOS Tiempo (ms) VCC_MCU (Voltios) 3.3V 2.8V UMBRAL PVD (Interrupt) 2.0V UMBRAL BOR (Muerte CPU) APAGÓN (CFE OFF) Capacitor Hold-Up EXTI 16 "DYING BREATH" Guardar Venta -> BKP ~ 2 milisegundos Antes de coma inducido CPU HALTED 3V Batería CR2032 (RTC) Mantiene vivos los Backup Registers en RAM

⚙️ Procedimiento Práctico: Hardware para Hold-Up Time

Paso 1: El Banco de Capacitores de la PSU.
El tiempo de ventana (Dying Breath) depende del consumo de corriente de la placa y de la cantidad de microfaradios a la entrada del STM32. Para ganar milisegundos preciosos, el Arquitecto instala en la PCB un banco de capacitores electrolíticos de 1000 uF / 6.3V (Low ESR) exclusivamente dedicados a la línea de 3.3V, y separados del resto por un diodo Schottky para evitar que el ESP32 consuma esa reserva durante el apagón.

Paso 2: La Batería del Dominio RTC (VBAT).
El STM32 posee un pin llamado VBAT. Conecta allí una batería tipo botón de Litio CR2032 de 3V. Cuando el voltaje principal VDD desaparece, un switch interno en el chip transfiere la alimentación del reloj en tiempo real (RTC) y de los Backup Registers a la batería. La RAM principal se borra, pero esos 20 registros especiales sobrevivirán por 3 a 5 años sin energía de pared.

💻 Firmware: Interrupción PVD (La Maniobra Defensiva)

Configuraremos el hardware para que lance una alarma cuando el VCC caiga a 2.8V. En la rutina de interrupción, el código paralizará cualquier comunicación (nada de `printf` ni UART) y usará la matemática de Punto Fijo para guardar la venta crítica en los BKP Registers a la velocidad del reloj del bus (nanosegundos).


/**
 * @file hardware_pvd_defense.c
 * @brief Protección contra caída de tensión (Brownout) y rescate de RAM
 */

#include "stm32f4xx_hal.h"
#include <stdint.h>
#include <stdbool.h>

// Variables globales de la venta en curso (64-bit Fixed Point - Día 309)
extern uint64_t volumen_acumulado_mL; 
extern uint32_t current_sale_cents;   

// Banderas de estado
volatile bool System_Power_Failing = false;

// =========================================================================
// CONFIGURACIÓN DEL DETECTOR DE VOLTAJE (PVD)
// =========================================================================
/**
 * @brief Activa el PVD para alertar a 2.8V (Nivel 6 en el STM32F4).
 */
void Power_PVD_Config(void) {
    PWR_PVDTypeDef sConfigPVD;

    // Habilitar el reloj del Power Controller
    __HAL_RCC_PWR_CLK_ENABLE();

    // Configurar umbral de disparo. Level 6 equivale a ~2.8V ascendente/descendente
    sConfigPVD.PVDLevel = PWR_PVDLEVEL_6;
    sConfigPVD.Mode = PWR_PVD_MODE_IT_RISING_FALLING;
    HAL_PWR_ConfigPVD(&sConfigPVD);
    
    // Habilitar la interrupción extrema del PVD (EXTI Line 16)
    HAL_NVIC_SetPriority(PVD_IRQn, 0, 0); // Prioridad Cero: ¡Vida o muerte!
    HAL_NVIC_EnableIRQ(PVD_IRQn);
    
    HAL_PWR_EnablePVD();
}

// =========================================================================
// RUTINA DE INTERRUPCIÓN: EL ÚLTIMO ALIENTO (DYING BREATH)
// =========================================================================
/**
 * @brief Manejador de la interrupción de caída de voltaje. El procesador 
 * está a milisegundos de morir por falta de energía. No hay tiempo para nada más.
 */
void HAL_PWR_PVDCallback(void) {
    
    // ¡Advertencia: Apagón en curso!
    System_Power_Failing = true;
    
    // 1. Cerrar Válvula Físicamente ¡INMEDIATO! 
    // Obligamos al pin a irse a LOW ignorando FSM y RTOS.
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); 
    
    // 2. Desbloquear acceso al dominio de Respaldo (BKP / RTC)
    HAL_PWR_EnableBkUpAccess();
    
    // 3. Guardar Variables Financieras Críticas
    // Dividimos el uint64_t en dos mitades de 32-bit porque los registros 
    // de respaldo del STM32 son de 32-bit de ancho.
    uint32_t vol_low  = (uint32_t)(volumen_acumulado_mL & 0xFFFFFFFF);
    uint32_t vol_high = (uint32_t)(volumen_acumulado_mL >> 32);
    
    // RTC_BKP_DR0, DR1 y DR2 son posiciones de SRAM soportadas por la pila CR2032
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, vol_low);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, vol_high);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, current_sale_cents);
    
    // 4. Escribir una bandera mágica que indique que el apagón fue rescatado
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, 0xA5A5); // 0xA5A5 = Firma de rescate
    
    // A partir de este instante, nos quedamos en bucle infinito hasta que el 
    // voltaje caiga a 2.0V y el BOR (Brown-out Reset) aniquile la CPU.
    while(1); 
}

// =========================================================================
// BOOTLOADER / MAIN: RESUCITACIÓN TRAS EL APAGÓN
// =========================================================================
void Check_PowerFailure_Recovery(void) {
    HAL_PWR_EnableBkUpAccess();
    
    // Si encontramos la firma mágica del "Último Aliento"
    if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3) == 0xA5A5) {
        printf("\r\n[!] RECUPERACIÓN DE APAGÓN DETECTADA [!]\r\n");
        
        // Reconstruir el uint64_t
        uint32_t v_low  = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);
        uint32_t v_high = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);
        volumen_acumulado_mL = ((uint64_t)v_high << 32) | v_low;
        
        current_sale_cents = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
        
        printf("    Venta Rescatada: %lu mL -> $%lu Centavos\r\n", 
               (uint32_t)volumen_acumulado_mL, current_sale_cents);
               
        // Borrar la bandera para el siguiente evento
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, 0x0000);
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Inmunidad Financiera ante Colapso de Red Eléctrica (Power Resilience).

Hemos enfrentado el último gran terror de la ingeniería de campo. Tu telemetría detectó en la gasera que la línea AC sufre micro-cortes devastadores cuando hay tormentas. En un software comercial estándar, un apagón repentino en medio de una venta de 2,000 pesos habría dejado la RAM en blanco. Cuando la luz regresara, la pantalla de tu máquina habría arrancado desde cero, el cliente se habría ido sin pagar lo despachado, y el dueño de la gasera habría exigido que retiraran tu Tesis de sus instalaciones.

Al implementar el **Programmable Voltage Detector (PVD)** del Cortex-M4, le has dado a tu equipo reflejos de nanosegundos. En lugar de ser víctima del apagón, tu firmware observa pasivamente la tensión. Cuando cae a 2.8V, el hardware detiene al mismísimo sistema operativo con la Interrupción de Máxima Prioridad (0). La rutina cierra brutalmente el paso del gas y, en menos de un milisegundo, fragmenta tu valiosa matemática metrológica de 64-bits y la entierra profundamente en los registros **SRAM BKP** blindados por la batería de botón CR2032. Cuando el mundo se apaga, tu dinero sobrevive en el dominio del reloj RTC. Al volver la luz, la máquina recupera el estado exacto del mililitro y el centavo donde se quedó, convirtiendo una catástrofe eléctrica en una anécdota imperceptible para el negocio.

📝 Resumen del Día