Watchdog Timer Delays - and measuring them

Hi,

I am trying to learn how to make use of the WDT in my Nano, eventually I will need to use the WDT
in my Observatory Automation project to produce a ‘failsafe’ action such that the system closes the roof slit if the radio-link to the main control board fails.

So, I have written this short sketch. It works fine and seems to blink the on-board LED with a nice regular period. BUT - when I try and measure the actual delay using calls to millis() and print these out using Serial I get strange results. With the delay as set to 0.5 seconds (or thereabouts) most of the dely readings are, as expected around 500 milliseconds but every now and then the Serial value will show a delay of 3 times the proper value, around 1500 milliseconds and sometimes 5 times and occasionally 7 times.

None of these variations are showing up in the actual blink rate of the LED which stays nice and steady even when Serial is reporting a delay of over 3 seconds.

I changed the code from digitalRead and digitalWrite to their fastDigital equivalents but it made no difference. I also minimised the amount sent to the Serial port by cutting out all the text.

I think it is important for me to understand what is happening as if I get this wrong in the real world the consequences could be very costly for my astronomy gear.

Finally, my apologies for setting the value of WDTCSR by writing a byte to the register but I had a lot of trouble trying to understand and use the bitwise logical operators. I will eventually get my head around these but one step at a time is enough for now.

My thanks in advance for your help.

Regards, Hugh

#include <avr/wdt.h>
#include <avr/interrupt.h>
#include "streaming.h" 
#include "digitalWriteFast.h"
/*
To use it just put the digitalWriteFast folder into your library 
alongside the EEPROM, Ethernet, Firmata, etc folders. In your 
source code #include <digitalWriteFast.h> then you can use 
digitalWriteFast(pin,HIGH/LOW), digitalReadFast(pin), 
pinMode(pin,INPUT/OUTPUT), digitalWriteFast2(pin,HIGH/LOW), 
digitalReadFast2(pin), pinMode2(pin,INPUT/OUTPUT) 
very much as you have used the built-in commands. 
The object code will not only be faster, but actually smaller as well.

WDTCSR Watchdog Timer Control Register: Configuration Bits:

WDIF //Bit7: Watchdog Interrupt Flag - CLEAR BY WRITING 1
WDIE //Bit6: Watchdog Interrupt Enable - SET by writing to 1
WDP3 //Bit5: Watchdog Timer Prescaler 3
WDCE //Bit 4: Watchdog Change Enable - SET to 1 to enable changes
   //to WDE and the Prescaler bits. Changes must be made within
   //4 clock cycles of setting WDCE bit.
WDE  //Watchdog System Reset Enable - Set to enable System Reset
   //on WDT timeout. NOTE This bit also tracks the value of
   //bit WDRF in the MCUSR (MCU Status Register). As the WDRF
   //bit is RESET (cleared) by a Power-on Reset, so is 
   //WDE cleared by a Power-on reset.
WDP2 //Watchdog Timer Prescaler 2
WDP1 //Watchdog Timer Prescaler 1
WDP0 //Watchdog Timer Prescaler 0

Delay Settings Table
WDP3  WDP2  WDP1  WDP0  Delay (mS)
0   0   0   0   16
0   0   0   1   32
0   0   1   0   64
0   0   1   1   128
0   1   0   0   256
0   1   0   1   512
0   1   1   0   1024
0   1   1   1   2048
1   0   0   0   4096
1   0   0   1   8192

So, for our target delay of 32 milliseconds, we need to set
as follows:
WDP3 = 0
WDP2 = 0
WDP1 = 0
WDP0 = 1
*/

// constants 
const uint8_t ledPin =  13;      // the number of the LED pin

//Variables
volatile uint8_t myFlag;
volatile uint8_t myMCUSR;
volatile uint8_t myWDTCSR;
uint32_t startT = 0;
uint32_t endT = 0;

//Function Prototypes
void watchdogSetup(void)
{
  cli();// disable all interrupts while setting up WDT
  wdt_reset(); // Always reset the WDT timer before setting it

  //WDE tracks WDRF so make sure WDRF is clear  
  MCUSR &= ~(1<<WDRF);// Clear WDRF in MCUSR because it overwrites WDE
  // Enter Watchdog Configuration mode by setting WDCE and WDE
   WDTCSR |= (1<<WDCE) | (1<<WDE);
   
  // Set Watchdog settings: Disable System Reset by clearing WDE and set WDPx bits for 512 mSec delay
  WDTCSR = 0b00000101;

  sei(); //Enable interrupts
  //Debugging Call to Serial
 //Serial << "Watchdog setup: MCUSR is " << _BIN(MCUSR) << " : WDTCSR is " << _BIN(WDTCSR) << "\n"; 
}

void watchdogStart(void)
{
    //Reset watchdog timer count to zero
    wdt_reset();
    
    //Enable watchdog interrupts
    WDTCSR = (1<<WDIE);
}

void watchdogStop(void)
{
  //Disable watchdog interrupts
  WDTCSR = (0<<WDIE);
}

void setup()
{ //Serial for debugging
  Serial.begin(115200);
  
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  
  //Set up WDT
  watchdogSetup();
  
  //turn on WDT interrupts
  watchdogStart();
}

void loop() {
  // Debugging
  if(myFlag) {
    if(digitalReadFast(ledPin))
    {
      startT = millis();
    }
    else
    {
      endT = millis();
      Serial << (endT - startT) << "\n";
     }
  }  
  myFlag = LOW;
}



ISR(WDT_vect)
{
    myFlag = HIGH;
    //Toggle LED on pin 13
    digitalWriteFast(ledPin, !digitalReadFast(ledPin));  // toggle state
    wdt_reset();
}

Race condition. Your interrupt service routine is manipulating two things that are shared with loop and neither of those things are protected with a critical section.

Hi,

Not sure I understand ‘Race Condition’.

I changed the loop code to

void loop() {
  // Debugging
  nrFlag = myFlag;
  nrPin = digitalReadFast(ledPin);
  if(nrFlag) {
    if(nrPin)
    {
      startT = millis();
    }
    else
    {
      endT = millis();
      Serial << (endT - startT) << "\n";
     }
  }  
  myFlag = LOW;

but I am still getting the same pattern of serial output as below

533
2665
534
533
2665
534
1599
533
534
533
533
1599
533
533
532
532
1598
533
1599
2665
532
532
532
1598
532
1598
2664
533

Any further advice / suggestions would be most welcome.

Regards, Hugh

Why do you toggle the LED in the interrupt service routine?

Consider what will happen if the interrupt fires sometime between the if( myFlag ) and the final myFlag = LOW line. You will skip an execution in the loop because you reset myFlag at the end and kill the signal.

Try disabling interrupts while the loop is servicing a myFlag signal. Also move the myFlag = LOW into the if-statement so it only gets reset when it was originally true. That way if the interrupt happens when myFlag is LOW but the loop has yet to finish up the if statement comparison, you won't inadvertently discard your signal from the ISR.

Because you are writing to the LED pin only from the ISR, it should not surprise you that its behavior is strictly regular. The ISR operates on a regular timed interrupt after all. What would be strange is if it didn't. If you were to move that code into the loop I'd bet you'll see less regularity in its behavior since loop() execution is not regular.

You could also try using staggered interrupts. The problem is that loop() is not regular. The timing of when each chunk of code runs walks around in relation to each other. Eventually you must expect the ISR to fire in the middle of your loop code somewhere and that will occasionally cause ambiguities with the value of variables accessed from both places. To fix, you need loop() to be as regular as ISR is. And timed in such a way that they don't run concurrently. So put your loop code into a second ISR running on the same period (duration) as the first one but staggered in time such that the first ISR has enough time to complete before the second ISR runs, and that one is permitted enough time to complete before the first one fires again. Thus you get a toggling interrupt deal ISR1...ISR2...ISR1...ISR2 at a regular interval which isolates the two code chunks from each other. Now you can be certain that the two code chunks will never run concurrently and will never be setting values from one place in the middle of computations or reads or whatever being done in the other. You could even do this from within a single ISR by using a Boolean toggle flag and running the thing twice as often. Gotta keep those ISR execution times short tho, so you can't do too much using this technique. In your case there isn't much going on in either place so I imagine it will be quite safe to try.

A race condition or race hazard is the behavior of an electronic, software or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended. The term originates with the idea of two signals racing each other to influence the output first.

Hi,

I thought that's what I needed to do? My (very confused) thinking was that if I toggled the LED in the main loop that although it would stay on for the 500 odd milliseconds, it would only be off for the time needed to repeat the loop - a few microseconds, so it would appear to be permanently on.

Hi,

Many thanks!

I changed the loop code to:

void loop() {
  // Debugging

ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    {
    nrPin = digitalReadFast(ledPin);
    }

  
  if(myFlag) {
    if(nrPin)
    {
      startT = millis();
    }
    else
    {
      endT = millis();
      Serial << (endT - startT) << "\n";
    }
    myFlag = LOW;    
  }  
}

and it runs how I wanted it to. I have checked it down to 8 milliseconds WDT delay and it seems fine.

Many thanks for your help.

Regards, Hugh