Low power project, interrupt attached to 6 buttons

Hi -

I have an idea which I'd like to verify! I would like to use 6 buttons on an Arduino, power the thing using 3.3 volt from a coin cell and sleep it until one of the six buttons is pressed.

The buttons are switching ground and each have a single lead going to an input pin. I've enabled the pullup resistors so my pins read HIGH when the button switches are open and LOW when pressed.

I know that you can wake up an Arduino using an interrupt. Mine has 2 but I need 6. Can I wire all 6 buttons to a single interrupt pin using diodes? Will that work?

Thanks

Anyone?

Can I wire all 6 buttons to a single interrupt pin using diodes?

No.

You can use two three input logic gates if you want, something like a 74HC10

Just about any pin can signal an interrupt, if you use pin change interrupts. There are several examples for how to do this. See also http://playground.arduino.cc/Main/PinChangeInt

Fexduino: Can I wire all 6 buttons to a single interrupt pin using diodes? Will that work?

Yes, if only one button is pressed at the same time.

Thanks for the replies. I'm mostly focussed on waking up the arduino, so it looks like I might be able the use the Pin Change Interrupt. I'll read up on it, thanks again.

I think the diode solution can work. Pin-change is possibly smarter.

A TV remote does exactly this task. The processor sleeps for months but any button can wake it up for a few milliseconds to send the IR code.

Not sure if the pin change interrupt can wake up a sleeping ATmega chip. Check the data sheet.

Waking the part from sleep is one of the most useful features of the pin change interrupts. Here is what the ATmega328 data sheet says:

Pin change interrupts on PCINT23...0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode.

This datasheet statement stands in contrast to all other statements about interrupts and sleep modes. As it is not detailed further, I wonder how a level change can be detected asynchronously.

Figure 12-1 shows flipflops in the interrupt signal path, triggered by clk. This part can work only when the flipflops are level triggered, and the clk level (during sleep) enables their inputs statically, except for the LE input of the first flipflop.

Where can I find sample code for starting experiments with PCInts?

DrDiettrich:
Where can I find sample code for starting experiments with PCInts?

http://gammon.com.au/power

// Wake from deep sleep with a keypress demonstration

// Author: Nick Gammon
// Date: 18th November 2012

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

const byte ROWS = 4;
const byte COLS = 4; 

char keys[ROWS][COLS] = 
  {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'},
  };

byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad
  
// number of items in an array
#define NUMITEMS(arg) ((unsigned int) (sizeof (arg) / sizeof (arg [0])))

const byte ledPin = 13;

  // Create the Keypad
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

/*

Pin change interrupts.

Pin                  Mask / Flag / Enable

D0  PCINT16 (PCMSK2 / PCIF2 / PCIE2)
D1  PCINT17 (PCMSK2 / PCIF2 / PCIE2)
D2  PCINT18 (PCMSK2 / PCIF2 / PCIE2)
D3  PCINT19 (PCMSK2 / PCIF2 / PCIE2)
D4  PCINT20 (PCMSK2 / PCIF2 / PCIE2)
D5  PCINT21 (PCMSK2 / PCIF2 / PCIE2)
D6  PCINT22 (PCMSK2 / PCIF2 / PCIE2)
D7  PCINT23 (PCMSK2 / PCIF2 / PCIE2)
D8  PCINT0 (PCMSK0 / PCIF0 / PCIE0)
D9  PCINT1 (PCMSK0 / PCIF0 / PCIE0)
D10  PCINT2 (PCMSK0 / PCIF0 / PCIE0)
D11  PCINT3 (PCMSK0 / PCIF0 / PCIE0)
D12  PCINT4 (PCMSK0 / PCIF0 / PCIE0)
D13  PCINT5 (PCMSK0 / PCIF0 / PCIE0)
A0  PCINT8 (PCMSK1 / PCIF1 / PCIE1)
A1  PCINT9 (PCMSK1 / PCIF1 / PCIE1)
A2  PCINT10 (PCMSK1 / PCIF1 / PCIE1)
A3  PCINT11 (PCMSK1 / PCIF1 / PCIE1)
A4  PCINT12 (PCMSK1 / PCIF1 / PCIE1)
A5  PCINT13 (PCMSK1 / PCIF1 / PCIE1)

*/

// turn off interrupts until we are ready
ISR (PCINT0_vect)
  {
  PCICR = 0;  // cancel pin change interrupts
  } // end of ISR (PCINT0_vect)

ISR (PCINT1_vect)
  {
  PCICR = 0;  // cancel pin change interrupts
  } // end of ISR (PCINT1_vect)

ISR (PCINT2_vect)
  {
  PCICR = 0;  // cancel pin change interrupts
  } // end of ISR (PCINT2_vect)

void setup ()
  {
  pinMode (ledPin, OUTPUT);
  
  // pin change interrupt masks (see above list)
  PCMSK2 |= bit (PCINT22);   // pin 6
  PCMSK2 |= bit (PCINT23);   // pin 7
  PCMSK0 |= bit (PCINT0);    // pin 8
  PCMSK0 |= bit (PCINT1);    // pin 9

  }  // end of setup

// set pins as keypad library expects them
// or call: kpd.initializePins ();
//    however in the library I have that is a private method

void reconfigurePins ()
  {
  byte i;
  
  // go back to all pins as per the keypad library
  
  for (i = 0; i < NUMITEMS (colPins); i++)
    {
    pinMode (colPins [i], OUTPUT);
    digitalWrite (colPins [i], HIGH); 
    }  // end of for each column 

  for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    pinMode (rowPins [i], INPUT);
    digitalWrite (rowPins [i], HIGH); 
    }   // end of for each row

  }  // end of reconfigurePins

void goToSleep ()
  {
  byte i;
   
  // set up to detect a keypress
  for (i = 0; i < NUMITEMS (colPins); i++)
    {
    pinMode (colPins [i], OUTPUT);
    digitalWrite (colPins [i], LOW);   // columns low
    }  // end of for each column

  for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    pinMode (rowPins [i], INPUT);
    digitalWrite (rowPins [i], HIGH);  // rows high (pull-up)
    }  // end of for each row
    
   // now check no pins pressed (otherwise we wake on a key release)
   for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    if (digitalRead (rowPins [i]) == LOW)
       {
       reconfigurePins ();
       return; 
       } // end of a pin pressed
    }  // end of for each row
  
  // overcome any debounce delays built into the keypad library
  delay (50);
  
  // at this point, pressing a key should connect the high in the row to the 
  // to the low in the column and trigger a pin change
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  byte old_ADCSRA = ADCSRA;
  // disable ADC to save power
  ADCSRA = 0;  

  power_all_disable ();  // turn off various modules
   
  PCIFR  |= bit (PCIF0) | bit (PCIF1) | bit (PCIF2);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE0) | bit (PCIE1) | bit (PCIE2);   // enable pin change interrupts
   
  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  sleep_cpu ();  
 
  // cancel sleep as a precaution
  sleep_disable();
  power_all_enable ();   // enable modules again
  ADCSRA = old_ADCSRA;   // re-enable ADC conversion
  
  // put keypad pins back how they are expected to be
  reconfigurePins ();
    
  }  // end of goToSleep
  
void loop () 
  {
   
   byte key =  kpd.getKey();
   if (!key)
     {
     // no key pressed? go to sleep
     goToSleep ();
     return;
     }

  // confirmation we woke - flash LED number of times
  // for the appropriate pin (eg. pin 1: one time) 
  for (byte i = 0; i < (key - '0'); i++)
    {
    digitalWrite (ledPin, HIGH);
    delay (500); 
    digitalWrite (ledPin, LOW);
    delay (500); 
    } // end of for loop
    
  } // end of loop

also:

// ATtiny85 sleep mode, wake on pin change interrupt or watchdog timer
// Author: Nick Gammon
// Date: 12 October 2013

// ATMEL ATTINY 25/45/85 / ARDUINO
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+

#include <avr/sleep.h>    // Sleep Modes
#include <avr/power.h>    // Power management
#include <avr/wdt.h>      // Watchdog timer

const byte LED = 3;  // pin 2
const byte SWITCH = 4; // pin 3 / PCINT4

ISR (PCINT0_vect) 
 {
 // do something interesting here
 }  // end of PCINT0_vect
 
// watchdog interrupt
ISR (WDT_vect) 
{
   wdt_disable();  // disable watchdog
}  // end of WDT_vect

void resetWatchdog ()
  {
  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset, clear existing interrupt
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF);
  // set interrupt mode and an interval (WDE must be changed from 1 to 0 here)
  WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  // pat the dog
  wdt_reset();  
  }  // end of resetWatchdog
  
void setup ()
  {
  resetWatchdog ();  // do this first in case WDT fires
  
  pinMode (LED, OUTPUT);
  pinMode (SWITCH, INPUT);
  digitalWrite (SWITCH, HIGH);  // internal pull-up
  
  // pin change interrupt (example for D4)
  PCMSK  = bit (PCINT4);  // want pin D4 / pin 3
  GIFR  |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK |= bit (PCIE);    // enable pin change interrupts 
  }  // end of setup

void loop ()
  {
  digitalWrite (LED, HIGH);
  delay (500); 
  digitalWrite (LED, LOW);
  delay (500); 
  goToSleep ();
  }  // end of loop
  
void goToSleep ()
  {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface
  noInterrupts ();       // timed sequence coming up
  resetWatchdog ();      // get watchdog ready
  sleep_enable ();       // ready to sleep
  interrupts ();         // interrupts are required now
  sleep_cpu ();          // sleep                
  sleep_disable ();      // precaution
  power_all_enable ();   // power everything back on
  }  // end of goToSleep

Great stuff ... wonderful resource. Especially awesome that you posted the ATTiny code which I will want to target eventually.