Sleep mode with internal interrupt?

Hey all,

I’m working on a project where power consumption is a big concern. I’d like to know how to get the ATmega into power-save mode and have an internal timer wake it back up. From the datasheet it looks like Timer2 should work as a wake-up source.

I’ve tried something like below, both with and without MsTimer, but it doesn’t work. Instead of waiting for the interrupt it immediately wakes up. If the timer isn’t started in the first place or it is disabled with the rest of the power_*_disable() calls then it goes to sleep like normal.

If something isn’t clear I’ll clarify. Any help would be appreciated!

#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <MsTimer2.h>

#define INIT_TIMER2_COUNT 0 

int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter


void timer2_interrupt(){
 Serial.println("wakeup!");
}

void setup()
{
  Serial.begin(9600);
  MsTimer2::set(4000, timer2_interrupt);
  MsTimer2::start();
}

void sleepNow()
{
  /* Now is the time to set the sleep mode. In the Atmega8 datasheet
   * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
   * there is a list of sleep modes which explains which clocks and 
   * wake up sources are available in which sleep modus.
   *
   * In the avr/sleep.h file, the call names of these sleep modus are to be found:
   *
   * The 5 different modes are:
   *     SLEEP_MODE_IDLE         -the least power savings 
   *     SLEEP_MODE_ADC
   *     SLEEP_MODE_PWR_SAVE
   *     SLEEP_MODE_STANDBY
   *     SLEEP_MODE_PWR_DOWN     -the most power savings
   *
   *  the power reduction management <avr/power.h>  is described in 
   *  http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
   */

  set_sleep_mode(SLEEP_MODE_PWR_SAVE);   // sleep mode is set here

  sleep_enable();          // enables the sleep bit in the mcucr register
  // so sleep is possible. just a safety pin 

  power_adc_disable();
  power_spi_disable();
  power_timer0_disable();
  power_timer1_disable();
 // power_timer2_disable();
  power_twi_disable();

  sleep_mode();            // here the device is actually put to sleep!!

  // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  sleep_disable();         // first thing after waking from sleep:
  // disable sleep...

  power_all_enable();

}


void loop()
{
  delay(750);
  
 // display information about the counter
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(1000);                           // waits for a second

  // compute the serial input
  if (Serial.available()) {
    int val = Serial.read();
    if (val == 'S') {
      Serial.println("Serial: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep 
      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
    }
    if (val == 'A') {
      Serial.println("Hola Caracola"); // classic dummy message
    }
  }

  // check if it should go asleep because of time
  if (count >= 10) {
    Serial.println("Timer: Entering Sleep mode");
    delay(100);     // this delay is needed, the sleep 
    //function will provoke a Serial error otherwise!!
    count = 0;
    sleepNow();     // sleep function called here
  }
}

Edit: I realize using MsTimer may not be the best option as it looks like it’s real interrupt (that would be waking everything up) could be called more often than the higher-level interrupt it triggers. But regardless of how I implement the timer it’s always waking up immediately.

Edit 2: After looking closer MsTimer probably won’t work here as the Timer2 interrupt is triggered almost constantly. I still don’t know if this is the only problem since it wasn’t working without MsTimer so I’ll keep at it.

I'm a bit confused by how the timer works but at 8 MHz and and an 8 bit counter how do you count to anything more than a few milliseconds? If anyone could show me how to set up Timer2 (and that pesky prescaler) so it interrupts at some longer interval that would be useful. Only idea I can think of is to keep putting it back to sleep in the interrupt and increment a counter until it's really time.