Auf einem ATMega328p versuche ich, ein Programm einzurichten, das 1-Sekunden-Intervalle über den Watchdog-Timer zählt, auch wenn sich der Chip im Ruhemodus befindet. Ein Knopf-Interrupt versetzt den Chip in den Schlafmodus. Das Problem, das ich habe, ist, dass der Watchdog-Vektor im Schlafmodus anscheinend nie aufgerufen wird. Ich glaube, ich habe alles so eingerichtet, wie es sein sollte, kann aber nicht herausfinden, was falsch ist. Irgendwelche Gedanken?
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
//#include <avr/io.h>
#define SLEEP_ENABLED MCUCR & _BV(SE)
void setup()
{
pinMode(2, INPUT);
digitalWrite(2, HIGH); //enable pullup
Serial.begin(115200);
MCUSR &= ~(1<<WDRF); //Clear WDT Reset Flag
/* In order to change WDE or the prescaler, we need to
* set WDCE (This will allow updates for 4 clock cycles).
*/
WDTCSR |= (1<<WDCE) | (1<<WDE);
/* set new watchdog timeout prescaler value */
WDTCSR = 1<<WDP2 | 1<<WDP1; /* 1.0 seconds */
//WDTCSR = 1<<WDP3 | 1<<WDP0; /* 8.0 seconds */
/* Enable the WD interrupt (note no reset). */
WDTCSR |= _BV(WDIE);
attachInterrupt (0, initSleep, LOW);
}
volatile uint32_t _count = 0;
volatile uint32_t _sleepCount = 0;
ISR(WDT_vect)
{
_count++;
//if(PIND & _BV(PIND2))
if(SLEEP_ENABLED)// && _count >= _sleepCount + 5)
{
wakeSleep();
}
}
volatile uint8_t _oldADC = ADCSRA;
void wakeSleep()
{
attachInterrupt (0, initSleep, LOW);
ADCSRA = _oldADC;
//setClock16();
sleep_disable();
}
inline void _doSleep()
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
//Do actual sleep
// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
sleep_cpu();
/////////////////
}
void initSleep()
{
_sleepCount = _count;
detachInterrupt(0);
//setClock8();
//disable ADC to save power
_oldADC = ADCSRA;
ADCSRA = 0;
/////////////////////////////
_doSleep();
}
static uint8_t __clock_prescaler = (CLKPR & (_BV(CLKPS0) | _BV(CLKPS1) | _BV(CLKPS2) | _BV(CLKPS3)));
void setClock(uint8_t prescale) //Only call from within an interrup
{
// Disable interrupts.
uint8_t oldSREG = SREG;
//cli();
// Enable change.
CLKPR = _BV(CLKPCE); // write the CLKPCE bit to one and all the other to zero
// Change clock division.
CLKPR = prescale; // write the CLKPS0..3 bits while writing the CLKPE bit to zero
__clock_prescaler = prescale;
// Recopy interrupt register.
SREG = oldSREG;
//sei();
}
void setClock16() { setClock(0x0); }
void setClock8() { setClock(0x1); }
void loop()
{
static uint32_t lastCount = -1;
if(!SLEEP_ENABLED)
{
if(lastCount != _count)
{
Serial.println(_count, DEC);
lastCount = _count;
delay(100);
}
}
else
{
_doSleep();
}
}
Lösung gefunden. _doSleep wurde innerhalb des INT0-Interrupts aufgerufen, was bedeutete, dass es sich während der Ruhephase effektiv innerhalb eines Interrupts befand. Und deshalb konnte der Watchdog-Interrupt nicht ausgelöst werden. Geändert, um ein Flag im INT0-Interrupt zu setzen und dann tatsächlich in der Hauptschleife schlafen zu gehen.
Rev
Adam Haile
Adam Haile
Rev
Ignacio Vazquez-Abrams
#define
Klammern setzen.