Watchdog-Timer wird im Schlafmodus nicht aufgerufen

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();
    }
}
Ist das der komplette Code? Ist es möglich, dass Sie das Bit "Global Interrupt Enable" in SREG nie gesetzt haben, um Interrupts zu aktivieren?
Ja, das ist alles. Seltsam ... die Interrupts funktionieren, bis es schlafen geht (so wird es schlafen gelegt). Ich werde es trotzdem versuchen wieder zu aktivieren.
Ich habe es herausgefunden ... es war nur ein seltsames Timing-Problem, wann die Dinge aufgerufen wurden.
Wenn Sie etwas genauer werden können, könnte dies anderen in Zukunft helfen.
Nebenbei können Sie den Ausdruck in #defineKlammern setzen.

Antworten (1)

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.