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.
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?
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.
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
bit() shouldn't be the problem, works fine in every other situation.
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.
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.
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.