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.
| Type | Rule | Catches | Example |
|---|---|---|---|
| Independent watchdog | Kick any time before timeout | Code that stops running | STM32 IWDG, AVR WDT |
| Window watchdog | Kick only inside a time window; too early also resets | Stopped code and code that runs wildly fast in a tight loop | STM32 WWDG |
| External watchdog IC | A separate chip toggled by a GPIO pin | Failures that stop the MCU's own clock or watchdog | Supervisor 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.
#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.
#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.