A diferencia del IWDG, el WWDG funciona con el reloj principal del sistema (Bus APB1). Esto significa que está atado a la salud del cristal de cuarzo y al PLL. Su propósito no es salvarnos de un fallo total de hardware, sino auditar la exactitud temporal de nuestro código.
El WWDG tiene un contador descendente de 7 bits (valores de 0x7F a 0x40). Para que el sistema funcione normalmente, debes recargar ("patear") el contador solo cuando su valor haya caído por debajo de un umbral que tú configuras, llamado Ventana (Window Value), pero antes de que alcance el fatídico valor de 0x3F.
El hardware de STMicroelectronics implementó esto de manera brillante usando matemáticas binarias simples. El bit número 6 del contador (T6) vale 64 (que en hexadecimal es 0x40). Mientras el contador esté entre 0x7F (127) y 0x40 (64), el bit T6 es '1'. En el instante en que la cuenta baja a 0x3F (63), el bit T6 pasa a '0'. El hardware vigila este único bit: si pasa a 0, activa el pin físico de RESET.
El WWDG tiene una característica espectacular llamada **EWI (Early Wakeup Interrupt)**. Justo antes de que el contador llegue a `0x3F` (específicamente al llegar a `0x40`), dispara una interrupción de altísima prioridad. Esto te da unos microsegundos críticos para escribir "¡FALLO DE SOFTWARE!" en la memoria EEPROM y cerrar las válvulas justo antes de que el procesador muera reiniciado.
Ejercicio 1: Asumiendo un reloj APB1 de 42MHz, configuralo para que la ventana segura se abra después de ~25 milisegundos y el reset ineludible ocurra a los ~50 milisegundos. Escribe la rutina para "patear" al perro correctamente.
/**
* @file wwdg_driver.c
* @brief Driver Bare-Metal para el Window Watchdog Timer
*/
#include "stm32f401xe.h"
#include "valve_driver.h" // Para cerrar válvulas en emergencia
// Valores calculados para una ventana entre ~25ms y ~50ms (APB1 = 42MHz)
#define VALOR_RECARGA 0x7F // 127 (Máximo valor permitido)
#define VALOR_VENTANA 0x5F // 95 (La ventana segura abre debajo de este número)
void inicializar_perro_ventana(void) {
// 1. Habilitar reloj del WWDG en el bus APB1
RCC->APB1ENR |= RCC_APB1ENR_WWDGEN;
// 2. Configurar el Registro de Configuración (CFR)
// - Limpiamos configuración previa
WWDG->CFR &= ~(WWDG_CFR_WDGTB | WWDG_CFR_W | WWDG_CFR_EWI);
// - Configurar Prescaler: WDGTB = 11 (Divisor /8)
WWDG->CFR |= (3 << WWDG_CFR_WDGTB_Pos);
// - Asignar valor de la ventana (0x5F)
WWDG->CFR |= (VALOR_VENTANA & WWDG_CFR_W);
// - Habilitar Interrupción de Despertar Temprano (Early Wakeup)
WWDG->CFR |= WWDG_CFR_EWI;
NVIC_SetPriority(WWDG_IRQn, 0); // Prioridad ineludible
NVIC_EnableIRQ(WWDG_IRQn);
// 3. Arrancar y Cargar primer valor
// El bit WDGA (bit 7) enciende el módulo, y los bits [6:0] cargan el tiempo
WWDG->CR = WWDG_CR_WDGA | VALOR_RECARGA;
}
// Función estricta para refrescar al WWDG
void refrescar_wwdg(void) {
// IMPORTANTE: Escribir en el registro CR cuando el contador es MAYOR
// al valor de la ventana causará un RESET inmediato por hardware.
// Nosotros solo recargamos a 0x7F, pero debemos asegurarnos de llamar
// a esta función en el momento matemático correcto.
WWDG->CR = VALOR_RECARGA;
}
// --- INTERRUPCIÓN DE MUERTE INMINENTE (LAST WORDS) ---
void WWDG_IRQHandler(void) {
// Entramos aquí si el contador bajó a 0x40.
// ¡En el próximo ciclo de reloj del WWDG (0x3F), la máquina morirá!
// 1. Limpiar bandera
WWDG->SR &= ~WWDG_SR_EWIF;
// 2. ACCIÓN DE EMERGENCIA CIBERFÍSICA
VALVULA_OFF();
// 3. Guardar estado de error en RAM estática o EEPROM I2C
// eeprom_write(DIR_ERROR, ERROR_WWDG_TIMEOUT);
// (El microcontrolador se reiniciará violentamente tras salir de aquí)
}
int main(void) {
inicializar_perro_ventana();
uint32_t tiempo_previo = 0;
extern volatile uint32_t milisegundos_globales; // Del SysTick (Día 98)
while(1) {
// Nuestro bucle de control crítico.
// Simulamos tareas que toman tiempo.
// ejecutar_lecturas();
// REFRESCAR WWDG: Solo dentro de la ventana de 25ms a 50ms
uint32_t delta_tiempo = milisegundos_globales - tiempo_previo;
if (delta_tiempo > 30 && delta_tiempo < 45) {
refrescar_wwdg();
tiempo_previo = milisegundos_globales;
}
// Si otra tarea bloquea el bucle y delta_tiempo sube a 50... ¡RESET!
// Si un bug corrompe el bucle y esto corre antes de 25ms... ¡RESET!
}
return 0;
}
Objetivo del día: Sincronización Estricta del Protocolo Modbus RTU.
La red Modbus que utilizará tu despachador para comunicarse con la computadora de la oficina central (a través de RS485) es implacable con el tiempo. El estándar Modbus dicta que el silencio entre tramas de datos no debe exceder los 3.5 caracteres de tiempo.
Si tu bucle principal se ejecuta demasiado lento (asfixiando al UART) o demasiado rápido (leyendo basura en la línea antes de tiempo), la comunicación colapsará. Al vincular el bucle de procesamiento del protocolo Modbus con el **WWDG**, el hardware mismo audita que la lectura de paquetes y la lógica del Despachador tomen exactamente el tiempo estipulado (entre 25ms y 50ms). Si el flujo del programa se altera por cualquier fallo electromagnético, el WWDG_IRQHandler cierra la válvula físicamente y fuerza un reinicio seguro para recuperar la comunicación. Esto es arquitectura de software verdaderamente robusta.
0x40 a 0x3F, el bit T6 se pone en cero y genera el Reset físico.