AtTiny25 external interrupts / handles

Hi Gurus,

Here is my code for low-power device, atTiny 25/45/85.

It sleeps until wakes up with PCINT. I use PB3 and PB4 INTs,
pins 2 and 3 on chip. When it wakes, it moves to WDT-mode,
causing LED blink every 8 sec.

Power consumption before wake-up is round 0,1uA, and in
WDT-mode round 6uA: quite good, I assume.

BUT: I would like do different things if wake-up signal (grounding
PB3 or PB4) occurs. Only thing seems to be, that both INTs are
triggering function ISR (PCINT0_vect). I have tried ISR(PCINT…younameit)
but grounding PB3 or PB4 allways goes to ISR(PCINT0_vect).

It would be very nice to have own ISR-handlers for PB3 and PB4
caused interrupts. This is the core of my question. I have googled
around and tested some codes without success - so please don’t send me
links (which I already have tested).

Any ideas to split functionality? In my application PB4 is the
trigger for certain action, PB3 should be battery monitor (I use
NCP301-series voltage monitor there).

Following code is mostly adapted/stolen/modified from
Nick Gammon’s great site. AND: I only use AtTiny25 for
this project, so don’t advise me on 328 or AtMega.

I need help with AtTiny25 now. Thanks in advance, -kari

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

const byte LED = PB0; // pin 5

const byte POWER = PB3; // pin2 /
const byte SWITCH = PB4; // pin 3 /

static int battLow=0, water=0;

void ledBlink()
digitalWrite (LED, HIGH);
delay (100);
digitalWrite (LED, LOW);
delay (100);
} while (true);

ISR (PCINT0_vect)
} // end of PCINT0_vect

// watchdog interrupt
ISR (WDT_vect)
wdt_disable(); // disable watchdog
} // end of WDT_vect

void setup ()
resetWatchdog (); // do this first in case WDT fires
wdt_disable(); // disable watchdog

pinMode (LED, OUTPUT);
pinMode (SWITCH, INPUT);
pinMode (POWER, INPUT);

digitalWrite (SWITCH, HIGH); // internal pull-up
digitalWrite (POWER, HIGH); // internal pull-up

GIMSK |= _BV(PCIE); // Enable Pin Change Interrupts
PCMSK |= _BV(PCINT4); // Use PB4 as interrupt pin

GIMSK |= _BV(PCIE); // Enable Pin Change Interrupts
PCMSK |= _BV(PCINT3); // Use PB3 as interrupt pin

ADCSRA &= ~_BV(ADEN); // ADC off
} // end of setup

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
} // end of resetWatchdog

void loop ()

} // 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

PCINT's call the same ISR for any pin on the port; that's how PCINT's work (and unlike INT0 external interrupt). At the start of your ISR, check which pin changed and take appropriate action (it may make for the most readable code if you put each handler in it's own function, and call those from the ISR). I generally do direct port read at the start of the ISR, and go from there.

Also, you don't need to set PCIE in GIMSK twice.

Do not call a function that uses delay() from within an interrupt.

detachInterrupt() is for external interrupts, nothing to do with PCINTs. To disable PCINTs either do it for a specific bit with PCMSK or for a port by unsetting PCIE.

Hi DrAzzy,

Thank you for your reply.

It seems that I'm a bit confused with ISR's...

I think that I got your point. One more question:
how should a pin read at beginning of ISR? What
shoud I write in the "if PB3 changed" -part?

Pseudo code

function PB_3
do something; return

function PB_4
do something; return

ISR (PCINT0_vect)
if PB3 changed then call PB3_function();
if PB4 changed then call PB4_function();
} // end of PCINT0_vect