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):
#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?
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.
#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…
I can’t really describe in detail why, but this strikes me as something that should cause a problem…
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."
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().