There are eleven external interrupts for the ATmega2560 chip, but the Arduino only allows you to use nine of them and only six of them, INT5:0, can be accessed through attachInterrupt(). These three extra mystery interrupts are the Pin Change Interrupts, referred to as PCI2:0. These three interrupts are set whenever the pins they monitor are toggled, which would be the same function as the CHANGE setting for the six other external interrupts. Each of the PCI interrupts have eight pins they are attached to, PCINT23:0.
The external interrupts and the pins they are wired to:
PCI2: Arduino:
PCINT23 PIN ANALOG15
PCINT22 PIN ANALOG14
PCINT21 PIN ANALOG13
PCINT20 PIN ANALOG12
PCINT19 PIN ANALOG11
PCINT18 PIN ANALOG10
PCINT17 PIN ANALOG9
PCINT16 PIN ANALOG8
PCI1:
PCINT15 N/A
PCINT14 N/A
PCINT13 N/A
PCINT12 N/A
PCINT11 N/A
PCINT10 PIN 14
PCINT9 PIN 15
PCINT8 PIN 0
PCI0:
PCINT7 PIN 13
PCINT6 PIN 12
PCINT5 PIN 11
PCINT4 PIN 10
PCINT3 PIN 50
PCINT2 PIN 51
PCINT1 PIN 52
PCINT0 PIN 53
INT7:0
INT7 N/A
INT6 N/A
INT5 PIN 3
INT4 PIN 2
INT3 PIN 18
INT2 PIN 19
INT1 PIN 20
INT0 PIN 21
You may notice that the pins that INT5:0 are attached to are not the same used for attachInterrupt(). This is import to note if you have multiple interrupts since certain interrupts will take priority over one another and can interrupt lower priority interrupts. The lower the interrupt vector, the higher the priority of the interrupt, as seen on the table below.
Found on page 105 of ATmega2560 data sheet
Let's take our button program from last post and replace the attachInterrupt() by manually setting the external interrupt control registers.
The trigger mode from attachInterrupt() is instead controlled by the EICRA and EICRB registers. Each INT has two corresponding bits in the registers used to set the mode. You can reference the table below on how to set either LOW, CHANGE, FALLING, or RISING.
Found on page 114 of ATmega2560 data sheet
To enable any of the INT5:0 interrupts, edit the External Interrupt Mask Register, EIMSK. The INT7:0 enable bits correspond to the eight bits of the register. To enable INT4 for instance, you would set EIMSK = 0x10.
Circuit diagram, again, use your imagination
#include <avr/interrupt.h>
#include <avr/io.h>
volatile int state = 0;
void setup(){
pinMode(53, OUTPUT); //attached to LED annode
pinMode(2, INPUT); //Button input tied to INT4
EICRB = 0x01; //INT4, triggered on any edge
EIMSK = 0x10; //Enable only INT4
}
void loop(){
}
ISR(INT4_vect) {
state = !state;
digitalWrite(53, state);
}
Note that Pin 2 used in the last sketch is now attached to INT4 when we manually set the registers. If we were to use INT0 like last time, we would need to connect our button to Pin 21 instead. The "real" INT0 is attached to Pin 21 and Pin 2 is attached to the "real" INT4. Refer to the previous table at the beginning for reference.
The code for the interrupt vector is just the source of the vector with _vect added to the end of it. For the button code, INT4 is used so the interrupt routine is IRS(INT4_vect). If you look at the interrupt vector table above you can figure out the vectors for any other interrupt. Before, when we used the Timer2 Overflow Interrupt if you look at the table you can see it uses the source TIMER2 OVF. So, the interrupt routine would be ISR(TIMER2_OVF_vect).
Also, you don't have to manually clear the interrupt flag bits at the end of the interrupt service routine as I previously thought. The ATmega automatically clears the flag once the service routine starts.
Thanks, this cleared up the strange interrupt linking I was confused about.
ReplyDeleteany ideas about the arduino due interrupt priorities?
ReplyDeleteI have an Elegoo 2560 and can only get pins 2 and 3 to work on CHANGE....the remaining pins called out as external inputs do not work on CHANGE...any ideas why?
ReplyDelete