ATtiny85 interrupt edge during sleep

Hi everyone,
i need to wake up the ATtiny85 only when the interrupt edge FALLING.
To enable the interrupt edge, i can't use the Power Down sleep mode, because it disable the clkI/O clock, so i use the IDLE sleep mode.

Now i would like to disable more modules as possible to preserve the battery life.
Could i disable the time0 or 1 to preserve the battery?
Are there any other modules that I can disable?

This is my code for the sleep function:

void sleepMode() {

        // Set sleepMode to IDE to keeps the clkI/O for the interrupt edge
        set_sleep_mode (SLEEP_MODE_IDLE);
        noInterrupts (); // make sure we don't get interrupted before we sleep

        // Turn off ADC
        // You must use the PRR after setting ADCSRA to zero,
        // otherwise the ADC is "frozen" in an active state.
        ADCSRA = 0;

        // Power Reduction Register (PRR)
        // This lets you "turn off" various things inside the processor.

        // disable modules availables
        power_adc_disable(); // ADC converter
        power_timer0_disable();// Timer 0
        power_timer1_disable();// Timer 1
        power_usi_disable();

        sleep_enable (); // ready to sleep
        // Set the interrupt
        attachInterrupt(digitalPinToInterrupt(interruptPin), interrupt, RISING);
        interrupts ();     // interrupts allowed now, next instruction WILL be executed
        sleep_cpu ();    // sleep

        detachInterrupt(interruptPin); // Disable external pin interrupt on wake up pin.
        sleep_disable (); // precaution
        power_all_enable (); // power everything back on
}

You may use PRR register to disable peripherals (read the Datasheet for details).
You should also disable Analog Comparator.

If the wake up signal is not very short you may use Pin Change Interrupt for this. After wake up from PCINT check the pin. If it is HIGH it was rising edge so you go back to sleep. If the pin is LOW it was falling edge and it is time to act. This works for power down sleep which is much better if you aim for low power consumption.

Use SLEEP_MODE_POWER_DOWN, and a PCINT or level interrupt to wake on. Leaving it in IDLE mode instead of putting it all the way to sleep, just so you can use a rising edge interrupt is insane if you want low power consumption.

Smajdalf:
After wake up from PCINT check the pin. If it is HIGH it was rising edge so you go back to sleep. If the pin is LOW it was falling edge and it is time to act. This works for power down sleep which is much better if you aim for low power consumption.

Good idea of save the current status of the register, if it's changed, power all modules. ( or i need to wake up some module to do this check?? )

DrAzzy:
Use SLEEP_MODE_POWER_DOWN, and a PCINT or level interrupt to wake on. Leaving it in IDLE mode instead of putting it all the way to sleep, just so you can use a rising edge interrupt is insane if you want low power consumption.

Yes you are right. Moreover SLEEP_MODE_IDLE not stops the millis over timer0 and the tiny85 wake-up immediately.

Interesting topic: wake atmega328 from sleep with rising event - #2 by johnwasser - Programming Questions - Arduino Forum

Gemmon uses the SLEEP_MODE_PWR_DOWN and the edge.
The only different is "EIFR = bit (INTF0); // clear flag for interrupt 0"

void sleepNow (){
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);   
  noInterrupts ();          // make sure we don't get interrupted before we sleep
  sleep_enable ();          // enables the sleep bit in the mcucr register
  EIFR = bit (INTF0);       // clear flag for interrupt 0
  attachInterrupt (0, wake, RISING);  // wake up on rising edge
  interrupts ();           // interrupts allowed now, next instruction WILL be executed
  sleep_cpu ();            // here the device is put to sleep
  detachInterrupt (0);      // stop this interrupt until next time
}

So the question is:
Save the register status ( probably the best solution ) Gammon Forum : Electronics : Microprocessors : Interrupts at "Why disable interrupts?"

void sleepMode() {

        // Set sleepMode 
        set_sleep_mode (SLEEP_MODE_PWR_DOWN);
        noInterrupts (); // make sure we don't get interrupted before we sleep

        // Turn off ADC
        // You must use the PRR after setting ADCSRA to zero,
        // otherwise the ADC is "frozen" in an active state.
        ADCSRA = 0;

        // Power Reduction Register (PRR)
        // This lets you "turn off" various things inside the processor.

        // diable all modules availables
        power_all_disable (); // power off ADC, Timer 0 and 1, serial interface

        sleep_enable (); // ready to sleep
        uint8_t oldSREG = SREG;

        interrupts ();     // interrupts allowed now, next instruction WILL be executed
        sleep_cpu ();    // sleep

        // Check the register status
        if( oldSREG == SREG) { // no change in the interrupt, go to sleep again
                sleep_enable (); // ready to sleep
                oldSREG = SREG;

                interrupts (); // interrupts allowed now, next instruction WILL be executed
                sleep_cpu (); // sleep
        }

        detachInterrupt(interruptPin); // Disable external pin interrupt on wake up pin.
        sleep_disable (); // precaution
        power_all_enable (); // power everything back on
}

OR uses the Gammon solution ( what is EIFR is GIFR in the Tiny85 ?? ):

void sleepMode() {

        // Set sleepMode 
        set_sleep_mode (SLEEP_MODE_PWR_DOWN);
        noInterrupts (); // make sure we don't get interrupted before we sleep

        // Turn off ADC
        // You must use the PRR after setting ADCSRA to zero,
        // otherwise the ADC is "frozen" in an active state.
        ADCSRA = 0;

        // Power Reduction Register (PRR)
        // This lets you "turn off" various things inside the processor.

        // diable all modules availables
        power_all_disable (); // power off ADC, Timer 0 and 1, serial interface

        sleep_enable (); // ready to sleep
        GIFR = bit (INTF0);       // clear flag for interrupt 0

        interrupts ();     // interrupts allowed now, next instruction WILL be executed
        sleep_cpu ();    // sleep

        detachInterrupt(interruptPin); // Disable external pin interrupt on wake up pin.
        sleep_disable (); // precaution
        power_all_enable (); // power everything back on
}

markcalaway:
Good idea of save the current status of the register, if it's changed, power all modules. ( or i need to wake up some module to do this check?? )

You should check the status of the pin itself as soon as you wake up. No "module" needed for this. If you use the power down sleep you don't have to disable modules via the PRR register, they will be disabled anyway. ADC is the only exception - it should be disabled by clearing ADEN bit in ADCSRA.

markcalaway:
( what is EIFR is GIFR in the Tiny85 ?? )

RTFM: you will not truly understand what you are doing until you read the ATTiny85 datasheet.

Smajdalf:
You should check the status of the pin itself as soon as you wake up. No "module" needed for this.

So can i use the digitalRead command to check the status of the interrupt BEFORE wake-up (power_all_enable) the modules?

void setup() {
        pinMode(interruptPin, INPUT_PULLUP);
        pinMode(ledDebugPin, OUTPUT);

        // I'm alive!
        digitalWrite(ledDebugPin, HIGH);
        delay(1000);
        digitalWrite(ledDebugPin, LOW);

        attachInterrupt(digitalPinToInterrupt(interruptPin), interrupt, LOW);
}


void loop() {
        // Go to sleep baby
        sleepMode(0);

        wakeup();

        digitalWrite(ledDebugPin, HIGH);
        delay(250);
        digitalWrite(ledDebugPin, LOW);
}


void interrupt() {
        // do samething
}


void sleepMode(bool n) {

        // Set sleepMode
        set_sleep_mode (SLEEP_MODE_PWR_DOWN);
        noInterrupts (); // make sure we don't get interrupted before we sleep

        // Are the modules already disabled?
        if (n==0) {
                // Turn off ADC
                // You must use the PRR after setting ADCSRA to zero,
                // otherwise the ADC is "frozen" in an active state.
                ADCSRA = 0;

                // Power Reduction Register (PRR)
                // This lets you "turn off" various things inside the processor.

                // diable all modules availables
                power_all_disable (); // power off ADC, Timer 0 and 1, serial interface
        }

        sleep_enable (); // ready to sleep
        interrupts ();     // interrupts allowed now, next instruction WILL be executed
        sleep_cpu ();    // sleep

        detachInterrupt(interruptPin); // Disable external pin interrupt on wake up pin.
        sleep_disable (); // precaution
}

void wakeup(){
        // Check the register status
        if( digitalRead(interruptPin) == 0) { // the pin still LOW, to go sleep again
                sleepMode(1);
        }
        else power_all_enable (); // power everything back on
}

But when i try it, the led still on after the interrupt....

UPDATE
That looks a good solution: ATTINY85: Intercept FALLING on interrupt... is it possible? - #26 by nickgammon - Programming Questions - Arduino Forum

it's very similar to my solution, but i don't undertand why he did:
"MCUCR &= ~(bit(ISC01) | bit(ISC00)); // INT0 on low level"

In SLEEP_MODE_POWER_DOWN, you can't wake on rising or falling edge with an external interrupt (eg, INTn), only with low level interrupt on INTn pin, or by PCINT. I have always used PCINTs - I don't see the external interrupts (INTn) as being particularly useful for most applications; I find PCINTs almost as easy to use for simple cases, and for complex cases, the PCINT model is often more convenient and lets me

For example, in my remote control code, I've got 6 buttons on a board that is in deep sleep all the time - all buttons are on the same port, so it's the same ISR regardless of which one fires (IIRC, the ISR is empty, it just wakes the part), then in the main loop, after the sleep command, where it resumes from when it wakes up, I read the PINx register for that port, and feed it to a switch-case statement to decide what to send before going back to sleep.... ). In my dual-encoder-reading setup, I have 2 encoders (4 pins) all on the same port - I then read the port, split out the two bits from each encoder, shift them into a variable that contains their previous value, then look that up in a 16-entry LUT to determine whether the value of the encoder has changed. In another case, there are two buttons to wake from sleep, and the flash usage was so tight I was doing things to save single-digit numbers of bytes of flash. A second ISR was out of the question :wink:

DrAzzy:
In SLEEP_MODE_POWER_DOWN, you can’t wake on rising or falling edge with an external interrupt (eg, INTn), only with low level interrupt on INTn pin, or by PCINT. I have always used PCINTs - I don’t see the external interrupts (INTn) as being particularly useful for most applications; I find PCINTs almost as easy to use for simple cases, and for complex cases, the PCINT model is often more convenient and lets me

In all examples of Gammon he uses the PCINTs, and the “Pin Change Interrupt” https://goo.gl/oKxmBD .
Example: Gammon Forum : Electronics : Microprocessors : Interrupts
It’s more simple than this: https://goo.gl/kD9cUk
Probably for the reason that you said.
In SLEEP_MODE_POWER_DOWN, no rising or falling edge, the easier way is to uses the PCINTs.
If you want to use the rising or falling edge, without the deepSleep, the easier way is to uses the attachInterrupt().

DrAzzy:
For example, in my remote control code, I’ve got 6 buttons on a board that is in deep sleep all the time - all buttons are on the same port, so it’s the same ISR regardless of which one fires (IIRC, the ISR is empty, it just wakes the part), then in the main loop, after the sleep command, where it resumes from when it wakes up, I read the PINx register for that port, and feed it to a switch-case statement to decide what to send before going back to sleep… ). In my dual-encoder-reading setup, I have 2 encoders (4 pins) all on the same port - I then read the port, split out the two bits from each encoder, shift them into a variable that contains their previous value, then look that up in a 16-entry LUT to determine whether the value of the encoder has changed. In another case, there are two buttons to wake from sleep, and the flash usage was so tight I was doing things to save single-digit numbers of bytes of flash. A second ISR was out of the question :wink:

GitHub links?