Can't seem to wake up from Power Down using Pin 2 interupt (Solved)

Happy new year all! I have a little problem - nothing too crazy. I found some code which lets you wake up the arduino with a pin 2 interupt and I plan to use it with the watchdog timer. The problem is the interupt only seems to work when I comment out my call to system_sleep(), i.e. when the Arduino is put to bed I can't wake it up (the watchdog can if I enabled it). I'm using a Mega 1280 and I think I've probably done something stupid; any ideas what I've done wrong would be appreciated?

Cheers

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

int interrupt_pin = 2;
int led_pin = 13;

volatile int toggle = 0;
volatile int f_wdt = 1;


#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


// This is our button interrup
void pin2Interrupt()
{
	toggle = 1;
	Serial.println("Interupt!");

}


void setup()
{
  Serial.begin(9600);

  pinMode(led_pin, OUTPUT);

  /* Setup the interrupt pin */
  pinMode(interrupt_pin, INPUT);
  attachInterrupt(0, pin2Interrupt, FALLING);

/*
  cbi( SMCR,SE );      // sleep enable, power down mode
  cbi( SMCR,SM0 );     // power down mode
  sbi( SMCR,SM1 );     // power down mode
  cbi( SMCR,SM2 );     // power down mode
*/
 // setup_watchdog(7);

  Serial.println("Initialisation complete.");

}


void loop()
{

	if (toggle == 1) {
		digitalWrite(led_pin, !digitalRead(led_pin));
		toggle=0;
		delay(250);
		//setup_watchdog(7);
		//system_sleep();
	}

	if(f_wdt == 1)	{
		digitalWrite(led_pin, !digitalRead(led_pin));
		f_wdt = 0;
		Serial.println("Awake");
		//system_sleep();
	}
}


//****************************************************************
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {

	// cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF

	set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
	sleep_enable();

	attachInterrupt(0, pin2Interrupt, FALLING);

	sleep_mode();                        // System sleeps here

	sleep_disable();                     // System continues execution here when watchdog timed out

	detachInterrupt(0);

	// Re-enable the peripherals.
	power_all_enable();

	// sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON

}



//****************************************************************
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {

  byte bb;

  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<<WDCE);

  MCUSR &= ~(1<<WDRF);
  // start timed sequence
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  // set new watchdog timeout value
  WDTCSR = bb;
  WDTCSR |= _BV(WDIE);
}


//****************************************************************
// Watchdog Interrupt Service / is executed when  watchdog timed out
ISR(WDT_vect) {
  f_wdt=1;  // set global flag
}

Happy New Year to you!

  attachInterrupt(0, pin2Interrupt, FALLING);
...
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here

Let's check the datasheet...

  1. External Interrupts
    Note that recognition of falling or rising edge interrupts on INT7:4 requires the presence of an I/O clock, described in “Overview” on page 40. Low level interrupts and the edge interrupt on INT3:0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode. The I/O clock is halted in all sleep modes except Idle mode.

In order for FALLING to work, the I/O clock has to be running. The I/O clock only runs in IDLE sleep mode. So, POWER DOWN shuts off the I/O clock which prevents FALLING from working.

You have some choices...

  • Use IDLE sleep mode instead (more power consumed)
  • Use LOW instead of FALLING
  • Use a pin change interrupt instead of an external interrupt

Hi Coding Badly! Yes, that's exactly where I was going wrong, many thanks for that pointing that out! :smiley:

attachInterrupt(0, pin2Interrupt, LOW); // Works brill

I did have a hunch that it might have been that parameter. I thought that to detect a failling or rising change there must be some kind of temporal element to calculate it, i.e. the IO clock. Now I've re-examined some similar code, it's obvious! :blush:

Cheers!

You are welcome.

Bear in mind that the interrupt will continue to fire as long as the pin is low. In other words, if the pin is low when the interrupt service routine returns, the interrupt service routine will be immediately called again.

Thanks for the extra info. :slight_smile: I'm using a button with a pullup resistor, so I think I will detach the interrupt as soon as it is detected and then reattach before putting it back to sleep.

Just for background info: I'm basically making a wireless control with an LCD display and temperature sensor to control my boiler. I just used the Virtual Wire library and got a demo working in seconds! Now all I have to do is start putting it altogether... :cold_sweat: