El temporizador de vigilancia no se llama en el modo de suspensión

1

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();
    }
}
    
pregunta Adam Haile

1 respuesta

2

Encontré la solución. _doSleep recibía llamadas desde dentro de la interrupción INT0, lo que significaba que durante el período de sueño, estaba efectivamente dentro de una interrupción. Y por lo tanto la interrupción del perro guardián no pudo disparar. Se modificó para establecer un indicador en la interrupción INT0 y luego irse a dormir en el bucle principal.

    
respondido por el Adam Haile

Lea otras preguntas en las etiquetas