Lesson 13: Watchdog Timer

Today’s lesson will cover the watchdog timer and how to read status registers on the S7 microcontroller.

The watchdog timer (WDT) is a 14-bit counter. When the timer counts down to zero, it can either trigger an interrupt or reset the microcontroller.

Why is this useful? Imagine your microcontroller is on a system where a person cannot easily access. This could be on an industrial machine, out in space, on a car, or a data logger in some remote location. For some reason, your code gets stuck in a loop, or freezes while it waits for some resource, or stops responding for any reason. Now you have an unresponsive microcontroller stuck in a place where it’s hard to reset it. You can’t do a remote reset (over the internet, or wireless) because the microcontroller won’t respond to your commands (this is not necessarily accurate; if your code is stuck in a loop, it might still respond to interrupts). But you still have to notice that it’s not responding before you can reset it.

Another case is where waiting too long to be reset could be a problem, like in a motor control application.

In any of these cases, it’s desirable to have the microcontroller either perform a reset or break out of whatever it is doing and do something else. This is where the WDT comes in handy. Once you start the timer, it will constantly count down. If the counter hits zero (all called an underflow), it will perform the configured action (reset or interrupt).

During the normal execution of your application, you occasionally reset the timer (also called tickling the timer, or kicking the dog) and this resets the counter. See the Embedded.com article Introduction to Watchdog Timers for more background on the WDT.

In today’s lesson, we will be doing a couple of things. First, we will blink a red LED on an off every second for five seconds. Then we will turn on a green led and put ourselves in an infinite loop. Once our watchdog timer counts down, the application will restart.

Finally, when the application first starts, it will check the reason the microcontroller restarted. These can include a power on reset, low voltage reset, software reset, memory error, and of course the watchdog timer.

Every microcontroller has a user’s manual. Check the chapter on resets for more information about the available resets for your microcontroller.

After creating your initial project, add the following threads:

g_timer Timer Driver on r_gpt (for the led timing)
g_wdt Watchdog Driver on r_wdt

Change the g_wdt so that it does not start automatically (Start Watchdog After Configuration is False).

The g_timer should have a Period Value of 1000 and Period Unit of Milliseconds.

For the LED functionality, you will need to configure P807, P808, and P810 for output mode, CMOS output type, and Chip input/output as GPIO. This is down under the Pins=>Ports.

Below is the complete code.

/* HAL-only entry function */
#include "hal_data.h"
#include 

#define COUNTS_PER_MILLISECOND  (120E6 / 1000)

void hal_entry(void)
{
    timer_size_t timerValue;

    // Check if reset was caused by the watchdog timer.
    // If it was, turn on an LED
    if (R_SYSTEM->RSTSR1_b.WDTRF != 0)
    {
        g_ioport.p_api->pinWrite (IOPORT_PORT_08_PIN_10, IOPORT_LEVEL_HIGH);
    }


    // Initialize Timer
    g_timer.p_api->open (g_timer.p_ctrl, g_timer.p_cfg);

    // Open Watchdog Timer
    g_wdt.p_api->open (g_wdt.p_ctrl, g_wdt.p_cfg);

    for (int i = 0; i < 5; i++)     {         // Turn Red LED on         g_ioport.p_api->pinWrite (IOPORT_PORT_08_PIN_08, IOPORT_LEVEL_HIGH);

        // Reset Timer
        g_timer.p_api->reset(g_timer.p_ctrl);
        while(true)
        {
            // Sleep for 500ms
            g_timer.p_api->counterGet(g_timer.p_ctrl, &timerValue);
            if (timerValue > (500*COUNTS_PER_MILLISECOND))
            {
                break;
            }
        }

        // Turn Red LED off
        g_ioport.p_api->pinWrite (IOPORT_PORT_08_PIN_08, IOPORT_LEVEL_LOW);

        // Reset Timer
        g_timer.p_api->reset(g_timer.p_ctrl);
        while(true)
        {
            g_timer.p_api->counterGet(g_timer.p_ctrl, &timerValue);
            if (timerValue > (500*COUNTS_PER_MILLISECOND))
            {
                break;
            }
        }
    }

    // Turn Red LED off
    g_ioport.p_api->pinWrite (IOPORT_PORT_08_PIN_08, IOPORT_LEVEL_LOW);

    // Turn Green LED on
    g_ioport.p_api->pinWrite (IOPORT_PORT_08_PIN_07, IOPORT_LEVEL_HIGH);

    // Start Watchdog
    g_wdt.p_api->refresh(g_wdt.p_ctrl);

    // Sit in a loop and wait for watchdog timer to fire.
    // This will not happen while in the debugger since the watchdog timer doesn't
    // run with the debugger attached.
    // Debug the code (so the device programs), disconnect the USB to the debugger, and
    // reset the development board. The program will run and 2.23 seconds after the green
    // led turns on, the program will start over.
    while(true)
    {
        ;
    }

}

The timer module runs from PCLKA which is usually the primary clock divided by two (check the Clocks tab in the configuration wizard). With a 240MHz primary clock (for the S7G2), PCLKA runs at 120MHz. So, it clocks 120 million times per second, or 120 thousand times every 1000 milliseconds (120KHz). The COUNTS_PER_MILLISECOND definition does this math for us. All we need to do is multiple this constant with the number of milliseconds we want to wait (with a limit of 1000, as set in the g_timer configuration) and we will get the number of clocks in our time period.

The first if statement checks the System.RSTSR1.WDTRF register (System, Resets, Watchdog Timer). If this value is non-zero, it means that the microcontroller was reset do to a watchdog timer underflow.

If it was reset from the WDT, we turn on an LED.

Next we open both the g_timer and g_wdt.

The for loop blinks an LED on and off waiting 500ms between each state.

At this point, although we have turned on the WDT, we haven’t actually started it yet. This is why there is no code to “kick the dog” in this loop.

After the for loop runs, we turn off the red LED (it should have already been off since the last state in our for loops does so, but it never hurts to be sure!). We then turn on the Green LED, and kick the dog (finally!). The SSP performs this kick through a call to refresh.

Lastly, we stick ourselves in an infinite loop and wait to be reset.

I could not find a reference in the SSP or User’s Manual on which clock the WDT runs from. However, in a tutorial on the Renesas website, it says it is PCLKB, which by default runs at 1/4 speed (60MHz for the S7G2). It is additional configured with another pre-scale of 8192.

So, every 4th system clock, PCLKB gets a clock. Then every 8192 PCLKB’s, the WDT counts down from the initial value.

The WDT has an initial value of 16,384 (2^14-1). So, our timeout period is:

First, find our actual tick frequency: 240MHz/(4*8192) = 7324.21875Hz

Take the inverse to get the time: 7324.21875^(-1) = 136.533us.

So, our watchdog timer is decremented every 136.533us. Since it has to count down from 16384 to 0, the timeout period is: 136.533us * 16384 = 2.23696 seconds.

So, we have to reset our WDT every 2.23 seconds or our application will reset itself.

If you run the program the red LED flashes as expected. The the green LED turns on. Then after 2.23 seconds…nothing happens. WTF?!?!

Well, it seems that if you are debugging the application, the WDT never gets started. This makes sense. Could you imagine stepping through your code and having your application reset on you because you were not fast enough?

If you hit the reset button on the DK-S7G2 and watch the program run again, you get the same result. Instead, you have to pull the power plug and perform a hard reset.

Once you do this, the watchdog timer works as expected and the application restarts 2.23 seconds after turning on the green LED.

That’s it for this lesson!

Full source code available on GitHub at https://github.com/lycannon/ajwrs/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s