WatchDog Timer

Table of Contents

  1. Brown-Out Detection

A watchdog timer (WDT) is a hardware timer that automatically generates a system reset if the main program neglects to periodically service it. It is often used to automatically reset an embedded device that hangs because of a software or hardware fault. Some systems may also refer to it as a computer operating properly (COP) timer. Many microcontrollers including the mbed processor have watchdog timer hardware.

The main program typically has a loop that it constantly goes through performing various functions. The watchdog timer is loaded with an initial value greater than the worst case time delay through the main program loop. Each time it goes through the main loop the code resets the watchdog timer (sometimes called “kicking” or “feeding” the dog). If a fault occurs and the main program does not get back to reset the timer before it counts down, an interrupt is generated to reset the processor. Used in this way, the watchdog timer can detect a fault on an unattended embedded device and attempt corrective action with a reset. Typically after reset, a register can also be read to determine if the watchdog timer generated the reset or if it was a normal reset. On the mbed this register is called the Reset Source Identification Register (RSID).

/media/uploads/4180_1/clementine1.jpg

In one famous case, a NASA deep space probe’s computer locked up with the thruster rockets firing. By the time the ground controllers realized the problem and sent a radio signal back to force a hardware reset, the probe had used all of the fuel. The processor had a watchdog timer but it was not used. A watchdog timer would have likely detected the fault and shut down the rocket motors in time to save the mission. In another space mission that was successful, several watchdog resets occurred during the NASA Pathfinder mission to Mars before the code problem was discovered and fixed with new software that was downloaded in deep space via a radio link. In deep space, there is no one around to push a reset button or take out the batteries.

Despite extensive software and hardware testing, faults will still occur in real devices. Even momentary noise spikes on a power supply can lock up a processor occasionally. Such events will occur on the power grid several times a year. Watchdog timers provide a last line of defense to prevent system failure with minimal hardware cost.

Here is a short code example showing how to use the watchdog timer on mbed. It is based on code examples in some earlier forum postings at http://mbed.org/forum/mbed/topic/508/. Additional information on the watchdog timer can be found in Chapter 28 of the LPC1768 User Manual.

Watchdog_Timer_Example

#include "mbed.h"
// LEDs used to indicate code activity and reset source
DigitalOut myled1(LED1); //in main loop part 1
DigitalOut myled2(LED2); //in main loop part 2 (where fault occurs)
DigitalOut myled3(LED3); //The pushbutton or power on caused a reset
DigitalOut myled4(LED4); //The watchdog timer caused a reset

// Simon's Watchdog code from
// http://mbed.org/forum/mbed/topic/508/
class Watchdog {
public:
// Load timeout value in watchdog timer and enable
    void kick(float s) {
        LPC_WDT->WDCLKSEL = 0x1;                // Set CLK src to PCLK
        uint32_t clk = SystemCoreClock / 16;    // WD has a fixed /4 prescaler, PCLK default is /4
        LPC_WDT->WDTC = s * (float)clk;
        LPC_WDT->WDMOD = 0x3;                   // Enabled and Reset
        kick();
    }
// "kick" or "feed" the dog - reset the watchdog timer
// by writing this required bit pattern
    void kick() {
        LPC_WDT->WDFEED = 0xAA;
        LPC_WDT->WDFEED = 0x55;
    }
};

// Setup the watchdog timer
Watchdog wdt;

int main() {
    int count = 0;
// On reset, indicate a watchdog reset or a pushbutton reset on LED 4 or 3
    if ((LPC_WDT->WDMOD >> 2) & 1)
        myled4 = 1; else myled3 = 1;
        
// setup a 10 second timeout on watchdog timer hardware
// needs to be longer than worst case main loop exection time
    wdt.kick(10.0);  

// Main program loop - resets watchdog once each loop iteration
// Would typically have a lot of code in loop with many calls
    while (1) {
        myled1 = 1; //Flash LEDs 1 & 2 to indicate normal loop activity
        wait(.05);
        myled1 = 0;
        myled2 = 1;
        wait(.05);
// Simulate a fault lock up with an infinite while loop, but only after 25 loop iterations
        if (count == 25) while (1) {};
// LED 2 will stay on during the fault
        myled2 = 0;
        count ++;
// End of main loop so "kick" to reset watchdog timer and avoid a reset
        wdt.kick();
    }
}



In the video below running the demo code, the program starts normally with a power on or the pushbutton, and LED3 is turned on. Then as it loops, LED1 and LED2 will flash. Each time at the end of the loop the watchdog timer is "kicked" or reset. After 25 loop iterations, a fault is simulated with an infinite while(1){} loop. LED2 will stay on for several seconds until the watchdog timer runs down from 10 seconds and it automatically generates a reset. When this reset is generated by the watchdog timer, the startup code turns on LED4. The code reads an WDT register to determine the reset source. If it is left running, the code runs a few seconds (LEDs 1&2 flash), locks up (LED2 on), and is then reset after a few seconds by the watchdog timer (LED 4 on). If you hit the pushbutton or cycle power, LED 3 will come back on until a watchdog timer reset occurs again.



If the program code contains other loops that wait on external I/O events for long periods of time, a timeout can also be added to those loops to periodically kick the watchdog timer.

Brown-Out Detection

In addition to the watchdog timer, also consider using the processor's brown-out detection circuit in a final product. A low power supply voltage can be detected and force an automatic interrupt and finally a reset if it still drops further. So if the AC power goes out or a battery runs down, corrective action can be taken automatically. Additional information on the Brown-Out Detector (BOD) can be found in Chapter 3 Section 5 of the LPC1768 User Manual.

Exactly what action to take when a brown-out occurs will vary depending on the application. The capacitors in the power supply will typically run the processor for a few milliseconds before a total power loss. So it might be possible to quickly put critical I/O devices in a failsafe mode and save a small amount of critical data. For example, a robot might turn off the drive motors or a UAV might throttle back and set the controls for level flight. In some cases, it might make sense to try to reduce power using power management features. Software can also monitor the BOD signal by reading the NVIC Status Registers, and determine if a Brown out caused a reset by reading the RSID register. The BOD circuit triggers an interrupt at around 2.2V. Write operations to Flash will become unreliable at low voltage levels and the processor shuts down at 1.85V.

Here is a short demo showing how to setup a brown out detection interrupt service routine (ISR). For the brown-out demo to work, it was necessary to power down the magic PC USB host interface chip. This requires new experimental firmware to be installed from http://mbed.org/users/simon/notebook/interface-powerdown/ .

Brown_Out_Example

#include "mbed.h"
#include "PowerControl/PowerControl.h"
#include "PowerControl/EthernetPowerControl.h"
// Need PowerControl *.h files from this URL
// http://mbed.org/users/no2chem/notebook/mbed-power-controlconsumption/

#define USR_POWERDOWN    (0x104)
int semihost_powerdown() {
    uint32_t arg;
    return __semihost(USR_POWERDOWN, &arg);
}

DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalOut myled3(LED3);
DigitalOut myled4(LED4);

// Brown Out Interrupt Service Routine
// Called when processor power supply voltage lowers to 2.2V
// on processor chip - Vin is around 3.3V
// Put system in a low power or failsafe mode
// Setup IO to safe inactive values
void BrownOut() {
    myled1 = 1;
    myled2 = 0;
    myled3 = 1;
    myled4 = 0;
// Power Down some internal devices
    Peripheral_PowerDown(0xFFFFFFFF);
// Stop running processor perhaps? - voltage levels are likely going too low
    Sleep();
}

int main() {
    int result;
    int count;
// Power down magic USB interface chip - exits debug mode
// Needs new firmware (URL below) and USB cable not connected
// http://mbed.org/users/simon/notebook/interface-powerdown/
// Need power supply on Vin and not USB
    result = semihost_powerdown();
// Setup a Brown Out Detect (BOD) Interrupt Service Routine
    NVIC_SetVector(BOD_IRQn, (unsigned)BrownOut);
// Enable Brown Out Detect Interrupt
    NVIC_EnableIRQ(BOD_IRQn);
    count = 1;
// Loop flashing LEDs until a Brown Out detect interrupt
    while (1) {
        count = count << 1;
        if (count > 0x08) count = 0x01;
        myled1 = count & 0x01;
        myled2 = count & 0x02;
        myled3 = count & 0x04;
        myled4 = count & 0x08;
        wait(.05);
    }
}





The code example is seen running in the video above. An external DC power supply was connected to Vin to supply power (No USB cable). The main program flashes the four mbed LEDs. The supply voltage on Vin starts at 5V and drops to around 3.4V before the glow of the LEDs fades out. At 3.3V, a brown-out detect interrupt occurs (2.2V on processor chip) and the Brown-Out Interrupt routine runs. The voltage is then increased so that the LED setting from the brown out ISR can be seen (i.e., a 1,0,1,0 pattern). Next, the voltage is dropped again to below 2.1V (1V on processor chip). As the voltage level goes below 1.85V on the processor chip, the BOD circuit asserts reset to halt the processor . As the voltage level is increased again, a power on reset triggers at around 3.6V and restarts the processor by releasing reset. The main loop then starts the LEDs flashing again.