9

Am I correct in thinking that if you have two pins causing the same AVR PCINT interrupt, (e.g. PCINT0 vector caused by either PCINT0 or PCINT1 pins -- I think the naming overlap of vectors and pins is confusing) the only way to determine which pin(s) caused the interrupt is to record their state after each interrupt and compare the previous and current values of all the pins which are enabled in PCMSKn?

Tom Davies
  • 306
  • 1
  • 2
  • 10
  • 1
    It's been a while since I've used an AVR, but I'm sure there has to be a flag that get triggered for the right pin. This flag should clear after the interrupt occurs, so that you don't need to store the state. The fact that the flag is set should be enough – Gustavo Litovsky Dec 02 '12 at 06:26
  • @gl3829 the flags are per group of pins if I am understanding correctly – Tom Davies Dec 02 '12 at 20:18

2 Answers2

12

I think the naming overlap of vectors and pins is confusing

It is!

The reason there are 8 different external pins for an interrupt vector is to make it easier to layout the PCB or to use a different pin if there is a conflict with another pin function.

Am I correct in thinking ... the only way to determine which pin(s) caused the interrupt is to record their state after each interrupt and compare the previous and current values of all the pins which are enabled in PCMSKn?

Pretty much, lets say you only care about PB0 (PCINT0) and PB1 (PCINT1). So the pin change enable mask PCMSK0 would be set to 0x03.

// External Interrupt Setup
...

volatile u_int8 previousPins = 0; 
volatile u_int8 pins = 0; 

ISR(SIG_PIN_CHANGE0)
{
    previousPins = pins; // Save the previous state so you can tell what changed
    pins = (PINB & 0x03); // set pins to the value of PB0 and PB1
    ...
}

So if pins is 0x01 you know it was PB0... And if you need to know what changed you need to compare it to previousPins, pretty much exactly what you thought.

Keep in mind in some cases, pins may not be accurate if the pin has changes state since the interrupt but before pins = (PINB & 0x03).

Another option would be to use separate interrupt vectors with one pin from each vector so you know which one is changed. Again, this also has some issues, like interrupt priority and once the CPU enters the ISR, the global interrupt enable bit I-bit in SREG will be cleared so that all other interrupts are disabled, although you can set it inside the interrupt if you want, that would be a nested interrupt.

For more information, take a look at Atmel's app note Using External Interrupts for megaAVR Devices.

Update

Here is a complete code example I just found here.

#include <avr/io.h>
#include <stdint.h>            // has to be added to use uint8_t
#include <avr/interrupt.h>    // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF;     // default is high because the pull-up

int main(void)
{
    DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
    // PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs

    PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
    // PB0, PB1 and PB2 are now inputs with pull-up enabled

    PCICR |= (1 << PCIE0);     // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);   // set PCINT0 to trigger an interrupt on state change 

    sei();                     // turn on interrupts

    while(1)
    {
    /*main program loop here */
    }
}

ISR (PCINT0_vect)
{
    uint8_t changedbits;

    changedbits = PINB ^ portbhistory;
    portbhistory = PINB;

    if(changedbits & (1 << PB0))
    {
    /* PCINT0 changed */
    }

    if(changedbits & (1 << PB1))
    {
    /* PCINT1 changed */
    }

    if(changedbits & (1 << PB2))
    {
    /* PCINT2 changed */
    }
}
Garrett Fogerlie
  • 4,958
  • 1
  • 22
  • 47
  • The mega has three pin change interrupts, with vectors PCINT[0-2], but each of those is triggered by a set of pins. My question is about how to distinguish which of the pins in that set caused the interrupt. – Tom Davies Dec 02 '12 at 07:40
  • @TomDavies you are correct, thanks, I changed my answer however it is exactly what you thought. And I read through the datasheet, there is no flag to indicate what pin changed. – Garrett Fogerlie Dec 02 '12 at 20:54
  • @ Garret: Did you recognize that in your original example one can easily determine whether it was the falling or the rising edge that triggered the interrupt? (well, unless both pins changed at the exact same moment ... but in this case only black magic helps) (previous_pins > pins): falling edge (previous pins < pins): rising edge Maybe this is worth being mentioned above. –  May 15 '13 at 01:49
  • @TomDavies PINB covers PCINT0-7, PINC covers PCINT8-15 etc – EkriirkE Jan 15 '15 at 01:53
1

On the newer ATTINY series INTFLAGS register will tell you what port bit has caused the interrupt.

Here is excerpt from datasheet:

Bits 7:0 – INT[7:0]: Interrupt Pin Flag The INT Flag is set when a pin change/state matches the pin's input sense configuration. Writing a '1' to a flag's bit location will clear the flag.

zmechanic
  • 294
  • 1
  • 10