MCUdude / MightyCore

Arduino hardware package for ATmega1284, ATmega644, ATmega324, ATmega324PB, ATmega164, ATmega32, ATmega16 and ATmega8535
Other
637 stars 181 forks source link

Timer0 stop working after wakeup from power_down sleep #260

Closed garudaonekh closed 1 year ago

garudaonekh commented 1 year ago

Hi, I use the following code to go power_down sleep. However, after wakeup from INT0 interrupt, the millis() function return the same value in the loop. It's based on Timer0 so I guess Timer0 must stop working.

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>

void setup()
{
   DDRC |= (1 << PC2) | (1 << PC1);     // leds for testing

   DDRD &= ~(1 << PD2);    // INT0: input...
   PORTD |= (1 << PD2);    // ...with pullup.

   // level interrupt INT0 (low level)
   MCUCR &= ~((1 << ISC01) | (1 << ISC00));

   // infinite main loop

}
void loop(){
      // trigger leds for testing
      PORTC ^= (1 << PC1);
      _delay_ms(500);
      PORTC ^= (1 << PC1);

      // enable external interrupt
      GICR |= (1 << INT0);

      // set sleep mode
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);

      // sleep_mode() has a possible race condition
      sleep_enable();
      sei();
      sleep_cpu();
      sleep_disable();

      // waking up...
      // disable external interrupt here, in case the external low pulse is too long
      GICR &= ~(1 << INT0);

      // disable all interrupts
      cli();
}

ISR(INT0_vect)
{
   // ISR might be empty, but is necessary nonetheless
   PORTC ^= (1 << PC2);    // debugging
}
MCUdude commented 1 year ago

Timer 0 is by default used for millis/micros(), and is initialized before setup(). it is bein initialised in the init() function:

https://github.com/MCUdude/MightyCore/blob/b360c00e41c494731e2898aaa3afd778f748f3e3/avr/cores/MCUdude_corefiles/main.cpp#L33-L51

If you want to run code natively, without the Arduino framework, ditch setup() and loop() in your sketch, and use this instead:

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main() {
    DDRC |= (1 << PC2) | (1 << PC1);     // leds for testing

    DDRD &= ~(1 << PD2);    // INT0: input...
    PORTD |= (1 << PD2);    // ...with pullup.

    // level interrupt INT0 (low level)
    MCUCR &= ~((1 << ISC01) | (1 << ISC00));  

    while(1) {
        // trigger leds for testing
        PORTC ^= (1 << PC1);
        _delay_ms(500);
        PORTC ^= (1 << PC1);

        // enable external interrupt
        GICR |= (1 << INT0);

        // set sleep mode
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);

        // sleep_mode() has a possible race condition
        sleep_enable();
        sei();
        sleep_cpu();
        sleep_disable();

        // waking up...
        // disable external interrupt here, in case the external low pulse is too long
        GICR &= ~(1 << INT0);

        // disable all interrupts
        cli();
    }
}

ISR(INT0_vect)
{
   // ISR might be empty, but is necessary nonetheless
   PORTC ^= (1 << PC2);    // debugging
}
garudaonekh commented 1 year ago

I don't get you. My problem is millis() return the same value after my above code wakeup from INT0 interrupt. My guess is that my deep sleep code disable the TImer0.

MCUdude commented 1 year ago

SLEEP_MODE_POWER_DOWN stops timer0 as well:

https://onlinedocs.microchip.com/pr/GUID-A834D554-5741-41A3-B5E1-35ED7CD8250A-en-US-5/index.html?GUID-825A28EE-C4E4-4C03-864F-92AA9BA41231

garudaonekh commented 1 year ago

Yes, but when wakeup from PIN interrupt, the loop() run as usual but timer0 still not working. I also call sleep_disable() but still not working.

garudaonekh commented 1 year ago

At sei() after wakeup solved the problem.