Howto: Using external interrupts on an Atmel AVR microprocessor
A common task in embedded programming is checking when the state of pin has changed. You can write software to poll a pin's state, or in the case of AVR you can set up an interrupt. An interrupt can be used to notify your program when a pin state has changed.
The following example will show how to use external interrupts (as opposed to timer interrupts) on an Atmel AVR using software written in GCC.
The first thing to do is to see what interrupts are available for your model of processor. For this example I will be using the attiny2313.
You can use the data sheet available from atmel to find the interrupts. On the attiny2313 externeral interrupts are triggered by INT0, INT1 and PCINT0..7 . (Table 21).

Next, you must find out which pins can be used as external interrupts. Figure 1 of the datasheet shows that pins 6 (PD2) and pin 7 (PD3) may be used as external interrupts for INT0 and INT1. Pins 12 through 19 can be used for PCINT.

The next step is find out the register that controls the pins that contribute to the pin change interrupt. The PCMSK register controls the external interrupt pins in the atttiny2313.

So what are your options? What can the pins detect?
The interrupts can detect four different types of pin changes:
1. pin goes low
2. any logical change in pin
3. falling edge (pin goes from high to low)
4. rising edge (ping goes from low to high)
Tables 31 and 32 describe this for the attiny2313.

MCUR , the MCU control register, defines which of the four states will active the interrupt. The above bits table 31 and 32 must be set in the MCUR register.
Lastly, we must turn on the interrupt. The general interrupt mask register, GIMSK, controls when an interrupt is enabled.
Now for a code sample. The following sample code will set interrupt int0 when pin 6 goes from high to low:
#include <avr/interrupt.h>
int main (void)
{
// Set Pin 6 (PD2) as the pin to use for this example
PCMSK |= (1<<PIND2);
// interrupt on INT0 pin falling edge (sensor triggered)
MCUCR = (1<<ISC01) | (1<<ISC00);
// turn on interrupts!
GIMSK |= (1<<INT0);
while (1) { }
}
SIGNAL (SIG_INT0)
{
// Do some stuff
}
First tell PCMSK which pin we will use. This line sets bit 2 of PCMSK to 1.
// Set Pin 6 (PD2) as the pin to use for this example
PCMSK |= (1<<PIND2);
Look at table 32 to find out the bits to set to make the interrupt trigger on the falling edge.
// interrupt on INT0 pin falling edge (sensor triggered)
MCUCR = (1<<ISC01) | (1<<ISC00);
Last we turn it on.
// turn on interrupts!
GIMSK |= (1<<INT0);
We have to figure out which vector will be called when the interrupt triggers. To find this out look at the avr/interrupt.h documentation.
| INT0_vect | SIG_INTERRUPT0 | External Interrupt 0 | AT90S1200, AT90S2313, AT90S2323, AT90S2333, AT90S2343, AT90S4414, AT90S4433, AT90S4434, AT90S8515, AT90S8535, AT90PWM3, AT90PWM2, AT90CAN128, AT90CAN32, AT90CAN64, ATmega103, ATmega128, ATmega16, ATmega161, ATmega162, ATmega163, ATmega165, ATmega169, ATmega32, ATmega323, ATmega325, ATmega3250, ATmega329, ATmega3290, ATmega406, ATmega64, ATmega645, ATmega6450, ATmega649, ATmega6490, ATmega8, ATmega8515, ATmega8535, ATmega168, ATmega48, ATmega88, ATmega640, ATmega1280, ATmega1281, ATmega324, ATmega164, ATmega644, ATtiny11, ATtiny12, ATtiny13, ATtiny15, ATtiny22, ATtiny2313, ATtiny26, ATtiny28, ATtiny24, ATtiny44, ATtiny84, ATtiny45, ATtiny25, ATtiny85, ATtiny261, ATtiny461, ATtiny861 |
This signal gets run when the interrupt fires.
SIGNAL (SIG_INT0)
{
// Do some stuff
} The PCINT interrupt differs from INT0 and INT1. With INT0 and INT1 you can trigger the interrupt on the falling or rising edge. With PCINT, the interrupt is triggered by any change in the pin state. Pins 12 through 19 share the same interrupt. The MCU control register (MCUR) is not used with the PCINT interrupt. You set the PCMSK mask to tell the attiny2313 which pins can trigger the interrupt. An example would be to set the mask to pins 12,13 and 14. When any of these three pins changes state, the SIG_PCINT event will fire. The event will not know which of the pins changed states. Sample code: #include <avr/interrupt.h>
int main (void)
{
PCMSK |= (1<<PCINT2); // tell pin change mask to listen to pin14
PCMSK |= (1<<PCINT3); // tell pin change mask to listen to pin15
PCMSK |= (1<<PCINT4); // tell pin change mask to listen to pin16
GIMSK |= (1<<PCIE); // enable PCINT interrupt in the general interrupt mask
sei(); // Enable all interrupts
while (1) { }
}
SIGNAL (SIG_PCINT)
{
// One of pins 14,15 or 16 just changed states
// do some stuff
}
Resources: avrfreaks.net> and C Programming for Microcontrollers
Comments
Ben Thijssen:
Thanks a lot for your article. This really helps me to understand the datasheets of atmega324p.
Those datasheets are not to difficult if you treat it as a study object and not being to lazy rephrase it in your own words.
Your notes helped me tremendously. Thanks for your good work.
Peter:
Using external interrupts on an Atmel AVR microprocessor is very harmsul and I have never used it
health writer jobs
Anonymous:
PCMSK |= (1<
Gus:
one step closer... =/
pomprocker:
Good read!!
Anonymous:
A modern search engine of the web is available for everybody- if you want to download some music, fims or software just get the link and download from - http://newfileengine.com -everything is simple!
Mike Coles - Bluelip:
http://mihirknows.blogspot.com/2008/02/using-external-interrupts-on-atmel-avr.html
dean:
Good article. Thanks for writing this up. I've been looking for a solution besides polling the pin and decided to use this interrupt. Thanks again!
Anonymous:
As the author said: The PCINT interrupt differs from INT0 and INT1. With INT0 and INT1 you can trigger the interrupt on the falling or rising edge. With PCINT, the interrupt is triggered by any change in the pin state.
How can I use PCINT interrupt with atmega128?
Anonymous:
for anyone trying to follow the avrfreaks link,
it's http://www.avrfreaks.net/ (not .com as the link says).
dunk.
chad:
fixed the link. Thanks.
Post new comment