En un ATMega328p, estoy intentando configurar un programa que cuenta intervalos de 1 segundo a través del temporizador de vigilancia, incluso cuando el chip está en modo de suspensión. Una interrupción de botón pone el chip en modo de suspensión. El problema que tengo es que una vez en el modo de suspensión, parece que el vector de vigilancia nunca se llama. Creo que tengo toda la configuración como debería ser, pero no puedo entender qué está mal. ¿Algún pensamiento?
#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();
}
}