Watchdog Timers in Embedded Systems: Setup, Kicking and Recovery

Firmware that runs unattended for months will eventually meet something its designers did not foresee: electrical noise that corrupts a variable, a sensor that stops answering an I2C transaction, a rare race condition that leaves the code stuck in a loop. A desktop user would press reset. A water pump controller in a field has nobody to do that. The watchdog timer is the hardware that presses reset for you.

This article explains how watchdogs work, how to compute a timeout, the right and wrong places to refresh one, and how to find out after start-up that a watchdog reset happened.

How a Watchdog Works

A watchdog is a down-counter that runs from its own clock. Once started, it counts toward zero. The firmware must regularly write a special value to it, called refreshing, feeding or kicking the watchdog, which reloads the counter. If the firmware hangs and stops kicking, the counter reaches zero and the watchdog resets the microcontroller.

TypeRuleCatchesExample
Independent watchdogKick any time before timeoutCode that stops runningSTM32 IWDG, AVR WDT
Window watchdogKick only inside a time window; too early also resetsStopped code and code that runs wildly fast in a tight loopSTM32 WWDG
External watchdog ICA separate chip toggled by a GPIO pinFailures that stop the MCU's own clock or watchdogSupervisor ICs with watchdog input

An independent watchdog usually runs from a separate low-speed internal oscillator, so it keeps working even if the main clock fails. On many parts, once started it cannot be stopped by software until the next reset, which prevents a crashed program from accidentally disabling it.

Calculating the Timeout

On the STM32 IWDG the counter is 12 bits wide and is clocked by the LSI oscillator through a prescaler of 4, 8, 16, 32, 64, 128 or 256, selected by the PR register. The reload value comes from the RLR register.

timeout = (prescaler x (RLR + 1)) / f_LSI

Example with f_LSI = 32 kHz (nominal on STM32F4):
  prescaler = 32  -> counter clock = 32 000 / 32 = 1000 Hz
  RLR       = 999 -> timeout = 1000 / 1000 = 1 s

Starting the Watchdog on Real Hardware

The IWDG is controlled through a key register. Writing 0xCCCC starts it, 0x5555 unlocks the PR and RLR registers, and 0xAAAA reloads the counter. The code below is MCU-specific and uses the CMSIS register names for STM32F4.

MCU-specific: STM32F4 IWDG start with a 1 s nominal timeout and reset-cause check (needs stm32f4xx.h)
#include "stm32f4xx.h"

void iwdg_start_1s(void)
{
    IWDG->KR  = 0xCCCC;              /* start the watchdog (also enables LSI) */
    IWDG->KR  = 0x5555;              /* unlock PR and RLR                     */
    IWDG->PR  = 3;                   /* prescaler /32                         */
    IWDG->RLR = 999;                 /* reload value                          */
    while (IWDG->SR != 0) { }        /* wait until PR and RLR are updated     */
    IWDG->KR  = 0xAAAA;              /* first refresh                         */
}

static inline void iwdg_kick(void)
{
    IWDG->KR = 0xAAAA;
}

int was_watchdog_reset(void)
{
    int wd = (RCC->CSR & RCC_CSR_IWDGRSTF) != 0;
    RCC->CSR |= RCC_CSR_RMVF;        /* clear all reset flags for next time */
    return wd;
}

On AVR with avr-libc, the equivalent is wdt_enable(WDTO_1S) and wdt_reset() from <avr/wdt.h>, and the reset cause is the WDRF bit in the MCUSR register. On AVR you must clear WDRF and disable the watchdog early in start-up, because after a watchdog reset the watchdog stays enabled with the shortest timeout on many parts and can reset the chip again before main gets far.

Where to Kick: The Most Important Decision

A watchdog only helps if a hang stops the kicks. The worst pattern is kicking it from a timer interrupt: the main loop can be stuck forever while the timer ISR keeps the watchdog happy. Kicking it inside a delay loop or a wait-for-flag loop has the same problem.

The robust pattern is to kick in one place only, and only when every important job has proved that it ran. Each job sets its bit in a check-in mask; the main loop kicks only when all bits are set, then clears the mask. The program below simulates this on a PC; the third loop pass shows a stuck sensor job causing a missed kick.

Check-in mask so the watchdog is kicked only when every job has run (PC simulation)
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#define JOB_SENSOR  (1u << 0)
#define JOB_COMMS   (1u << 1)
#define JOB_CONTROL (1u << 2)
#define ALL_JOBS    (JOB_SENSOR | JOB_COMMS | JOB_CONTROL)

static uint32_t checkin = 0;

static void watchdog_kick(void) { printf("  watchdog kicked\n"); } /* IWDG->KR = 0xAAAA */

static void job_sensor(bool healthy) { if (healthy) checkin |= JOB_SENSOR; }
static void job_comms(void)          { checkin |= JOB_COMMS; }
static void job_control(void)        { checkin |= JOB_CONTROL; }

static void watchdog_service(void)
{
    if ((checkin & ALL_JOBS) == ALL_JOBS) {
        watchdog_kick();
        checkin = 0;
    } else {
        printf("  NOT kicked, missing mask 0x%02X\n",
               (unsigned)(ALL_JOBS & ~checkin));
    }
}

int main(void)
{
    for (int pass = 1; pass <= 3; pass++) {
        printf("pass %d\n", pass);
        job_sensor(pass != 3);     /* sensor job fails on pass 3 */
        job_comms();
        job_control();
        watchdog_service();
    }
    return 0;
}

With an RTOS, each task checks in from its own loop and a low-priority monitor task, or the idle hook, kicks the watchdog when all tasks have reported. A task that is blocked forever then causes a reset, which is exactly what you want.

After a Watchdog Reset

  • Read the reset cause register at start-up and record it, for example as a counter in non-volatile memory or a log message.
  • Put outputs into a safe state first. Motors, heaters and valves should not restart blindly after an unexpected reset.
  • Consider keeping a small diagnostic area in RAM that startup code does not clear, holding the last state or a fault code, so you can learn what hung.
  • If watchdog resets repeat several times in a short period, enter a safe degraded mode instead of rebooting forever.

Common Mistakes

  • Kicking the watchdog from a periodic interrupt, which hides main-loop hangs.
  • Setting a timeout shorter than the slowest legitimate operation, such as erasing a flash sector, causing random resets.
  • Forgetting that the debugger halts the CPU but not always the watchdog. Many STM32 parts have a DBGMCU freeze bit to stop the IWDG while halted.
  • Kicking it everywhere in the code to stop resets, which turns the watchdog into decoration.

Summary

A watchdog resets the microcontroller when firmware stops refreshing it. Calculate the timeout from the prescaler, reload value and the worst-case low-speed oscillator frequency. Kick it in exactly one place, only after every job has checked in, never from a timer ISR. At start-up, read the reset cause, make outputs safe and log the event. Used this way, the watchdog turns rare hangs into short, recorded recoveries.