DÍA 055 DE 365

Fase 1: Fundamentos de C y Lógica de Software

📖 Teoría a Estudiar

Memoria Flash y Lógica de Guardado (Power-Loss Recovery)

En el diseño de sistemas de punto de venta (como tu despachador), perder el registro de un servicio a la mitad porque se cortó la luz es inaceptable. Sin embargo, no puedes escribir en la Memoria Flash en cada ciclo de tu Súper Bucle por dos razones: la latencia de borrado bloquearía la lectura de sensores, y la memoria se degradaría en un par de días (los sectores Flash soportan típicamente ~10,000 a 100,000 ciclos de borrado/escritura).

La solución en la industria se conoce como la estrategia del "Último Aliento" (Dying Gasp). Se basa en detectar la caída del voltaje de suministro principal antes de que el voltaje del microcontrolador baje de su mínimo operativo, y usar un capacitor de respaldo para mantener la CPU encendida los milisegundos exactos que requiere la rutina de guardado.

Fuente 24V (Red) Divisor Línea de Alerta Temprana (PVD/EXTI) Diodo Shottky LDO 3.3V C_Respaldo (Ej. 1000uF) MCU VDD (3.3V) EXTI_PIN 1. Cae el voltaje de la red (24V a 0V). 2. El Diodo impide que el capacitor se descargue hacia atrás. 3. El capacitor alimenta a la CPU lo suficiente para guardar la Flash.

Mecánica de Software para el Último Aliento

Al caer el voltaje de la red principal, el pin detector disparará una interrupción de altísima prioridad (típicamente configurada en el NVIC). El software que escribas dentro de esta interrupción debe seguir una dieta estricta:

  1. Apagar Periféricos: Deshabilitar inmediatamente pantallas LCD traseras, LEDs y módulos de comunicación para que el capacitor de reserva dure más tiempo (cada miliamperio ahorrado se traduce en milisegundos de vida).
  2. Bloquear Interrupciones: Impedir que el Súper Bucle o el temporizador del sistema (SysTick) interrumpan la delicada secuencia de escritura de la memoria Flash.
  3. Ejecutar Guardado: Desbloquear la Flash, borrar un sector preasignado, escribir el estado actual (litros y dinero) y volver a bloquear.
  4. Estado de Coma (Halt): Atrapar al procesador en un bucle infinito (while(1)) para evitar que intente volver a operar con voltajes inestables hasta que se apague por completo.

⚙️ Ejercicios Prácticos

Ejercicio 1: Programa la rutina C (simulando bare-metal) que se disparará automáticamente cuando el pin detector de caída de voltaje alerte al microcontrolador.


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

// --- 1. ESTRUCTURA DE DATOS VITALES ---
// Esto agrupa la información del despacho activo para escribirla de golpe
typedef struct {
    uint32_t id_transaccion;
    uint32_t mililitros_despachados;
    uint32_t centavos_cobrados;
    uint32_t checksum_integridad;
} RespaldoDespacho_t;

volatile RespaldoDespacho_t despacho_actual;

// Dirección reservada en la Memoria Flash (Ej. Sector 127 de un STM32)
#define FLASH_RECOVERY_ADDR 0x0801FC00


// --- 2. RUTINA DE INTERRUPCIÓN CRÍTICA (PVD / EXTI) ---
// Se invoca asíncronamente cuando el voltaje de la red cae de 24V.
// En este instante, el equipo está operando exclusivamente con la 
// energía almacenada en el capacitor de la placa (aprox. 5 a 20 ms de vida).
void PVD_CaidaVoltaje_IRQHandler(void) {
    
    // PASO A: Aislar consumos inmediatamente para extender la vida del capacitor
    // hardware_apagar_retroiluminacion_lcd();
    // hardware_cerrar_todas_las_valvulas();
    
    // PASO B: Deshabilitar todas las demás interrupciones (Bloquear el sistema)
    // Evita que un pulso del caudalímetro corrompa la rutina de escritura.
    __asm("cpsid i"); 
    
    // PASO C: Preparar los datos
    despacho_actual.checksum_integridad = 
        despacho_actual.id_transaccion ^ despacho_actual.mililitros_despachados;
    
    // PASO D: Secuencia de Escritura en Flash
    // (Pseudocódigo ilustrando los pasos bare-metal genéricos)
    
    // 1. Desbloquear los registros de la Flash mediante llaves de seguridad
    // FLASH->KEYR = 0x45670123;
    // FLASH->KEYR = 0xCDEF89AB;
    
    // 2. Comandar el borrado de la página
    // FLASH->CR |= FLASH_CR_PER; ...
    
    // 3. Copiar la estructura de la RAM hacia la memoria no volátil
    uint32_t *puntero_origen = (uint32_t *)&despacho_actual;
    uint32_t *puntero_destino = (uint32_t *)FLASH_RECOVERY_ADDR;
    uint32_t numero_de_palabras = sizeof(RespaldoDespacho_t) / 4;
    
    for (uint32_t i = 0; i < numero_de_palabras; i++) {
        // En escritura real de Flash, a menudo se escribe en medias-palabras (16 bits)
        // o se espera a que la bandera de estado de ocupado (BSY) se libere.
        puntero_destino[i] = puntero_origen[i]; 
    }
    
    // 4. Bloquear la Flash para evitar corrupciones por ruidos eléctricos finales
    // FLASH->CR |= FLASH_CR_LOCK;
    
    // PASO E: Coma Inducido
    // El microcontrolador se queda atrapado aquí esperando que la energía 
    // residual del capacitor se agote en los próximos milisegundos.
    while(1) {
        // No hacer absolutamente nada.
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Tolerancia Cero a la Pérdida de Datos Contables.

Imagina este escenario industrial: Un cliente está llenando un tanque estacionario de gran capacidad; van 150 litros despachados y la cuenta asciende a miles de pesos. De repente, un transformador de la calle explota y la estación de gas se queda a oscuras. La bomba trifásica se detiene por la falta de energía de la red. Pero en el corazón electrónico de tu máquina, el microcontrolador entra en estado de emergencia.

Durante apenas 10 milisegundos, alimentado por el capacitor electrolítico de tu PCB diseñada en KiCad, tu código "siente" la caída a través de la interrupción PVD. Ejecuta la rutina que acabas de escribir, cierra lógicamente el registro con los 150 litros exactos, lo incrusta en la memoria Flash (o en una EEPROM I2C) y se apaga. Horas más tarde, cuando la CFE restablezca el suministro, el POST (Power-On Self Test) del Día 56 leerá ese sector, encontrará el checksum válido, y reanudará la sesión mostrando en pantalla el saldo pendiente de cobro. Esto es software Fail-Safe puro.

📝 Resumen del Día

❓ Cuestionario de Autoevaluación

1. ¿Por qué en la rutina de interrupción utilizamos `__asm("cpsid i");` para desactivar las demás interrupciones globales?
Respuesta: El proceso de desbloqueo, borrado y escritura de la memoria Flash exige secuencias de tiempo y registros precisos. Si el microcontrolador es interrumpido a la mitad de este proceso por un evento banal (como el temporizador que parpadea un LED o una señal UART entrante), se gastará valioso tiempo de ejecución de la batería/capacitor remanente y la escritura en Flash fallará o quedará corrupta.

2. ¿Qué ventaja ofrece usar el Periférico PVD (Programmable Voltage Detector) interno de chips como el STM32 frente a un pin GPIO normal para detectar la caída?
Respuesta: El PVD monitorea el propio riel de alimentación interno (VDD) del chip y puede programarse para disparar una interrupción a un umbral específico (por ejemplo, cuando el voltaje baja de 2.9V a 2.7V). Esto simplifica el circuito de tu PCB, ya que no requieres resistencias externas para un divisor de tensión hacia un pin EXTI, ahorrando componentes y elevando la fiabilidad general del equipo ante fluctuaciones.