Bare Metal Micro

AVR Digital I/O

7. External Interrupts

Like pin change interrupts, external interrupts allow you to be notified every time a specific input pin changes state.

There are a few notable differences:

Figure 1 shows the names and locations of the two external interrupt pins.

Figure 1. ATmega328P PDIP External Interrupt Pins

To use an external interrupt, first configure how the interrupt will be triggered using the External Interrupt Control Register. Next you will need to enable the external interrupt by setting the appropriate bit in the External Interrupt Mask Register. The following example shows triggering the external interrupt on the falling edge of INT0.

1
2
3
4
5
6
7
// Configure external interrupt 0 to generate an interrupt request on
// the falling edge of INT0 using External Interrupt Control Register A (EICRA)
EICRA |= _BV(ISC01);

// Enable external interrupt 0 using the External Interrupt Mask Register
// (EIMSK)
EIMSK |= _BV(INT0);

A full working example of using the external interrupt is listed below. This example configures PD2 as an input with a pull-up resistor and enables INT0 to trigger whenever any change occurs. If the pin is high, PB5 outputs a low value, which will turn the LED off. If the pin is low, PB5 outputs a high value, which will turn the LED on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <avr/io.h>
#include <avr/interrupt.h>

ISR(INT0_vect)
{
    // Read PD2 using the Port D Pin Input Register (PIND)
    if (PIND & _BV(PIND2))
    {
        // PD2 is high, so button is released

        // Set PB5 low using the Port B Data Register (PORTB)
        PORTB &= ~_BV(PORTB5);
    }
    else
    {
        // PD2 is low, so button is pressed

        // Set PB5 high using the Port B Data Register (PORTB)
        PORTB |= _BV(PORTB5);
    }
}

int main(void)
{

    // Configure PD2 as an input using the Data Direction Register D (DDRD)
    DDRD &= ~_BV(DDD2);

    // Enable the pull-up resistor on PD2 using the Port D
    // Data Register (PORTD)
    PORTD |= _BV(PORTD2);

    // Configure external interrupt 0 to generate an interrupt request on any
    // logical change using External Interrupt Control Register A (EICRA)
    EICRA |= _BV(ISC00);

    // Enable external interrupt 0 using the External Interrupt Mask Register
    // (EIMSK)
    EIMSK |= _BV(INT0);

    // Configure PB5 as an output using the Port B Data Direction Register
    // (DDRB)
    DDRB |= _BV(DDB5);

    // Enable interrupts
    sei();

    // Loop forever
    while (1)
    {
        // Nothing to do here
        // All work is done in the ISR
    }
}

The circuit for this example should be wired according to Figure 1. If you run this example on an Arduino Uno, you will see the LED that is connected to Arduino pin 13 turning ON when the push button is pressed and turning OFF when the push button is released.

Figure 1. Input example wiring

Make sure to orient the push button correctly. If it is rotated the wrong way, the LED will always be on.