Using Watchdog Timer as a time base

I was looking int using the watchdog timer as a simple time base for some ideas I have coming up. Wasn't too sure about how good it would be.

The code is a simple time loop. It blinks the on board LED on each heart beat. You can print out the time interval between the heart beats to see how long they were, based on the system clock - millis(). Also include the min and max interval. Just send it something form the serial monitor.

Mine seems to be running a little slow compared to the system clock. But it looks very stable. A couple of milliseconds either way.

For simple LED blinking, this could save someone the headache of keeping track of previous and current time stamps (mine are just for the measuring of the interval). It wouldn't be much good for time critical applications. I am going to use it for a simple traffic light state machine, spanning minutes of time. So a couple of milliseconds one way or the other doesn't really matter. And I don't need to have the lights on for any exact amount of time. They just need to be repeatable.

Sorry for the formatting. Tabs seem a little screwy.

#include <avr/wdt.h>            // library for default watchdog functions
#include <avr/interrupt.h>      // library for interrupts handling

// pin on which a led is attached on the board
#define led 13

// flags and variables
volatile bool HeartBeat = false;	// timer has expired
bool LEDState = false;

unsigned long currentTime = 0;	// current time stamp
unsigned long previousTime = 0;	// last time stamp
unsigned long Interval = 0;		// time between time stamps
unsigned long minTime = 0xffff;	// minimum time interval
unsigned long maxTime = 0;		// maximum time interval

// interrupt raised by the watchdog firing
// when the watchdog fires during sleep, this function will be executed
// remember that interrupts are disabled in ISR functions

ISR(WDT_vect)
{
	// reset the watchdog, and set flag
	wdt_reset();
	HeartBeat = true;	
}


// function to configure the watchdog:

void configure_wdt(void)
{
/*	WDTCSR Watchdog Timer Control Register
	bit		7		6		5		4		3		2		1		0
			WDIF	     WDIE	     WDP3	     WDCE	      WDE	     WDP2	     WDP1	   WDP0
			
			WDFI	Watchdog Interupt Flag
			WDIE	Watchdog Interupt Enable
			WDP3	Watchdog Prescalar 3
			WDCE	Watchdog Change Enable - set with WDE for change
			WDE		Watchdog System Reset Enable
			WDP2-0	Watchdog Prescalars 2-0
*/
  cli();                          	                // disable interrupts for changing the registers
  MCUSR = 0;                      	                // reset status register flags
										
  WDTCSR |= 0b00011000;            	        // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
									
  WDTCSR = 0b01000000 | 0b00000111;	// set WDIE: interrupt enabled
							// clr WDE: reset disabled
							// and set delay interval. see below

  sei();                           	                // re-enable interrupts

  /* delay interval patterns:
		16 ms:	0b00000000
		32 ms:	0b00000001
		64 ms:	0b00000010
		125 ms:	0b00000011
		250 ms:	0b00000100
		500 ms:	0b00000101
		1 Sec:	0b00000110
		2 Sec:	0b00000111
		4 Sec:	0b00100000
		8 Sec:	0b00100001
  */
}

void setup()
{ 
  // use led 13 and put it in low mode
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  
  Serial.begin(115200);
  Serial.println("Starting");
  
  // configure the watchdog
  configure_wdt();
}

void loop()
{
	static byte prOut;
	if(HeartBeat)
	{
		HeartBeat = false;
		previousTime = currentTime;
		currentTime = millis();
		Interval = currentTime - previousTime;
		if(Interval <= minTime) minTime = Interval;
		if(Interval >= maxTime) maxTime = Interval;
		
		if(LEDState)
			digitalWrite(led,HIGH);
		else
			digitalWrite(led,LOW);
		LEDState = !LEDState;
		
		if(Serial.available()) prOut = 10;
		
		if(prOut)  // print the the current and next 9 readings
		{
			// 1st time print
			if(Serial.available()) Serial.println("------");

			//clean out receive buffer
			while(Serial.available()) char c = Serial.read();
			
			prOut --;

			Serial.print("Interval = ");
			Serial.println(Interval);
			Serial.print("Min time = ");
			Serial.println(minTime);
			Serial.print("Max Time = ");
			Serial.println(maxTime);
			Serial.println();	
		}	
	}
}

this could save someone the headache of keeping track of previous and current time stamps (

Just what is so difficult about this?

This is just a silly idea. The watchdog reset is far too severe a tool to be useful in normal code. Stop tying to be clever and learn how to code properly.

NEVER NEVER use TABs to format a program!

Mark

Not using the reset. There is an option on the timer for interrupt only. Just do a ISR, reset the timer, and done.

Hello, this might belong here.

I'd like to use watchdog timer interrupt for 8sec/1sec timebase. The problem is when I config the watchdog for 1sec timer (after it was running for some time on 8sec without any issue) it goes crazy. Here is my watchdog setting.

 //watchdog setting
  MCUSR = 0;      // clear various "reset" flags
  if ( warningActive == false )
  {
    WDTCSR = bit (WDCE) | bit (WDE);      // allow changes, disable reset
    // set interrupt mode and an interval
    WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  }
  else
  {
    WDTCSR = bit (WDCE) | bit (WDE);      // allow changes, disable reset
    // set interrupt mode and an interval
    WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);    // set WDIE, and 1024 ms delay
  }
  wdt_reset();  // pat the dog

So when warningActive = true, I'd like to use 1024 ms delay, but instead of that, I get far more often watchdog interrupts. Should I configure it differently under "else" branch?

Thanks in advance.

I'd like to use watchdog timer interrupt for 8sec/1sec timebase.

As with the original poster, we have to ask why you want to do this? Is the Arduino going to be in a low power sleep mode? If not, just use millis().

PaulRB:
As with the original poster, we have to ask why you want to do this? Is the Arduino going to be in a low power sleep mode? If not, just use millis().

I didn't mention that directly, but yes. The watchdog interrupt wakes up the controller.

Try using "_BV(x)" instead of "bit(x)".

Try moving "wtd_reset()" to before the "if".

Try setting "WDIF" also when you set "WDCE".

I don't know if any of those suggestions will help....

PaulRB:
Try using "_BV(x)" instead of "bit(x)".

Try moving "wtd_reset()" to before the "if".

Try setting "WDIF" also when you set "WDCE".

I don't know if any of those suggestions will help....

Sorry, I didn't post the WDT_vect, where I also detach the other interrupt.

// watchdog interrupt
ISR (WDT_vect)
{
  wdt_disable();  // disable watchdog
  detachInterrupt(digitalPinToInterrupt(MENU_BUTTON_PIN));
  watchdogInterrupt = true;
}  // end of WDT_vect
  1. bit() shouldn't be the problem, works fine in every other situation.

  2. I'm just guessing here, watchdog timer is disabled in the WDT_vect, I think the timer is stopped. But I will give this a try in the evening.

  3. You mean clearing WDIF by writing logic 1?
    WDIF should be automatically cleared in the WDT_vect. ("WDIF is cleared by hardware when executing the corresponding interrupt handling vector.") And WDRF is cleared by MCUSR = 0.

EDIT
There is a datasheet example about this. I think the problem will be the reset "place", as your second suggestion, thanks for that.

The sequence was incorrect in my code, the WDT was enabled without counter reset, and with the modified prescaler it fired immediately and this started over and over again. I will confirm this in the evening.

The datasheet example:

void WDT_Prescaler_Change(void)
{
__disable_interrupt();
__watchdog_reset();
/* Start timed equence */
WDTCSR |= (1<<WDCE) | (1<<WDE);
/* Set new prescaler(time-out) value = 64K cycles (~0.5 s) */
WDTCSR = (1<<WDE) | (1<<WDP2) | (1<<WDP0);
__enable_interrupt();
}

I changed the code to this:

noInterrupts();
  //watchdog setting
  MCUSR = 0;      // clear various "reset" flags
  wdt_reset();  // pat the dog
  if ( warningActive == false )
  {
    WDTCSR = bit (WDCE) | bit (WDE);      // allow changes, disable reset
    // set interrupt mode and an interval
    WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  }
  else
  {
    WDTCSR = bit (WDCE) | bit (WDE);      // allow changes, disable reset
    // set interrupt mode and an interval
    WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);    // set WDIE, and 1024 ms delay
  }
interrupts();  // one cycle

Still not OK, I think RE-configuring the WDT has to be done somehow else, because if I configure the WDP bits in the else branch, as it was configured earlier when "warningActive" was false (e.g. for 1sec delay), than the timer behaves as expected.

EDIT
I printed WDTCSR and MCUSR values before going to sleep.

Normal case:
MCUSR --> 0
WDTCSR --> B01100001 --> This is OK, WDIE, WDP bits are set correctly.

"Else" case:
MCUSR --> 0
WDTCSR --> B01001000 --> This is NOK, WDIE set, but all WDP bits are cleared and WDE set to logic 1.

From datasheet:
"WDE is overridden by WDRF in MCUSR. This means that WDE is always set when WDRF is set. To clear WDE, WDRF must be cleared first. This feature ensures multiple resets during conditions causing failure, and a safe start-up after the failure."

WDRF seems to be cleared, because the serial print gives 0.

What am I missing here?