Go Down

Topic: Using the watchdog to wake up from sleep (Read 8599 times) previous topic - next topic

Dago

Hey, I'm trying to make a timer thingie that basically just displays the elapsed time on an LCD. I want to put the CPU to sleep every second (or every 500ms) and just wake up from sleep to refresh the LCD with the current time (need to have timer0 running so I can't do a full power-down, just power-save afaik). The documentation on the watchdog and sleep seem surprisingly sparce and I'm having trouble setting it up. Currently I'm doing something like this (just showing the bits related to sleep and watchdog):

Code: [Select]

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

void setup() {
 
 MCUSR = 0;
 wdt_disable();  // disable watchdog on boot
 
 set_sleep_mode(SLEEP_MODE_PWR_SAVE);    // set sleep mode

 // set other stuff
}

void loop() {
 
 sei();          // disable interrupts after waking up from sleep
 wdt_disable();  // disable watchdog when running
 
 // do stuff
 
 wdt_enable(WDTO_500MS);    // enable watchdog
 cli();                     // enable interrupts
 sleep_mode();              // go to sleep
}

ISR(WDT_vect) {  // go back to the main loop when we get an interrupt from the watchdog timer
 loop();
}


But it is not working. Seems like the processor just goes to an infinite watchdog reset loop (very hard to reprogram the chip when its in the loop, you have to reset the chip exactly at the right time so it will get programmed). How do I just simply execute the loop(); every one or every half second and keep the cpu in power-save mode for the rest of the time?

wayoda

Hi,
the standard settimg for the atmega is to do a System-reset when the watchdog times out. If you want to you IRS-metghod to be called instead you have to set some bits  in Register : WDTCSR

I think you should download the Datasheet for the ATMega168 and read chapter 10.9.2. to find out which setting would be correct.

Eberhard

Dago

#2
Nov 14, 2009, 08:27 pm Last Edit: Nov 14, 2009, 09:34 pm by Dago Reason: 1
Code: [Select]
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

void setup() {
 
 MCUSR = 0;
 wdt_disable();  // disable watchdog on boot
 
 set_sleep_mode(SLEEP_MODE_PWR_SAVE);    // set sleep mode

 // set other stuff
}

void loop() {
 
 sei();          // disable interrupts after waking up from sleep
 wdt_disable();  // disable watchdog when running
 
 // do stuff
 
 WDTCSR = 0x55;      
 MCUSR = 0;
 wdt_reset();
 
 cli();
 sleep_mode();
}

ISR(WDT_vect) {  // go back to the main loop when we get an interrupt from the watchdog timer
 loop();
}


I changed it to this and now it does not reset but it just goes to sleep and never wakes up. Never enters the ISR(WDT_vect) function.

Edit: Whoops, sei() and cli() the wrong way. After swapping them the CPU does wake up but quite uhh intermittently and randomly and then stop waking up completely after a while.

Also, it seems timer0 (from which the millis() function gets its time and therefore the DateTime library that I'm using) is not running in power-save mode. It afaik SHOULD be running in idle mode but something weird happens in idle, the time jumps around for hours or days at a time and goes backwards etc., dunno why is that...

Mitch_CA

#3
Nov 15, 2009, 01:34 am Last Edit: Nov 15, 2009, 01:41 am by mitch_79 Reason: 1
I can't really describe in detail why, but this strikes me as something that should cause a problem...
Code: [Select]

ISR(WDT_vect) {  // go back to the main loop when we get an interrupt from the watchdog timer
 loop(); // <- I think this disrupts your expected program flow
}


It is unnecessary at the least.  From the atmega Datasheet: "If an enabled interrupt occurs while the MCU is in a sleep mode, the MCU wakes up. The MCU
is then halted for four cycles in addition to the start-up time, executes the interrupt routine, and
resumes execution from the instruction following SLEEP."


[Edit:  I think I need to look at sleep.h]

Mitch_CA

So... while I was studying, I accidentally wrote some evil code...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1258251558/0

In other variants of my test code, I've been having trouble getting the watchdog timer to Interrupt at anything less than a few milliseconds.

with...
WDTCSR = B01010111;

ISR(WDT_vect) gets called constantly.  According to the datasheet I should expect 2.0s between interrupts but that does not appear to be the case.

I don't mean to hijack your thread Dago, but if we understand this I think we could then solve your application.

Mitch_CA

Ok, I think I have it...

The datasheet is a little vague, but between the example and the content it has become clear that setting WDTCSR is a two step procedure.
First, set WDTCSR = B00011000.  This enables Watchdog Changes (I'm not sure why bit3 WDE: Watchdog System Reset Enable also must be set but it doesn't work otherwise).
Second, set WDTCSR WDPn bits to set prescaler.

Here is some code that is working on my 1280.  Note: I couldn't figure out how to use wdt_enable(value) in wdt.h so I wrote myWatchdogEnable().

Code: [Select]

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

ISR(WDT_vect) {
 cli();
 wdt_disable();
 mySerial.println("wakeup!");
 sei();
}

void setup() {
 mySerial.begin(115200);
 set_sleep_mode(SLEEP_MODE_PWR_SAVE);
}

void loop() {
 delay(500);  // stay awake for 0.5 seconds
 wdt_reset();
 myWatchdogEnable();
 mySerial.println("...going to sleep.");
 delay(100);
 sleep_mode();
}

void myWatchdogEnable() {  // turn on watchdog timer; interrupt mode every 2.0s
 cli();
 MCUSR = 0;
 WDTCSR |= B00011000;
 WDTCSR = B01000111;
 sei();
}


Odisej

If you are looking to make a low-consumption arduino clock I recomend buying a RealTimeClock (RTC) chip and just use full shutdown. Works for me :)

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy