01 · El perro y su dueño
La idea y sus límites
Un watchdog es un contador que cuenta hacia atrás a velocidad constante. Si llega a cero, reinicia el microcontrolador. Para evitarlo, el programa tiene que recargarlo (alimentarlo, patearlo, «acariciar al perro») antes de que llegue a cero, y volver a hacerlo una y otra vez durante toda su vida. Mientras el programa funciona, lo alimenta a tiempo y el perro nunca ladra. Si el programa se cuelga (un bucle infinito, una espera que no termina, un interbloqueo entre tareas, un salto a una dirección corrupta), deja de alimentarlo, el contador llega a cero y el perro reinicia el equipo. El mecanismo es simple, y esa simplicidad es su fortaleza: no depende de que el programa «sepa» que está colgado, porque un programa colgado no puede saber nada.
Pero hay que entender con precisión qué demuestra una alimentación: no que el programa funciona bien, sino que el código que alimenta al perro se ejecutó a tiempo. Todo el diseño de un watchdog consiste en que esa afirmación sea lo más parecida posible a «el sistema entero está sano». De ahí salen los límites:
| Detecta | No detecta |
|---|---|
| Un bucle infinito en el camino que alimenta al perro | Un programa que corre pero hace cosas incorrectas (un cálculo equivocado, un sensor mal leído) |
| Una espera que nunca termina (un periférico que no responde, un semáforo que nadie libera) | Una falla de una tarea que no está vigilada, si otra sigue alimentando al perro |
| Un interbloqueo (tema 22) que paraliza a quien alimenta | Un dato corrupto que no cuelga al programa |
| Un salto a una dirección corrupta o una pila destruida que desorganiza el flujo (tema 31) | Una alimentación hecha desde un lugar que sigue funcionando aunque el resto esté colgado (apartado 3) |
| Un chip que queda atascado por una interferencia eléctrica y deja de ejecutar | Un fallo del hardware externo (un sensor que dejó de enviar datos) que el programa tolera sin colgarse |
02 · El hardware
Cómo funciona y cómo se configura
Los chips suelen tener más de un watchdog, con una propiedad importante: el principal funciona con su propio reloj, independiente del reloj del sistema. Si el oscilador principal se detiene o el reloj del sistema se corrompe (una causa de cuelgue), el watchdog sigue contando y reinicia el chip. En el STM32 este es el IWDG (independent watchdog), con un oscilador interno lento (LSI, del orden de 40 kHz); en el AVR, el watchdog usa un oscilador interno de 128 kHz propio.
El tiempo de espera (timeout) sale de un divisor y un valor de recarga, y hay que calcularlo con la fórmula de la hoja de datos. Por ejemplo, para el IWDG: timeout = recarga × preescalar / fLSI. La Parte 1 del programa de Python arma una tabla con un reloj de 40 kHz nominal:
| Preescalar | Recarga | Con 40 kHz (nominal) | Con 60 kHz | Con 30 kHz |
|---|---|---|---|---|
| 4 | 4095 | 0,409 s | 0,273 s | 0,546 s |
| 32 | 1250 | 1,000 s | 0,667 s | 1,333 s |
| 64 | 1250 | 2,000 s | 1,333 s | 2,667 s |
| 256 | 4095 | 26,208 s | 17,472 s | 34,944 s |
Observá las dos últimas columnas: el oscilador interno de un perro independiente es impreciso (la hoja de datos de un STM32F103 da entre 30 y 60 kHz para un valor nominal de 40 kHz), de modo que un timeout «de 2 segundos» puede ser, en un chip dado, de 1,3 a 2,7 segundos. Es el precio de un reloj propio, simple y barato, y hay que tenerlo en cuenta al elegir el valor (apartado 4). El rango de configuración va, con el reloj nominal, desde alrededor de 0,1 ms hasta unos 26 segundos.
Otras propiedades que conviene conocer de un watchdog serio:
- Una vez activado, no se puede apagar por software. Es deliberado: si un programa corrupto pudiera desactivarlo, la protección valdría poco. En el IWDG, solo un reinicio lo detiene; en el AVR, se puede desactivar con una secuencia temporizada, a menos que el fuse
WDTONlo deje siempre encendido. - Puede activarse por hardware desde el reinicio (en los STM32, con un byte de opción): el watchdog corre desde el primer instante, antes de que ejecute ninguna línea, lo cual protege incluso el arranque.
- Se alimenta escribiendo una clave en un registro (en el IWDG, el valor
0xAAAAenKR); una escritura distinta no lo alimenta, lo que evita alimentarlo por accidente con un acceso erróneo. - Se puede congelar en depuración (en el STM32, un bit del módulo de depuración): sin eso, detener el programa en un punto de interrupción dejaría de alimentarlo y lo reiniciaría en medio de la sesión.
/* STM32 (HAL): perro independiente de 2 s nominales (LSI de 40 kHz, preescalar 64, recarga 1250) */
IWDG_HandleTypeDef hiwdg;
void watchdog_iniciar(void) {
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64; /* 40 000 / 64 = 625 Hz */
hiwdg.Init.Reload = 1250; /* 1250 / 625 Hz = 2,0 s */
HAL_IWDG_Init(&hiwdg); /* a partir de aquí corre y NO se puede detener */
}
void watchdog_alimentar(void) { HAL_IWDG_Refresh(&hiwdg); } /* escribe 0xAAAA en IWDG->KR */
/* AVR (avr-libc) */
#include <avr/wdt.h>
wdt_enable(WDTO_2S); /* perro de 2 s */
wdt_reset(); /* alimentarlo */
03 · Lo que más importa
Cómo alimentarlo bien
Casi toda la utilidad (o inutilidad) de un watchdog se decide en una pregunta: ¿desde dónde se lo alimenta? Los errores comunes son tentadores porque «funcionan» en la mesa: el equipo anda, el perro nunca vence, y nadie se entera de que no protege nada hasta que el equipo se cuelga en el campo y el perro, contento, no reinicia. La Parte 2 del programa compara tres estrategias con las mismas fallas:
| Falla (a los 3000 ms) | Desde una ISR de temporizador | Desde la tarea inactiva (idle) | Desde un supervisor |
|---|---|---|---|
| La tarea A (alta prioridad) entra en un bucle | No detecta | Detecta en 491 ms | Detecta en 376 ms |
| La tarea B queda bloqueada para siempre | No detecta | No detecta | Detecta en 376 ms |
| La tarea B entra en un bucle | No detecta | Detecta en 491 ms | Detecta en 376 ms |
| Una operación legítima de 800 ms | Sin reinicio (correcto) | Falso positivo (a los 3491 ms) | Falso positivo (a los 3376 ms) |
| Sin falla | Sin reinicio | Sin reinicio | Sin reinicio |
Cada columna enseña una lección distinta:
- Desde la interrupción de un temporizador: nunca. Es el error clásico. La interrupción del temporizador sigue disparándose aunque el programa principal esté en un bucle infinito (las interrupciones interrumpen al bucle), de modo que el perro se alimenta aunque el sistema esté muerto. La tabla lo muestra: no detecta ninguna falla. Es un watchdog que cumple con el requisito de tener un watchdog y con ninguna otra cosa.
- Desde la tarea inactiva (idle) de un RTOS: mejor, pero incompleta. La tarea inactiva solo corre cuando ninguna otra tiene trabajo, de modo que si una tarea de mayor prioridad se queda en un bucle y acapara el procesador, la inactiva no corre, no alimenta, y el perro reinicia. Pero si una tarea queda bloqueada esperando algo que nunca llega (un semáforo, una cola), no consume procesador, la inactiva sigue corriendo y alimenta al perro. La tabla muestra el hueco: no detecta una tarea bloqueada.
- Desde un supervisor que comprueba el progreso de todas las tareas: la forma correcta. Cada tarea crítica deja una señal de vida cuando completa su ciclo; una tarea supervisora de período fijo verifica que todas las señales llegaron y recién entonces alimenta al perro. Detecta las tres fallas, y lo hace en 376 ms.
/* Supervisor: el perro solo se alimenta si TODAS las tareas críticas avanzaron en este período */
#define TAREA_SENSOR (1u << 0)
#define TAREA_RADIO (1u << 1)
#define TAREA_CONTROL (1u << 2)
#define TODAS (TAREA_SENSOR | TAREA_RADIO | TAREA_CONTROL)
static volatile uint32_t vivas; /* bits que las tareas levantan al completar su ciclo */
void watchdog_avance(uint32_t tarea) { /* cada tarea lo llama UNA vez al final de su ciclo, si todo salió bien */
__disable_irq(); vivas |= tarea; __enable_irq();
}
void tarea_supervisor(void *p) { /* período fijo, menor que el timeout del perro */
for (;;) {
vTaskDelay(pdMS_TO_TICKS(125));
__disable_irq(); uint32_t v = vivas; vivas = 0; __enable_irq();
if (v == TODAS && salud_del_sistema_ok()) { /* además: pila con margen, colas sanas, tensión correcta... */
watchdog_alimentar(); /* el ÚNICO lugar del programa que alimenta al perro */
} else {
caja.hito = hito_actual(); /* dejar evidencia de qué falta (tema 37) */
caja.faltan = (uint16_t)(TODAS & ~v);
/* no se alimenta: el perro vencerá y reiniciará al equipo */
}
}
}
Las reglas que se desprenden de todo esto:
- Un único lugar alimenta al perro. Si hay varios (cada módulo alimentándolo por su cuenta), basta que uno funcione para ocultar que los demás se colgaron.
- Se alimenta como consecuencia del progreso, no antes ni «por si acaso». No al principio del bucle, no en la rutina de espera, no dentro de una función de uso general que se llama desde cualquier parte.
- Nunca se alimenta dentro de un bucle de espera. Un
while (!listo) { wdt_reset(); }está diciéndole al perro «sigo vivo» mientras espera algo que quizás nunca ocurra: el cuelgue que se quería detectar. La espera debe tener un tiempo máximo propio, y si lo excede, tratarse como un error. - Se alimenta después de una verificación de salud, no solo de que el código corrió: la pila con margen (tema 29), las colas sin saturar, los datos del sensor dentro de rango, la tensión de la batería correcta. Cuanto más cerca esté la condición de «el sistema está sano», mejor el perro.
- El supervisor mismo tiene que poder colgarse y ser detectado. Por eso es una tarea o un camino real del programa (que si se cuelga, deja de alimentar), y no una interrupción que corre sola.
Un detalle sobre la operación legítima larga (la cuarta fila): un borrado de flash de 800 ms (tema 28) o una actualización de firmware pueden bloquear al sistema durante más que el timeout, y el perro lo reinicia: un falso positivo. Las salidas son dos: elegir un timeout mayor que la operación más larga permitida (apartado 4), o alimentar al perro explícitamente durante la operación, en puntos controlados (entre bloques de una copia larga), y no desde una ISR que alimente sola.
04 · Un compromiso con dos límites
Elegir el timeout
El tiempo de espera del perro se encuentra entre dos límites opuestos:
- Un límite inferior: debe ser mayor que el tiempo máximo legítimo entre dos alimentaciones. Si el peor ciclo del programa, incluyendo las operaciones lentas, es de 160 ms, un timeout de 100 ms reiniciará al equipo en funcionamiento normal. La regla práctica es dejar al menos el doble del peor ciclo, para absorber variaciones, interrupciones y la imprecisión del reloj del perro.
- Un límite superior: debe ser menor que el tiempo durante el cual el sistema puede quedar sin control sin consecuencias. Si un calefactor debe estar apagado en menos de 1 segundo ante una falla, un timeout de 5 s es inútil. Y a ese tiempo hay que restar lo que tarda en reiniciar y volver al estado seguro (el reinicio y el arranque, quizás 150 ms).
Además, hay que tener en cuenta la imprecisión del oscilador: con un reloj de 30 a 60 kHz para uno nominal de 40 kHz, el timeout real es de 0,667 a 1,333 veces el nominal. Hay que garantizar los dos límites en ambos extremos. La Parte 4 lo calcula con un ejemplo:
| Dato | Valor | Cálculo |
|---|---|---|
| Peor ciclo (120 ms + una operación de 40 ms) | 160 ms | — |
| Límite inferior efectivo (el doble del peor ciclo) | 320 ms | 2 × 160 |
| Límite de seguridad de la salida | 1000 ms | Dato de diseño |
| Reinicio y arranque | 150 ms | Dato medido |
| Límite superior efectivo | 850 ms | 1000 − 150 |
| Nominal mínimo (reloj rápido: el perro vence antes) | 480 ms | 320 / 0,667 |
| Nominal máximo (reloj lento: el perro vence después) | 637,5 ms | 850 / 1,333 |
| Elección | 550 ms (real entre 367 y 733 ms) | Dentro de [480; 637,5], con margen en los dos lados |
Si el intervalo resultara vacío (el nominal mínimo mayor que el máximo), el diseño no tiene solución con ese reloj: hay que acortar el peor ciclo, aflojar el requisito de seguridad, o pasar a un perro con un reloj más preciso (el watchdog de ventana de un STM32 se alimenta del reloj del bus, mucho más exacto; un perro externo con cristal también). Y hay otra interacción: el sueño profundo (apartado 8) cambia la cuenta, porque durante el sueño no hay quien alimente al perro.
05 · Alimentar a tiempo, no solo antes
El watchdog de ventana
El perro independiente tiene un punto ciego: un programa que se cuelga dentro del propio código que alimenta al perro. Un bucle que se atascó incluyendo la llamada a wdt_reset() alimenta al perro en cada vuelta, miles de veces por segundo, y el perro nunca vence aunque el programa esté completamente fuera de control. La Parte 3 lo muestra: con un perro independiente de 500 ms, el bucle atascado que alimenta cada 1 ms no se detecta nunca.
El watchdog de ventana (window watchdog) cierra ese hueco con una condición más: además de no alimentarlo demasiado tarde, no se lo puede alimentar demasiado pronto. Existe una ventana de tiempo, entre un mínimo y un máximo desde la última alimentación, y solo en esa ventana la alimentación es válida. Alimentarlo antes de que se abra provoca un reinicio inmediato, igual que no alimentarlo antes de que se cierre. Con el mismo bucle atascado, la Parte 3 da el resultado opuesto: el perro de ventana reinicia al equipo en el primer milisegundo, porque la alimentación llegó a destiempo.
El watchdog de ventana del STM32 (WWDG) funciona con un contador de 7 bits que baja con el reloj del bus (PCLK1 dividido por 4096 y por un preescalar de 1, 2, 4 o 8). Se alimenta recargando el contador, y solo se permite cuando su valor está por debajo del valor de ventana W. El reinicio ocurre si el contador baja de 0x40, o si se lo recarga mientras está por encima de W. La duración de la ventana es calculable:
| Magnitud | Fórmula | Valor |
|---|---|---|
| Duración de un conteo | 4096 × 8 / 36 MHz | 0,91 ms |
Tiempo hasta el reinicio (de 0x7F a 0x3F: 64 conteos) | 4096 × 8 × 64 / 36 MHz | 58,25 ms |
Ventana con W = 0x50 (47 conteos hasta abrirse) | 4096 × 8 × 47 / 36 MHz | se abre a los 42,8 ms |
| Ventana de alimentación válida | — | de 42,8 ms a 58,25 ms desde la recarga |
La ventana obliga a diseñar la alimentación con cuidado: el supervisor no puede alimentar cada 10 ms porque sí, sino con un período alineado al tramo permitido (en la simulación, al 60 % del timeout). El laboratorio lo muestra: en modo ventana, alimentar cada 10 ms desde una interrupción o desde la tarea inactiva produce un reinicio constante; el supervisor con un período de 0,6 veces el timeout lo hace bien. Y el WWDG ofrece algo valioso para el diagnóstico: una interrupción de aviso previo (early wakeup interrupt) cuando el contador llega a 0x40, unos instantes antes del reinicio, en la que el programa puede guardar el PC, los hitos y el estado (apartado 7).
| Aspecto | Independiente (IWDG, AVR WDT) | De ventana (WWDG) |
|---|---|---|
| Reloj | Propio, lento e impreciso (30–60 kHz en el ejemplo) | El del bus del sistema: preciso, pero depende de él |
| Detecta | Falta de alimentación | Falta de alimentación y alimentación fuera del tramo permitido |
| Si el reloj del sistema falla | Sigue funcionando y reinicia | Se detiene con el reloj del bus: no protege contra eso |
| Rango de tiempos | De milisegundos a decenas de segundos | De microsegundos a decenas de milisegundos |
| Aviso previo | No, en el IWDG (el modo «interrupción y reinicio» del AVR sí lo tiene) | Sí (interrupción de despertar temprano) |
| Se usa para | La red de seguridad contra cualquier cuelgue | Detectar un programa que corre pero fuera de control, con tiempos de ciclo cortos y regulares |
Los dos son complementarios, y los sistemas serios usan ambos: el independiente como red de seguridad que sobrevive a la falla del reloj, y el de ventana para vigilar que el ciclo principal se ejecute con la cadencia esperada.
06 · Un perro que no vive en el chip
El watchdog externo
Los perros internos comparten el chip con el programa que vigilan, y por lo tanto comparten algunas de sus fallas: una alimentación que se hunde, un error en la configuración del propio perro, una falla del silicio. Un watchdog externo (o supervisor) es un circuito integrado separado, con su propio reloj, que el microcontrolador alimenta mediante un pin (un pulso o un cambio de nivel) y que, si deja de recibirlo, reinicia al microcontrolador por su pin de reset. Hay familias comerciales, como los supervisores MAX6369 a MAX6374 o los TPS3823, y también temporizadores de ultra bajo consumo como el TPL5010 (que despierta al microcontrolador y espera una señal de «hecho»; si no llega, lo reinicia).
| Ventaja | Por qué importa |
|---|---|
| Independencia total (reloj, alimentación y lógica propios) | Funciona aunque el microcontrolador esté completamente colgado, su reloj roto o su perro interno mal configurado o desactivado por error |
| Reinicio por el pin de reset | Reinicia el chip de forma más completa que un perro interno, y puede ser usado también para reiniciar otros circuitos (un módulo de comunicación que se colgó) |
| Supervisión de la tensión (muchos integran un detector de baja tensión) | Mantiene el microcontrolador en reset mientras la alimentación no es válida: una barrera contra el brown-out (tema 37) independiente de la del chip |
| Posibilidad de cortar y volver a dar la alimentación | Para recuperar equipos remotos con periféricos que no se recuperan con un simple reset (un módem celular, un sensor) |
| Consumo mínimo y control del sueño (en los de nanopotencia) | Puede ser el reloj que despierta al sistema y a la vez vigila que haga su trabajo |
Los costos son reales: un componente más (dinero, espacio, un pin del microcontrolador) y otra cosa que configurar y probar. Se justifica en los productos donde un cuelgue es costoso o peligroso (equipos remotos de difícil acceso, de seguridad, médicos, industriales) y no vale la pena en un juguete. La regla de los sistemas críticos es defensa en capas: el perro de ventana vigila el ciclo, el independiente protege contra la falla del reloj, el externo contra casi todo lo demás, y cada capa cubre la falla de la anterior.
Un último nivel, para productos con más de un procesador: cada uno vigila al otro. El principal manda señales de vida al secundario (que reinicia al principal si faltan), y el secundario las manda al principal. Es lo que hacen, por ejemplo, un módulo de comunicaciones y un microcontrolador de aplicación conectados por una UART.
07 · De rescate a información
Cuando vence: convertirlo en información
Un watchdog que vence es, en el mejor caso, un equipo que se recuperó, y en el peor, un equipo que seguirá fallando a cada rato sin que nadie sepa por qué. Su valor completo aparece cuando cada rescate deja evidencia, usando lo del tema 37: la bandera de causa (hardware), los hitos de ejecución y los contadores de la caja negra. Hay tres momentos en que se puede dejar esa evidencia:
- Antes del vencimiento, en el supervisor: cuando decide no alimentar al perro, sabe qué tarea no dio señal de vida. Si lo anota (
caja.faltanen el ejemplo), el próximo arranque sabe qué tarea falló, aunque el perro la haya reiniciado. - En el aviso previo: con el watchdog de ventana del STM32, o con el modo de «interrupción y reinicio» del AVR, hay una interrupción unos instantes antes del reinicio. El código puede guardar el último hito y el estado de la pila.
- Al arrancar: la bandera de watchdog (
IWDGRSTF,WDRF) confirma que fue el perro, y se actualizan los contadores de reinicios seguidos para detectar un bucle (tema 37).
/* AVR: modo «interrupción y reinicio». El primer vencimiento llama a la ISR (hay tiempo para anotar); el segundo, sin haber alimentado, reinicia. */
#include <avr/wdt.h>
#include <avr/interrupt.h>
ISR(WDT_vect) { /* corre en el primer vencimiento, antes del reinicio */
caja.motivo = MOTIVO_WATCHDOG_AVISO;
caja.hito_colgado = caja.hito; /* el último hito alcanzado: dónde se colgó el programa */
/* el hardware desactiva la interrupción de este modo: el próximo vencimiento reinicia */
}
void watchdog_iniciar_con_aviso(void) {
cli();
wdt_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE); /* abre la ventana de cambio de configuración */
WDTCSR = (1 << WDIE) | (1 << WDE) | (1 << WDP2) | (1 << WDP1); /* interrupción y reinicio, timeout de 1 s */
sei();
}
Y tras el rescate, lo importante es qué se hace con él. Reiniciar es solo el primer paso, y repetirlo sin fin es el bucle de reinicios del tema 37. La política completa es: registrar la causa y el hito; contar los reinicios seguidos; en el primero, arrancar normalmente; en el tercero, modo seguro (tema 39); y enviar la información al servidor para detectar patrones en la flota. Además, hay que pensar qué pasa con las salidas durante el reinicio: al reiniciar, los pines pasan a alta impedancia, y el circuito externo debe estar diseñado para que ese estado sea seguro (tema 27). Un watchdog que reinicia con el solenoide energizado, o con el calefactor encendido y flotante, cumple su función con el software y la falla en el hardware.
08 · Donde se complica
Casos delicados
| Situación | El problema | Qué hacer |
|---|---|---|
| Sueño profundo (temas 34 y 35) | El perro independiente de un STM32 sigue corriendo en stop y standby y no se puede detener; si el equipo duerme 60 s y el perro vence a los 26 s como máximo, lo reinicia dormido | Despertar antes de que venza (con el RTC o un temporizador de bajo consumo) solo para alimentarlo, o usar un perro externo de nanopotencia que despierte al sistema; en un AVR, el perro puede ser la propia fuente de despertar periódico (modo de interrupción) |
| Depuración | Al parar el programa en un punto de interrupción, el perro sigue contando y reinicia el equipo en medio de la sesión | Congelar el perro cuando el depurador detiene el núcleo (en el STM32, el bit DBG_IWDG_STOP del módulo de depuración); y recordar volver a probar sin depurador |
| Arranque largo | Si el perro corre desde el reset (activado por hardware) y el arranque (la inicialización, un autotest, la copia de una actualización) dura más que su timeout, vence en el arranque y entra en un bucle de reinicios | Alimentar durante el arranque en puntos controlados, o un timeout inicial más largo; verificar el tiempo de arranque en el peor caso |
| Operaciones largas legítimas (borrado de flash, actualización, cálculo pesado) | Bloquean al programa más que el timeout y generan un falso positivo | Alimentar explícitamente entre las etapas de la operación; o un timeout mayor, sabiendo el costo |
| Interrupciones muy frecuentes | Una tormenta de interrupciones puede dejar sin tiempo a las tareas y, si una ISR alimenta al perro, ocultarlo | No alimentar desde ninguna ISR; que el supervisor, de prioridad adecuada, compruebe el progreso |
| Actualización de firmware (tema 42) | Un firmware nuevo con un error de alimentación puede dejar al equipo sin protección, o en un bucle de reinicios que impida volver atrás | El cargador de arranque tiene su propio manejo del perro; contar reinicios y volver al firmware anterior si el nuevo no confirma que arrancó bien |
| Un RTOS con muchas tareas | No todas son críticas, y las que tienen períodos muy distintos complican la supervisión | Cada tarea crítica con su señal de vida y su plazo; el supervisor las revisa según el período de cada una |
Y una regla que atraviesa todo: un watchdog debe probarse. Muchos equipos salen de fábrica con un perro que nunca se probó, y resulta que estaba desactivado por un fuse mal grabado, se alimentaba desde una interrupción o tenía un timeout tan largo que no servía. La prueba es simple y obligatoria: provocar un cuelgue a propósito (un bucle infinito) y comprobar que el equipo reinicia en el tiempo esperado, deja la causa registrada y vuelve a un estado seguro.
09 · Comprobar que funciona
Verificación y diagnóstico
| Qué verificar | Cómo | Resultado esperado |
|---|---|---|
| Que el perro está activo | Leer el registro de estado del perro tras iniciar el equipo (o verificar el byte de opción o el fuse) | El perro corre; no se puede desactivar por software |
| Que detecta un cuelgue | Insertar a propósito un bucle infinito en cada tarea crítica, de a una | El equipo reinicia en un tiempo cercano al timeout, con causa «watchdog» y el hito de esa tarea |
| Que detecta una tarea bloqueada | Bloquear una tarea en un semáforo que nadie libera | El supervisor deja de alimentar y el perro reinicia; caja.faltan indica la tarea |
| Que no hay falsos positivos | Correr el equipo durante horas con la peor carga y con todas las operaciones lentas (borrado de flash, comunicaciones) | Cero reinicios por watchdog en funcionamiento normal |
| El margen del timeout | Medir el peor tiempo entre alimentaciones y compararlo con el timeout real (con el reloj en sus dos extremos) | El peor tiempo es menos de la mitad del timeout efectivo mínimo |
| El tiempo de recuperación | Medir desde el cuelgue hasta que el equipo está operativo y con las salidas seguras | Menor que el límite de seguridad del producto |
| El estado de las salidas al reiniciar | Observar las salidas peligrosas con un analizador durante un reinicio por watchdog | Las salidas quedan en el estado seguro durante todo el reinicio |
| Con sueño profundo | Dejar el equipo durmiendo más que el timeout | El equipo despierta a alimentarlo, sin reinicios espurios |
| Síntoma | Causa probable | Qué hacer |
|---|---|---|
| El equipo se cuelga en el campo y el watchdog no reinicia | Se alimenta desde una ISR o desde un lugar que sigue funcionando; o está desactivado | Mover la alimentación a un supervisor condicionado; verificar la activación; probar con un cuelgue provocado |
| Reinicios por watchdog aleatorios en funcionamiento normal | Una operación legítima más larga que el timeout; un timeout demasiado ajustado; un reloj del perro rápido | Medir el peor ciclo; aumentar el timeout con los dos límites; alimentar durante las operaciones largas |
| Un bucle de reinicios por watchdog al arrancar | El arranque dura más que el timeout, o el perro corre desde el reset | Alimentar en el arranque; revisar el tiempo de arranque; contar reinicios y entrar en modo seguro (tema 37) |
| En depuración, el equipo se reinicia al parar en un punto de interrupción | El perro sigue contando con el núcleo detenido | Congelar el perro en depuración |
| Un perro de ventana reinicia siempre | Se alimenta fuera del tramo permitido (demasiado pronto) | Ajustar el período del supervisor a la ventana; revisar quién más alimenta |
| El equipo dormido se reinicia a cada rato | El perro no se puede detener y el sueño dura más que su timeout | Despertar a alimentarlo; un perro externo de nanopotencia |
| El watchdog vence y no hay pistas del porqué | Sin hitos, sin aviso previo, sin registro del supervisor | Agregar hitos, caja.faltan y la interrupción de aviso previo (tema 37) |
| Tras un watchdog, las salidas hacen cosas raras | El estado de los pines durante el reinicio no es seguro | Un diseño de hardware con la salida en estado seguro por defecto (resistencias de polarización, interruptor de carga) |
10 · Analogía con programación
Probar el modelo en Python
El programa tiene cuatro partes: la tabla de timeouts de un perro independiente con un reloj impreciso; la simulación del comportamiento de tres estrategias de alimentación (ISR, tarea inactiva, supervisor) ante cuatro situaciones (dos cuelgues distintos, un bucle de prioridad baja y una operación larga legítima), con un paso por milisegundo; la demostración de un bucle atascado dentro de la propia rutina que alimenta, con un perro independiente y con uno de ventana; y el cálculo del timeout que cumple los dos límites con un reloj impreciso.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
# El watchdog: cuánto vale el timeout, qué estrategias de alimentación detectan qué, qué agrega la ventana y cómo elegir el tiempo.
print("Parte 1: temporizador independiente (tipo IWDG) con un reloj lento de 40 kHz nominal (la hoja de datos da 30 a 60 kHz)")
F_LSI = 40_000
print(f" {'preescalar':>10} | {'recarga':>7} | {'nominal':>9} | {'con 60 kHz':>10} | {'con 30 kHz':>10}")
for presc, recarga in ((4, 4095), (32, 1250), (64, 1250), (256, 4095)):
t = lambda f: recarga * presc / f
print(f" {presc:>10} | {recarga:>7} | {t(F_LSI):>7.3f} s | {t(60_000):>8.3f} s | {t(30_000):>8.3f} s")
print(f" rango posible con el reloj nominal: de {4 * 1 / F_LSI * 1000:.2f} ms (preescalar 4, recarga 1) a {256 * 4096 / F_LSI:.1f} s (preescalar 256, recarga 4096)")
def simular(T, modo, estrategia, falla, tf=3000, dur=8000):
"""Mil pasos de 1 ms por segundo: ¿cuándo (y si) el perro reinicia el equipo? La falla se inyecta en tf."""
abre = T / 2 # la ventana se abre a la mitad del timeout (solo en modo ventana)
periodo_sup = round(0.6 * T) if modo == "ventana" else round(0.25 * T)
activa = lambda t: (falla in ("A_bucle", "B_bloq", "B_bucle") and t >= tf) or (falla == "largo" and tf <= t < tf + 800)
inanicion = lambda t: (falla in ("A_bucle", "B_bucle") and t >= tf) or (falla == "largo" and tf <= t < tf + 800)
ultimo = 0
for t in range(1, dur):
if estrategia == "ISR del temporizador": alimenta = t % 10 == 0
elif estrategia == "tarea inactiva (idle)": alimenta = t % 10 == 0 and not inanicion(t)
else: alimenta = t % periodo_sup == 0 and not activa(t) # supervisor: solo si todas las tareas avanzaron
if alimenta:
if modo == "ventana" and t - ultimo < abre: return ("prematuro", t)
ultimo = t
if t - ultimo > T: return ("reinicio", t)
return ("nada", None)
def veredicto(res, falla, tf=3000):
tipo, t = res
if tipo == "prematuro": return f"RESET por alimentar fuera de la ventana (t = {t} ms)"
real = falla in ("A_bucle", "B_bloq", "B_bucle")
if tipo == "reinicio": return f"detecta en {t - tf} ms" if real else f"FALSO POSITIVO (t = {t} ms)"
return "NO DETECTA" if real else "sin reinicio (correcto)"
print()
print("Parte 2: tres maneras de alimentar un perro independiente de 500 ms; la falla ocurre a los 3000 ms")
FALLAS = (("A_bucle", "tarea A en un bucle (alta prioridad)"), ("B_bloq", "tarea B bloqueada para siempre"), ("B_bucle", "tarea B en un bucle"), ("largo", "operación legítima de 800 ms"), ("ninguna", "sin falla"))
print(f" {'falla':<38} | " + " | ".join(f"{e:<26}" for e in ("ISR del temporizador", "tarea inactiva (idle)", "supervisor")))
for clave, texto in FALLAS:
celdas = [veredicto(simular(500, "independiente", e, clave), clave) for e in ("ISR del temporizador", "tarea inactiva (idle)", "supervisor")]
print(f" {texto:<38} | " + " | ".join(f"{c:<26}" for c in celdas))
print()
print("Parte 3: un bucle que se atascó dentro de la propia rutina que alimenta al perro (alimenta cada 1 ms)")
def bucle_alimentando(T, modo, dur=3000):
ultimo = 0
for t in range(1, dur):
if modo == "ventana" and t - ultimo < T / 2: return f"reinicia en t = {t} ms (alimentó a destiempo)"
ultimo = t # alimenta en cada milisegundo
if t - ultimo > T: return "reinicia por vencimiento"
return "NUNCA reinicia: el programa está colgado y el perro, contento"
for modo in ("independiente", "ventana"):
print(f" perro {modo:<13} de 500 ms: {bucle_alimentando(500, modo)}")
print()
print("Parte 4: elegir el timeout (ciclo del programa de 120 ms más una operación de 40 ms; la salida debe quedar segura en 1 s; el reinicio y arranque tardan 150 ms)")
ciclo_max, seguridad, arranque = 120 + 40, 1000, 150
t_min = 2 * ciclo_max # al menos el doble del peor ciclo
t_max = seguridad - arranque # el equipo tiene que volver antes del límite de seguridad
print(f" el timeout efectivo debe estar entre {t_min} ms y {t_max} ms")
print(f" con un reloj de 30 a 60 kHz el timeout real varía de x{40 / 60:.3f} a x{40 / 30:.3f} del nominal")
nom_min, nom_max = t_min / (40 / 60), t_max / (40 / 30)
print(f" por eso el valor NOMINAL debe estar entre {nom_min:.0f} ms y {nom_max:.1f} ms; una elección razonable: 550 ms (real entre {550 * 40 / 60:.0f} y {550 * 40 / 30:.0f} ms)")
Resultado, parte 1: con 40 kHz, preescalar 4 y recarga 4095 el timeout es de 0,409 s; con preescalar 32 y recarga 1250, 1,000 s; con 64 y 1250, 2,000 s (de 1,333 s con 60 kHz a 2,667 s con 30 kHz); y con 256 y 4095, 26,208 s; el rango va de 0,10 ms a 26,2 s. Parte 2: con un perro de 500 ms, alimentar desde la ISR del temporizador no detecta ninguna falla; desde la tarea inactiva detecta el bucle de la tarea A (491 ms) y el de la B (491 ms) pero no la tarea B bloqueada; y el supervisor detecta las tres, a los 376 ms. Con la operación legítima de 800 ms, la tarea inactiva y el supervisor dan un falso positivo (a los 3491 y los 3376 ms) y la ISR no. Parte 3: con un bucle atascado que alimenta cada 1 ms, el perro independiente de 500 ms nunca reinicia, y el de ventana reinicia en t = 1 ms. Parte 4: el timeout efectivo debe estar entre 320 y 850 ms; con el reloj entre ×0,667 y ×1,333 del nominal, el nominal debe estar entre 480 y 637,5 ms, y 550 ms (real de 367 a 733 ms) cumple.
Probá cambiar el timeout de la Parte 2 a 1000 ms y mirá que los falsos positivos de la operación de 800 ms desaparecen, a costa de que las detecciones tarden más (la compensación del apartado 4), o cambiá la falla a un bloqueo que ocurra a los 500 ms para ver el efecto del instante de la falla en el tiempo de detección. La lección del programa es doble: no todas las maneras de alimentar al perro lo vuelven útil, y un timeout se elige con dos límites, no con un número redondo.
11 · Exploración interactiva
El perro guardián
El laboratorio simula, paso a paso en milisegundos, un programa con dos tareas críticas vigilado por un watchdog. Elegís el tipo de perro (independiente o de ventana), el timeout, cómo se lo alimenta (desde una ISR, desde la tarea inactiva o desde un supervisor) y qué falla ocurre a los 3000 ms. El laboratorio muestra si el perro detecta la falla, cuánto tarda, o si reinicia sin motivo (un falso positivo) o por alimentarlo a destiempo.
Qué detecta cada manera de alimentar al perro
Línea de tiempo
—
Elegí una estrategia y una falla.
Recorrido guiado
- Con los valores iniciales (perro independiente de 500 ms, ISR del temporizador, la tarea A en un bucle), el resultado es «NO DETECTA»: el perro nunca venció, porque la interrupción sigue alimentándolo aunque la tarea A esté colgada. Probá las otras fallas con esta estrategia: no detecta ninguna.
- Cambiá a La tarea inactiva: ahora detecta la tarea A en un bucle (unos 491 ms después de la falla: la tarea A acapara el procesador y la inactiva no llega a correr). Probá «La tarea B queda bloqueada»: no detecta, porque la tarea bloqueada no consume procesador y la inactiva sigue alimentando al perro.
- Cambiá a Un supervisor: detecta las tres fallas (376 ms), incluida la tarea B bloqueada. Es la única estrategia que verifica el progreso de las tareas.
- Probá «Una operación legítima de 800 ms» con el supervisor y un timeout de 500 ms: falso positivo, porque el timeout es menor que la operación. Subí el timeout a 2 s: sin reinicio. Es el límite inferior del apartado 4: el timeout debe superar la operación legítima más larga.
- Cambiá el tipo de perro a De ventana con la ISR (cada 10 ms): reinicia a los pocos milisegundos, por alimentarlo antes de que se abra la ventana. Y con la tarea inactiva pasa lo mismo. Solo el supervisor, con su período alineado a la ventana (0,6 veces el timeout), funciona.
- Con el supervisor y el perro de ventana, probá las fallas de a una. El perro detecta cada una igual que el independiente. La ventana no agrega detección de tareas colgadas; agrega la detección de alimentar a destiempo (el bucle atascado dentro de la rutina que alimenta, que se estudió en la Parte 3 del programa), un caso que el modelo del laboratorio no incluye.
Desafío: ¿cómo detectarías una tarea que sigue «viva» pero hace trabajo inútil (por ejemplo, reintenta eternamente una comunicación)?
Con una señal de vida condicionada al resultado, no solo a la ejecución. Si la tarea de comunicación reintenta sin éxito, sigue ejecutándose y dando «señal de vida» al supervisor, pero no hace progreso real. La solución es que la tarea solo avise cuando completó trabajo útil (envió un dato con confirmación, recibió una respuesta válida), o que lo haga con una condición de calidad: if (reintentos < MAX) watchdog_avance(TAREA_RADIO);. Si los reintentos superan el máximo, la tarea deja de dar señal, el supervisor no alimenta al perro y el equipo reinicia (con el motivo registrado en la caja negra). Es una decisión de diseño: un reinicio puede ser una buena respuesta a una radio colgada (reinicia también el módulo), o una mala (si el problema es que no hay cobertura, reiniciar no lo arregla y solo gasta batería). En ese caso lo que corresponde es un contador de reinicios por esta causa y, pasado un límite, no volver a intentar durante un tiempo largo.
12 · Método de trabajo
Método: un watchdog que sirve
- Activá el watchdog desde el principio del proyecto, no al final: así todo el desarrollo se hace con él puesto y los falsos positivos aparecen temprano. Si el chip lo permite, activalo por hardware, desde el reset.
- Definí qué significa «el sistema está sano»: qué tareas deben avanzar y qué condiciones deben cumplirse (pila, colas, tensión, datos). Esa es la condición de alimentación.
- Alimentá desde un único lugar: un supervisor que comprueba las señales de vida de todas las tareas críticas. Nunca desde una ISR, nunca dentro de un bucle de espera, nunca desde una función de uso general.
- Elegí el timeout con los dos límites: mayor que el doble del peor ciclo (con las operaciones lentas) y menor que el tiempo de seguridad menos el reinicio, considerando la imprecisión del reloj del perro en los dos extremos.
- Agregá un perro de ventana si el ciclo es corto y regular, para atrapar el programa que corre fuera de control, y un perro externo si un cuelgue es costoso o peligroso. Cada capa cubre la falla de la anterior.
- Resolvé las interacciones: alimentar durante el arranque y las operaciones largas, congelar el perro en depuración, despertar a alimentarlo si hay sueño profundo.
- Dejá evidencia en cada rescate: el supervisor anota qué tarea falló, los hitos de ejecución, el aviso previo si el chip lo tiene, y al arrancar se registra la causa y se cuentan los reinicios (tema 37).
- Diseñá el hardware para el reinicio: las salidas peligrosas en estado seguro cuando los pines pasan a alta impedancia.
- Probá el watchdog provocando cuelgues a propósito, tarea por tarea, y comprobá el tiempo de recuperación, la causa registrada y el estado de las salidas. Un perro que nunca se probó es una suposición.
- Tratá cada vencimiento en el campo como un error pendiente: el perro rescató al equipo, pero la causa sigue ahí.
13 · Comprensión
Confusiones frecuentes
«Si el equipo tiene watchdog, no se puede colgar»
Se puede colgar tanto como siempre; lo que cambia es que se reinicia después. Y solo si el perro está bien alimentado: un perro alimentado desde una ISR no detecta ningún cuelgue del programa principal. Tener un watchdog no es lo mismo que tener un watchdog que funcione.
«Alimento al perro en la interrupción del temporizador, así nunca vence por error»
Y tampoco vence cuando debería. La interrupción sigue corriendo con el programa principal colgado, de modo que el perro nunca ve el cuelgue. Es exactamente lo contrario de lo que se quiere: se evitan los falsos positivos a costa de eliminar toda la detección.
«Pongo wdt_reset() en todos los bucles para que no venza»
Con eso se silencia al perro justo donde más información daría: un bucle que espera algo que nunca llega es el cuelgue clásico, y alimentarlo dentro del bucle lo esconde. Las esperas deben tener su propio tiempo máximo y tratarse como un error si se agota.
«Un timeout largo es más seguro porque evita falsos reinicios»
Evita falsos reinicios, pero deja al sistema fuera de control durante todo ese tiempo antes de rescatarlo. Si el calefactor debe apagarse en un segundo, un perro de 10 s es inútil. El timeout tiene un límite superior, que es un requisito de seguridad, no una preferencia.
«El oscilador del perro es exacto, así que 2 s son 2 s»
El oscilador interno de un perro independiente es impreciso (en el ejemplo, entre 30 y 60 kHz para un valor nominal de 40): un timeout de 2 s nominales puede ser de 1,3 a 2,7 s en un chip dado. Los límites hay que garantizarlos en los dos extremos.
«El watchdog de ventana es solo un watchdog más preciso»
Hace algo cualitativamente distinto: detecta no solo la falta de alimentación sino también la alimentación a destiempo, es decir, el programa que corre fuera de control (un bucle que incluye la llamada que alimenta). El perro independiente no puede detectarlo.
«Con el perro del chip alcanza para cualquier producto»
Comparte con el programa el chip, su alimentación y a veces su reloj; una falla del chip puede desactivarlo. Para productos donde un cuelgue es costoso o peligroso, se agrega un perro externo con alimentación y reloj propios, y se usan capas.
«Después de que el watchdog reinicia, el problema está resuelto»
El equipo se recuperó, pero la causa sigue ahí y volverá a ocurrir. Un vencimiento es información: hay que registrarlo, contarlo, ver el patrón y arreglar la causa. Un equipo que se rescata solo una vez por semana durante años está pidiendo ayuda en silencio.
14 · Práctica guiada
Ejercicios
1. Calcular un timeout
Un perro independiente tiene un reloj de 40 kHz nominales, preescalar 128 y recarga 625. ¿Qué timeout nominal tiene? ¿Entre qué valores puede estar si el reloj real va de 30 a 60 kHz?
Ver solución
timeout = recarga × preescalar / f = 625 × 128 / 40 000 = 80 000 / 40 000 = 2,0 s. Con 60 kHz: 80 000 / 60 000 = 1,333 s. Con 30 kHz: 80 000 / 30 000 = 2,667 s. El timeout real está entre 1,33 y 2,67 s; cualquier decisión de diseño que dependa de él (el peor ciclo, el límite de seguridad) debe cumplirse en esos dos extremos.
2. Elegir el timeout
El peor ciclo de un programa es de 80 ms. Ante una falla, la salida debe quedar segura en 500 ms, y el reinicio más el arranque tardan 100 ms. El reloj del perro es de ×0,667 a ×1,333 del nominal. ¿Entre qué valores debe estar el timeout nominal? Elegí uno.
Ver solución
Límite inferior efectivo: 2 × 80 = 160 ms. Límite superior efectivo: 500 − 100 = 400 ms. Nominal mínimo: 160 / 0,667 = 240 ms (si el reloj es rápido, el perro vence antes: hay que asegurar el límite inferior). Nominal máximo: 400 / 1,333 = 300 ms (si el reloj es lento, vence después: hay que asegurar el superior). El nominal debe estar entre 240 y 300 ms; una elección razonable es 270 ms, con un timeout real entre 180 y 360 ms. El intervalo es angosto: si el peor ciclo creciera a 120 ms, el mínimo (360 ms) superaría al máximo (300 ms) y no habría solución con ese reloj.
3. El watchdog de ventana
Un WWDG tiene PCLK1 = 36 MHz, preescalar ÷8 y se recarga a 0x7F. ¿Cuánto tarda en reiniciar si no se lo alimenta? Si la ventana es W = 0x50, ¿a partir de qué instante se puede alimentar?
Ver solución
El reinicio ocurre cuando el contador baja a 0x3F: de 0x7F a 0x3F son 64 conteos. Cada conteo dura 4096 × 8 / 36 MHz = 0,91 ms. Total: 4096 × 8 × 64 / 36 000 000 = 58,25 ms. La ventana se abre cuando el contador baja a W = 0x50: de 0x7F a 0x50 son 47 conteos: 4096 × 8 × 47 / 36 000 000 = 42,8 ms. Alimentarlo antes de 42,8 ms (con el contador por encima de W) provoca un reinicio, y no alimentarlo antes de 58,25 ms también. La alimentación válida está entre 42,8 y 58,25 ms desde la última recarga, una ventana de 15,5 ms.
4. ¿Dónde se alimenta?
Decidí qué pasa con cada diseño: (a) SysTick_Handler() { wdt_reset(); ms++; }, (b) while (!rx_listo) { wdt_reset(); }, (c) al final del bucle principal: if (todas_las_tareas_avanzaron()) wdt_reset();.
Ver solución
(a) Inútil: la interrupción del tick corre aunque el programa principal esté colgado, y el perro se alimenta siempre; no detecta nada. (b) Esconde el cuelgue clásico: si rx_listo nunca se levanta, el programa espera para siempre alimentando al perro, que nunca reinicia. Hay que darle a la espera un tiempo máximo y tratar su vencimiento como un error. (c) Correcto: el perro se alimenta como consecuencia del progreso comprobado de todas las tareas, en un único lugar del bucle principal; si una se cuelga, la condición falla y el perro vence. Mejora posible: que la verificación incluya también la salud del sistema (pila, colas, tensión).
5. Dormir con perro
Un nodo duerme 60 s entre ciclos. Su perro independiente no puede configurarse a más de 26 s y no se detiene en el sueño. ¿Qué se hace? Si cada despertar adicional cuesta 1 µC, ¿cuánta corriente media agrega?
Ver solución
El perro vencería a los 26 s (como máximo) y reiniciaría al nodo dormido. Se despierta al nodo antes de que venza, con el RTC o un temporizador de bajo consumo, solo para alimentarlo y volver a dormir: con un despertar cada 20 s, el sueño de 60 s se parte en tres tramos, o sea dos despertares adicionales por ciclo. Cada uno cuesta 1 µC: 2 µC por cada 60 s = 2 / 60 = 0,033 µA de corriente media: despreciable. Alternativa: un perro externo de nanopotencia (como el TPL5010) que despierte al nodo y espere la señal de «hecho», con su propio consumo de decenas de nA. Y hay que tener en cuenta que despertar solo a alimentar al perro no verifica nada: la alimentación debe seguir condicionada a que el sistema avanzó.
6. Qué cubre cada perro
Para cada falla, indicá si la detecta el perro independiente (IWDG), el de ventana (WWDG) o el externo: (a) el oscilador principal del chip se detiene; (b) un bucle atascado incluye la llamada que alimenta al perro; (c) un error de configuración desactiva el perro interno; (d) la tensión se hunde y el chip queda en un estado inconsistente.
Ver solución
(a) IWDG y el externo: el independiente tiene su propio reloj y sigue contando; el de ventana, que depende del reloj del bus, se detiene con él. (b) WWDG (detecta la alimentación a destiempo) y el externo si es de ventana; el IWDG no, porque el bucle alimenta a tiempo, una y otra vez. (c) Solo el externo: si el perro interno está desactivado, no hay quien lo detecte desde adentro. (d) El externo si integra un supervisor de tensión, que mantiene el chip en reset hasta que la alimentación es válida; el detector de brown-out interno (tema 37) también ayuda, pero comparte el chip. De ahí la defensa en capas: ninguna cubre todo, y cada una cubre lo que la otra no.
15 · Síntesis
Ideas para recordar
- El watchdog es un contador regresivo que reinicia el chip si llega a cero. El programa lo alimenta (lo recarga) a tiempo mientras funciona. Protege contra la falta de progreso (bucles, esperas infinitas, interbloqueos, saltos corruptos), no contra un programa que corre mal.
- El perro independiente tiene su propio reloj (lento e impreciso: 30–60 kHz en el ejemplo) y, una vez activo, no se puede detener por software. Timeout = recarga × preescalar / f. Se congela en depuración.
- Dónde se alimenta decide todo: desde la ISR de un temporizador, nunca detecta nada; desde la tarea inactiva, detecta bucles pero no tareas bloqueadas; desde un supervisor que comprueba las señales de vida de todas las tareas críticas y la salud del sistema, detecta todo. Un único lugar alimenta, y nunca dentro de un bucle de espera.
- El timeout se elige entre dos límites: mayor que el doble del peor ciclo (con las operaciones lentas), menor que el tiempo de seguridad menos el reinicio; y se garantiza en los dos extremos de la imprecisión del reloj (en el ejemplo, nominal de 480 a 637,5 ms).
- El watchdog de ventana exige además no alimentar demasiado pronto: atrapa el bucle atascado dentro de la propia rutina que alimenta, que el independiente nunca detecta. Obliga a alinear el período del supervisor a la ventana, y ofrece un aviso previo útil para el diagnóstico.
- El watchdog externo es independiente del chip (reloj, alimentación y lógica propios), reinicia por el pin de reset, puede vigilar la tensión y cortar la alimentación de otros circuitos. Se usa en capas: de ventana para el ciclo, independiente para la falla del reloj, externo para casi todo lo demás.
- Cada vencimiento debe convertirse en información: el supervisor anota qué tarea falló, los hitos y el aviso previo guardan el estado, y al arrancar se registra la causa y se cuentan los reinicios (tema 37). Y el hardware debe dejar las salidas en estado seguro durante el reinicio.
- Los casos delicados: el sueño profundo (hay que despertar a alimentarlo), el arranque largo, las operaciones largas legítimas, la depuración y las actualizaciones. Y una regla sin excepciones: el watchdog se prueba, provocando cuelgues a propósito y midiendo el tiempo de recuperación.
¿Listo para el tema siguiente?
Antes de avanzar, mirá el watchdog de un firmware tuyo y respondé: ¿está activo?, ¿quién lo alimenta, y desde dónde?, ¿qué falla de las que imaginás no detectaría?, ¿cuál es su timeout real en los dos extremos del reloj? Después provocá un cuelgue a propósito en cada tarea y mirá si el equipo reinicia, en cuánto tiempo y qué anota. Si tu watchdog nunca ha reiniciado a tu equipo en una prueba, no sabés si funciona.
En el próximo tema cerramos el bloque con la programación defensiva y la recuperación: cómo validar las entradas, usar aserciones, definir un modo seguro y degradar el servicio de forma ordenada, para que el equipo se comporte bien frente a un fallo en lugar de depender de reiniciarse.