I've made an arduino sketch that does a software reset using the watchdog timer.
When I want to reset I call wdt_enable and enter an infinite loop. Everything works great.
The problem occurs when I try to run the same sketch on a standalone chip without bootloader. Once it resets, it just hangs there. My theory is that the bootloader must be disabling the watchdog and so since I'm running the chip standalone I need to disable it myself. Great, not a problem.
Unfortunately, running wdt_disable() in the Setup() function doesn't seem to do anything. After some google searching I've found some people saying the code for wdt_disable() is messed up. All the fixes I've tried don't do anything.
The watchdog functions are simple enough for you to go through them on the datasheet and use the appropriate registers to set and reset whatever you need. Have you tried that?
I've found a C code example in the datasheet for disabling the watchdog:
__disable_interrupt();
__watchdog_reset();
/* Clear WDRF in MCUSR */
MCUSR &= ~(1<<WDRF);
/* Write logical one to WDCE and WDE */
/* Keep old prescaler setting to prevent unintentional time-out */
WDTCSR |= (1<<WDCE) | (1<<WDE);
/* Turn off WDT */
WDTCSR = 0x00;
__enable_interrupt();
The functions in here disable_interrupt(), watchdog_reset(), and enable_interrupt() wouldn't compile. I read a forum post elsewhere that suggested I change them to this:
cli();
wdt_reset();
/* Clear WDRF in MCUSR */
MCUSR &= ~(1<<WDRF);
/* Write logical one to WDCE and WDE */
/* Keep old prescaler setting to prevent unintentional time-out */
WDTCSR |= (1<<WDCE) | (1<<WDE);
/* Turn off WDT */
WDTCSR = 0x00;
sei();
Now it compiles, but I still have the same behavior as before :-(.
I wonder if it's even resetting? I have it hooked up to a parallel 16x2 LCD and the display just hangs at whatever text was there before I initiate. Any code I put after the reset doesn't get executed, so I know it's doing something, but if I were in a reset loop wouldn't the display get cleared at some point during reset?
I'm using this code and its working perfectly in a RBBB (an arduino clone with Duemilanove bootloader).
/*
Watchdog Sleepy Bones (the skeleton is the essential armature for any living thing)
This is the basic structure that you need to Sleep_Mode_Pwr_Down/Watchdog interrupt wake
Provided for general consumption by Rachel's Electronics http://www.rachelselectronics.com
Under the CC licence here (http://creativecommons.org/licenses/by-sa/3.0/us/)
Rachel's Electronics: Making the World Safe for Robots! -Joel Murphy joel@joelmurphy.net
Built in part from code posted by:
* KHM 2008 / Lab3/ Martin Nawrath nawrath@khm.de
* Kunsthochschule fuer Medien Koeln
* Academy of Media Arts Cologne
http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
*/
#include <avr/sleep.h>
#include <avr/wdt.h>
#define sleepTime 1 //number of 8 second sleep cycles
volatile byte wdt=0; //used to cound number of sleep cycles
void setup(){
setup_watchdog(); // set prescaller and enable interrupt
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here Power Down uses the least current
pinMode(13,OUTPUT); // system clock is turned off, so millis won't be reliable!
delay(10);
}
void loop(){
// do what you want to before you sleep here
system_sleep();
// do what you want to after you sleep here
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
delay(100);
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
delay(100);
}
void system_sleep() {
ADCSRA |= (0<<ADEN); // disable ADC
sleep_enable(); // enable sleeping
while (wdt < sleepTime){ // sleep for sleepTime * 8sec
sleep_mode(); // activate system sleep
// sleeping ...
}
sleep_disable(); // disable sleep
ADCSRA |= (1<<ADEN); // switch ADC on
wdt = 0; // reset wdt for next cycle
}
void setup_watchdog() {
cli(); //disable global interrupts
MCUSR = 0x00; //clear all reset flags
//set WD_ChangeEnable and WD_resetEnable to alter the register
WDTCSR |= (1<<WDCE) | (1<<WDE); // this is a timed sequence to protect WDTCSR
// set new watchdog timeout value to 1024K cycles (~8.0 sec)
WDTCSR = (1<<WDP3) | (1<<WDP0);
//enable watchdog interrupt
WDTCSR |= (1<<WDIE);
sei(); //enable global interrupts
}
// Watchdog Interrupt Service Routine.
// Very first thing after sleep wakes with WDT Interrupt
ISR(WDT_vect) {
wdt++; // increment the watchdog timer
}
I have however a question that I couldn't figure out reading the datasheet. This line:
WDTCSR |= (1<<WDCE) | (1<<WDE);
sets the Watchdog to both interrupt and reset. I wonder to which point does it reset and why it is necessary to set the watchdog to reset reset in order to obtain the expected functionality (a LED that blinks every 8 seconds).
If I just set it to Interrupt mode, it blinks all the time.
I do the same kind of thing on a different processor and I initially had trouble with it. Not the same problem you're describing, but maybe it will help to set a time in the register. By clearing it to zero you're going for the lowest possible time and that could be your problem. Try setting it to something longer like:
WDTCSR = (0<<WDIE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0); //no interrupts with a 1 second timeout.
If I remember correctly, this will set the timeout to a second.
The reason you do the watchdog reset at the beginning is to make it start counting down all over again. Otherwise it might fire while you're changing it. I saw that somewhere in the datasheet, couldn't find it when I looked again, but the idea stuck to some neuron.