Restarting the Watchdog Timer

In the previous article, I first mentioned the watchdog, without explaining what it actually is and how it works. This week I'm going to change that, and also show you how to restart the watchdog timer using Rust code.

So what is the watchdog? The watchdog timer (WDT) is a feature of the microcontroller that prevents a deadlock in the program from locking up the whole system.[1] It does so by counting down the timer. Once the timer reaches zero, the watchdog resets the microcontroller. A program that is working correctly can just regularly restart the watchdog timer to prevent that. If it stops working for some reason it will, in theory, also stop restarting the timer, which will trigger a system reset.

So why is that important for our little LED blinking program? Well, by default, the watchdog is enabled and will reset the hardware whenever the timer reaches zero. As the watchdog timer and the LED blinking pattern aren't perfectly aligned, you will notice a much shorter pause between the 16th and 17th blinking of the LED. This is because the program is restarted directly after the 16th blinking, and what we might think of as the 17th blinking is actually the first, after the reset.

Ok then, it's time to get into the implementation. Let's start with the low-level interface. Here's the definition of the watchdog's user interface:

pub struct Wdt {
    pub control: Volatile<u32>,
    pub mode   : Volatile<u32>,
    pub status : Volatile<u32>,

pub const WDT: *mut Wdt = 0x400E1A50 as *mut Wdt;

As microcontroller features go, the watchdog is one of the simpler ones. The user interface consists of 3 registers (the struct), and there's just one instance of the user interface at address 0x400E1A50 (the constant).[2][3]

We're not even going to use all of that though. By default, the watchdog is configured to reset every 16 seconds. The timer uses a 12-bit counter, which is initialized at its maximum value (4096). The counter is counted down by the slow clock (SLCK), which runs at 32768 Hz, but is divided by a configurable divider value of 128. This comes down to a reset every 16 seconds (4096 / 32768 * 128).[4]

Normally when designing a program, you would figure out what kind of delay is acceptable when the program stops working and set the divider value appropriately. The watchdog also has additional configuration that can protect it from being reset in an endless loop by a program that no longer does anything useful. In our case, we're just blinking an LED though, so we don't care about any of that. All we need is a function to restart the timer. This is it:

pub fn restart_watchdog() {
    unsafe {

That's really it. All we need to do is to write a single value into the control register. The upper byte is a key that always needs to be 0xA5. I'm not 100% sure what its purpose is, but I'm assuming it's designed to prevent a faulty program that overwrites random memory from restarting the timer by accident. The only other significant bit in that value is the last one. When it's value is written as 1, it restarts the timer.[5]

Now all that's left to do is to call that function every once in a while:

// Initialization code omitted for brevity

loop {

    // LED blinking code also omitted

By restarting the watchdog at the start of the loop, we make sure to do it often enough to prevent the it from ever resetting the microcontroller.

And that wraps us up for today. As always, the full code is available on GitHub. See you next time!