A diferencia de un apagón eléctrico (donde el PVD nos dio una advertencia de 2 milisegundos para guardar datos, Día 325), un fallo de software o un cuelgue por EMI que dispara el Independent Watchdog (IWDG) no avisa. La CPU se corta a sí misma el cuello instantáneamente. Si intentabas escribir la EEPROM en ese preciso instante, la memoria queda corrupta. Si mantenías tus variables solo en RAM, la rutina de inicialización de C (el código _start) borrará toda la RAM al reiniciar la máquina.
Para sobrevivir, necesitamos dos estrategias combinadas:
RCC_CSR (Reset and Clock Control Status Register) mantiene banderas físicas que indican qué lo mató. Si encendemos y leemos IWDGRSTF (Independent Watchdog Reset Flag), sabemos que no fue el usuario encendiendo la máquina con el interruptor, sino que fue un colapso. Esto nos permite "saltarnos" la pantalla de inicio y forzar a la Máquina de Estados a regresar directamente al estado DESPACHANDO_GAS.Paso 1: Habilitar Acceso Perpetuo a la BKP SRAM.
Por defecto, el STM32 protege su dominio de reloj (RTC) y registros de respaldo (Backup Registers) contra escrituras accidentales. En tu inicialización de Hardware, debes habilitar los relojes y llamar a HAL_PWR_EnableBkUpAccess() para poder escribir en ellos como si fueran variables normales a máxima velocidad.
Paso 2: Periodo de Guardado (Journaling).
En la interrupción del Caudalímetro (donde cuentas los mililitros despachados), agregas una línea de código que asigne el volumen actual directamente al registro `RTC_BKP_DR0`. Escribir en este registro toma 2 ciclos de reloj del bus APB. A 84 MHz, eso son 24 nanosegundos. Puedes hacerlo miles de veces por segundo sin retrasar tu máquina ni dañar la Flash.
El código a continuación se sitúa en los primeros instantes del main(). Antes de encender la pantalla LCD y antes de inicializar las Máquinas de Estado en `IDLE` (Modo reposo), el procesador evalúa su registro de reseteo RCC_CSR para decidir si acaba de ser asesinado por el Watchdog.
/**
* @file system_watchdog_resurrection.c
* @brief Rutinas de recuperación de estado (Warm Boot) post-Watchdog crash.
*/
#include "stm32f4xx_hal.h"
#include <stdio.h>
#include <stdbool.h>
// Variables Globales de Estado del Sistema
extern uint64_t volumen_acumulado_mL;
extern uint32_t current_sale_cents;
extern uint8_t system_fsm_state;
#define STATE_IDLE 0
#define STATE_DISPENSING 1
#define BKP_MAGIC_ACTIVE_SALE 0xABCD1234 // Firma que indica que la venta estaba viva
// =========================================================================
// CONTINUOUS JOURNALING (Se llama en cada interrupción del caudalímetro)
// =========================================================================
void Save_State_To_Backup_Registers(void) {
// Escribimos en nanosegundos la venta actual
uint32_t vol_low = (uint32_t)(volumen_acumulado_mL & 0xFFFFFFFF);
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, vol_low);
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, current_sale_cents);
// Marcamos que la transacción está en progreso
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, BKP_MAGIC_ACTIVE_SALE);
}
// =========================================================================
// FORENSE DE ARRANQUE (Se llama al principio de main())
// =========================================================================
void Diagnose_And_Recover_Reset_Cause(void) {
printf("\r\n[BOOT] Analizando banderas de reseteo...\r\n");
// 1. Verificar si el Watchdog Independiente (IWDG) causó este reinicio
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET) {
printf("[WARN] !WATCHDOG RESET DETECTADO! Sistema colapsó previamente.\r\n");
// Habilitar acceso a los registros de supervivencia
HAL_PWR_EnableBkUpAccess();
// 2. Verificar si el colapso ocurrió A MITAD de una venta
uint32_t magic_flag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
if (magic_flag == BKP_MAGIC_ACTIVE_SALE) {
printf("[CRIT] El colapso ocurrió con válvula abierta. !INICIANDO RESURRECCIÓN!\r\n");
// Restaurar Variables Críticas
uint32_t v_low = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);
volumen_acumulado_mL = (uint64_t)v_low; // (Simplificado para 32-bit de demostración)
current_sale_cents = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
// Forzar el estado de la Máquina de Estados a "Despachando"
system_fsm_state = STATE_DISPENSING;
// ¡Reabrir la válvula de gas INMEDIATAMENTE!
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
printf(" -> Rescatados %lu mL. Válvula reabierta.\r\n", (uint32_t)volumen_acumulado_mL);
} else {
printf("[INFO] Colapso en estado IDLE. No hay venta que rescatar.\r\n");
system_fsm_state = STATE_IDLE;
}
} else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET) {
printf("[INFO] Arranque Frío normal (Power-On Reset).\r\n");
system_fsm_state = STATE_IDLE;
}
// 3. LIMPIAR BANDERAS: Crucial para no confundir el siguiente reinicio
__HAL_RCC_CLEAR_RESET_FLAGS();
// Si la venta terminó o se rescató, limpiamos la bandera mágica
// Para que no se rescate falsamente en el futuro
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, 0x00000000);
}
Objetivo del día: Fault Tolerance, Resiliencia Estructural y Continuidad Operacional.
La diferencia entre un código de estudiante y un firmware industrial es la gestión del fracaso. Un estudiante programa la pantalla de "Bienvenido" asumiendo que el arranque siempre ocurre porque el usuario lo solicitó. Tú, como Arquitecto de Sistemas de Misión Crítica, sabes que si el microcontrolador se atasca y el Watchdog corta la ejecución, el tiempo que tarda la máquina en volver a encender es tiempo en el que el cliente cree que le robaron su dinero, o peor, tiempo en el que el gas pudo haberse desbordado.
Al implementar el Continuous Journaling hacia el dominio RTC BKP, creaste un sistema de autoguardado pasivo que opera en nanosegundos sin gastar la vida útil de la EEPROM. Y al programar la Inspección Forense de Arranque (RCC_CSR), dotaste a tu hardware de instinto de supervivencia. Ahora, si la máquina colapsa a la mitad de un despacho, el Watchdog la reinicia, el procesador lee su propia "partida de defunción", ve la bandera `IWDGRSTF`, esquiva la pantalla de inicio, recupera los mililitros exactos y vuelve a abrir la válvula. Para el operario de la gasera, la pantalla parpadeó medio segundo y la venta continuó como si nada hubiera pasado. Esto es tolerancia a fallos extrema. Esto es Bare-Metal llevado a la perfección.
.bss, destruyendo las variables.RCC_CSR (Reset and Clock Control Status Register). La bandera IWDGRSTF informa al firmware si el inicio anterior terminó en colapso.