Go Down

Topic: Tutorial: Basic Watchdog Timer Setup (Read 130940 times) previous topic - next topic

za_nic

Hi Stanley,

I've never tried but I presume it would be fairly easy to just count the number of reset interrupts with the WDE set to 0 (reset off), there may be a conflict if your board freezes and cant alter the count value but its a theoretical guess. I don't have access to a board right now so I cant test it but I presume the code logic would work somewhat like this:

Run the example code.
Change the WDT setup so the that board will not restart (WDE = 0; //reset off)
Now the board will no longer reset and the WDT will keep calling an interrupt at whatever period of time it is set up
Insert a variable (say wdt_counter = 0) that will increment up when the WDT interrupt is called.
At however many loops or increments you decide (minus one) you will need to arm the WDT to reset (WDE = 1, reset on)
Now if the interrupt is called the board will reset.
If the WDT is reset or fed you will need to reset the counter and WDT back to zeros to reset the whole loop cycle.

Out of interest what need do you have for doing this?

Hope this helps! Its morning where I am but if your still having problems in 10+ hours let me know and Ill try write some proper example code.

Nic

stan001

Hi Nic,

I'm planning to use the watchdog timer for a COSM feed updates for my sensors.
At times, the COSM will not return a value and timeout... and the timeout is more than 8 secs, so the WDT will kick in and reset the board first before the timeout from the COSM server..

So I kinda need a longer timeout value more than 8 secs

Is this a good application/example to use the WDT ??

My codes below for the main loop...

Code: [Select]


  ... put sensor values into arrays feed and call the comsclient.put function...

  cosmReturn = cosmclient.put(feed,cosmKey);   // Send feed to cosm
  Serial.print("Cosmclient returned value : "); // Get return code, similar to HTTP code
  Serial.println(cosmReturn);

  wdt_reset();  // Reset WDT timer

  delay(2000); // Put a delay before the next updates to Cosm




za_nic

#17
Oct 22, 2012, 06:38 pm Last Edit: Oct 22, 2012, 10:34 pm by za_nic Reason: 1
Apologies for late reply!

As I mentioned in my previous post, it would be possible to extend the 8s timer to something longer by applying the WDE to 1 on the second iteration of the WDT interrupt but there is still a small risk that the board could potentially freeze or encounter an error when reconfiguring the WDT fuses. I believe the likely hood to be quite low but it still exists and if encountered would cause your board to hang indefinitely or until a hardware reset is performed.

I've modified the example code to include this step. I feel I should stress once again that this does make the WDT quite vulnerable to flaws. If your looking for a more serious solution, look at implementing a second arduino or ATtiny chipset (you'll need a super basic one...) to monitor and hard reset the main board in the event of a fault or error.

*note: I don't have a board at hand to test this code on so your going to have to guinea pig it for me

Code: [Select]
#include <avr/wdt.h>

int loop_count = 0;
int wdt_counter = 0;

void setup() {

 Serial.begin(9600);
 Serial.println("Starting up...");
 pinMode(13,OUTPUT);
 digitalWrite(13,HIGH);
 delay (500);
 watchdogStart();
}

void watchdogStart(void)
{
 cli();  // disable all interrupts
 wdt_reset(); // reset the WDT timer
/*
//WDTCSR configuration:
WDIE = 1: //Interrupt Enable
WDE = 1 : //Reset Enable
WDP3 = 0 :
For 2000ms Time-out WDP2 = 1 :
For 2000ms Time-out WDP1 = 1 :
For 2000ms Time-out WDP0 = 1 :
   For 2000ms Time-out
   */
 // Enter Watchdog Configuration mode:
 WDTCSR |= (1<<WDCE) | (1<<WDE); // Set Watchdog settings:
 WDTCSR = (1<<WDIE) | (0<<WDE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (1<<WDP0);
 sei();
}

void watchdogArm(void)
{
 cli();  // disable all interrupts
 wdt_reset(); // reset the WDT timer
/*
//WDTCSR configuration:
WDIE = 1: //Interrupt Enable
WDE = 1 : //Reset Enable
WDP3 = 0 :
For 2000ms Time-out WDP2 = 1 :
For 2000ms Time-out WDP1 = 1 :
For 2000ms Time-out WDP0 = 1 :
   For 2000ms Time-out
   */
 // Enter Watchdog Configuration mode:
 WDTCSR |= (1<<WDCE) | (1<<WDE); // Set Watchdog settings:
 WDTCSR = (1<<WDIE) | (1<<WDE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (1<<WDP0);
 sei();
}

void loop()
{
 for (int i = 0; i <= loop_count;i++){
   digitalWrite(13,HIGH);
   delay(100);
   digitalWrite(13,LOW);
   delay(100);
 }
 loop_count++;
 wdt_reset();
 watchdogStart();
 wdt_counter = 0;
 Serial.print(loop_count);
 Serial.print(". Watchdog fed in approx. ");
 Serial.print(loop_count*200);
 Serial.println(" milliseconds.");
}
ISR(WDT_vect) // Watchdog timer interrupt.
{
 if(wdt_counter==0)
 {
 wdt_counter++;
 watchdogArm();
 }
 // Include your code here - be careful not to use functions they may cause the interrupt to hang and // prevent a reset.
}


jack wp

@za_nic,
really informative. I ran the test code, and it worked perfect, right out of the box.
Thanks, Jack

chalupien

Great tutorial, I was wondering for more power saving I read that I can upload the 8Mhz bootloader to the ATMEGA328P instead of the default 16Mhz setting. Makes sense. But I read that this will mess with timers. Would this change make a difference? Save Power?

-Nick


daniel2415

Za Nick, In your program you mention about code that will cause the arduino to hang,
" Include your code here - be careful not to use functions they may cause the interrupt to hang and // prevent a reset."

I am a newbie to Arduino, and I wonder what functions would cause it to hang, In my program I am using a watchdog timer to reset a Christmas light project, so I have a bunch of delays to trigger relays for lights, but one small problem, I have  a hell of time trying to get it to call a reset and run a bunch delays. Here is my code that I have been working with.

#include <avr/wdt.h>
int loop_count = 0;
const int buttonPin = 12;
int ledpin13=13;
int ledpin11=11;
int ledpin1=2;
int ledpin2=3;
int ledpin3=4;
int ledpin4=5;
int ledpin5=6;
int ledpin6=7;
int ledpin7=8;
int ledpin8=9;
int ledpin9=10;
int buttonState = 0;

void setup()
{
   Serial.begin(9600);
   Serial.println("Starting up...");
   pinMode(13,OUTPUT);
   pinMode(9,OUTPUT);
    pinMode(8,OUTPUT);
   digitalWrite(13,HIGH);
   delay(500);
   watchdogSetup();
   
}

void watchdogSetup(void)
{
  cli();
  wdt_reset();
  WDTCSR |=(1<<WDCE) | (1<<WDE);
  WDTCSR = (1 <<WDIE) | (1<<WDE) | (0<<WDP3) |(1<<WDP2) | (1<<WDP1) | (1<<WDP0);
  sei();
 
}

void loop()
{ pinMode(12, INPUT);
  int GreenWire = digitalRead(12);
  if (GreenWire ==HIGH)
    { 
     program();
    }
  else if(GreenWire ==LOW);
    {
       WatchDog();
    }
}

void program()
{      unsigned long time;
        time = millis()/1000;
        Serial.println(time);
       
        if (time < 17 )
      {
        SeqA();
 
      }
   else if (time ==17 || time ==18)     
     {
       
       SeqA(); 
     }
     return;

     
}



void SeqA()
{
  digitalWrite(ledpin1,HIGH);
  digitalWrite(ledpin2,HIGH);
  digitalWrite(ledpin3,HIGH);
  digitalWrite(ledpin4,HIGH);
  digitalWrite(ledpin5,HIGH);
  digitalWrite(ledpin6,HIGH);
  digitalWrite(ledpin7,HIGH);
  digitalWrite(ledpin8,HIGH);
 delay(525);
   digitalWrite(ledpin8,LOW);
  digitalWrite(ledpin7,LOW);
  delay(525);//3
  digitalWrite(ledpin8,HIGH);
  digitalWrite(ledpin7,HIGH);
  return;
}
void WatchDog()
{   
   
     for (int i = 0; i <= loop_count;i++)
      {

        digitalWrite(13,HIGH); delay(100); digitalWrite(13,LOW); delay(100);
       
      }
      loop_count++;
       
     
      wdt_reset();
   
      Serial.print(loop_count);
      Serial.print(".Watchdog fed in approx."); Serial.print(loop_count*200); Serial.println("milliseconds.");
      return;

}

flagtrax

Pardon my stupidity, but I'm just starting to play with the concept of the watch dog timer. Originally I was going to use an external timer (ie:555) to reset several scattered modules associated with my growing project when I came across this thread. I am confused though about some of the statements in the sketch presented by za_nic, probably because I'm new to arduino as well. Anyway what I'm confused about is the statements like WDTCSR |= (1<<WDCE) | (1<<WDE); that use << I'm assuming that sets the variable to 1? and the vertical line between (which in my previous experience we called "pipe". Is this a delimiter? I can't seem to find reference to these. I could probably just copy/past and check the outcome, but I'd like to attempt to understand first. I'm also curious if the reset caused by the WDT is visible on the reset pin, so that it may be used externally.
thanks for any help.

CrossRoads

Quote
I'm also curious if the reset caused by the WDT is visible on the reset pin, so that it may be used externally.
I don't think so. Internal only as the Reset pin can also be configured to be used as a general IO pin (with a fuse change).
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Coding Badly

...that use << I'm assuming that sets the variable to 1?
Bit shift left.  WDCE are WDE simple numeric constants.

https://www.google.com/search?q=c%2B%2B+operator+shift+left

Quote
...and the vertical line between (which in my previous experience we called "pipe". Is this a delimiter?
Bitwise or.

https://www.google.com/search?q=c%2B%2B+operator+bitwise+or


flagtrax

"Bit shift left.  WDCE are WDE simple numeric constants."

Thanks for that, Coding Badly I searched for << to no avail :smiley-red: .

So I gather he's using this method to set the WDT characteristics so that it may be done within the 4 clock cycles stated in the Data sheets?

nickgammon

#26
Feb 14, 2015, 06:16 am Last Edit: Feb 14, 2015, 07:38 am by Nick Gammon
Code: [Select]

  WDTCSR = (1 <<WDIE) | (1<<WDE) | (0<<WDP3) |(1<<WDP2) | (1<<WDP1) | (1<<WDP0);


The compiler will turn the RHS of that statement into a single constant. For example, WDIE is the number 6, so 1 << WDIE will be the 6th bit along (counting from zero).

A bit of disassembling of that function shows that it is done within 4 clock cycles (3, in fact):

Code: [Select]

void watchdogSetup(void);
  cli();
  a6:   f8 94           cli   (1)

  wdt_reset();
  a8:   a8 95           wdr   (1)

// load Z register with address 0x0060
  aa:   e0 e6           ldi     r30, 0x60       ; 96   (1)   // WDTCSR
  ac:   f0 e0           ldi     r31, 0x00       ; 0   (1)

  WDTCSR |=(1<<WDCE) | (1<<WDE);
  ae:   80 81           ld      r24, Z   (2)    // get existing WDTCSR
  b0:   88 61           ori     r24, 0x18       ; 24   (1)  // OR in new value
  b2:   80 83           st      Z, r24   (2)    // save to WDTCSR

  WDTCSR = (1 <<WDIE) | (1<<WDE) | (0<<WDP3) |(1<<WDP2) | (1<<WDP1) | (1<<WDP0);
  b4:   8f e4           ldi     r24, 0x4F       ; 79   (1)
  b6:   80 83           st      Z, r24   (2)

  sei();
  b8:   78 94           sei   (1)
 
}
  ba:   08 95           ret   (4)



(Cycle counts in brackets)

From the moment that WDTCSR is written to the first time, it only takes 3 clock cycles to write to it again.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

Quote
I'm also curious if the reset caused by the WDT is visible on the reset pin, so that it may be used externally.
No I doubt that. The reset pin is an input, not an output.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

flagtrax

Thanks Nick, excellent explanation! And I have indeed verified that reset is not visible on the (reset)  pin. Perhaps using a spare GPIO pin in a setup function could be used to reset "downstream" modules. FYI this all relates to sending commands from a remote laptop via 8266 wifi modules. The one I'm working with seems to be a bit qwerky, and seems to have a delay in responding at times. Thanks again!

Coding Badly

Quote
Perhaps using a spare GPIO pin in a setup function could be used to reset "downstream" modules.
Assuming the downstream modules have an active low reset: tie their reset to ground with a pulldown resistor.  Connect that to an Arduino pin.  To bring the modules out of reset change the pin to OUTPUT / HIGH.  Doing it this way guarantees when the Arduino is in reset so are the modules.  It also allows the Arduino to reset the modules at will.


Go Up