PORTC interrupts and TWI - will they conflict?

Hi, I've been trying to grasp Arduino interrupts by reading the extraordinary good guide on http://gammon.com.au/interrupts as well as trying to understand the Atmel datasheet for the ATmega 328P. Not sure I manged to fully digest every detail though...

I am currently using the twi bus so SDA and SCL lines are connected to pins A4 and A5. Now, I would like to enable a pin change interrupt on pin A2. Since A2 belongs to PORTC I need to enable the PORTC interrupt. I guess it would something like this

ISR (PCINT1_vect) {
 ++my_counter;
}

PCMSK0 |= bit (PCINT10); // want pin A2
PCICR  |= bit (PCIE1);    // enable pin change interrupts for A0 to A

Now my questions; Because A4 and A5 also belongs to PORTC, will any twi bus activity on A4/A5 cause the interupt service intended for A2 to fire as well? Or will I break the twi-bus functionallity when I set PCICR to enable interrupts only on pin A2?

Thanks in advance!

I doubt it, but you could set up a quick test.

If you have enabled interrupts on those pins, it seems it detects them. But otherwise not.

Example test:

// I2C Scanner
// Written by Nick Gammon
// Date: 20th April 2011

volatile bool interrupted;

ISR (PCINT1_vect)
 {
 // handle pin change interrupt for A0 to A5 here
 interrupted = true;
 }  // end of PCINT1_vect


#include <Wire.h>

void setup() {

  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;
  
  Wire.begin();

  // pin change interrupt 
  PCMSK1 =  bit (PCINT10);  // want pin A2
  PCIFR  |= bit (PCIF1);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE1);   // enable pin change interrupts for A0 to A5

  Serial.begin (115200);

  for (byte i = 8; i < 120; i++)
  {
    Wire.beginTransmission (i);
    if (Wire.endTransmission () == 0)
      {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);
      Serial.println (")");
      count++;
      delay (1);  // maybe unneeded?
      } // end of good response
  } // end of for loop
  Serial.println ("Done.");
  Serial.print ("Found ");
  Serial.print (count, DEC);
  Serial.println (" device(s).");
}  // end of setup

void loop() 
  {
  if (interrupted)
    {
    Serial.println ("Interrupted");
    interrupted = false;
    delay (200);
    } 
  }

That exercises the I2C pins but since I only wanted an interrupt on A2, I did not see “Interrupted” printed.

But if you enable interrupts on those pins, you do see it:

  PCMSK1 =  bit (PCINT10) | bit (PCINT12) | bit (PCINT13);  // want pin A2 / A4 / A5

Nick I find your result odd (can't see an error in your code) after all a pin on that port has changed, so the pin change int should fire. And I don't recall seeing any special rule on this in the data sheet.

What happens if you try this with SPI/Serial and pin change on those ports?

Mark

See the datasheet:

13.2.7 PCMSK1 – Pin Change Mask Register 1 ... Each PCINT[14:8]-bit selects whether pin change interrupt is enabled on the corresponding I/O pin.

You can mask out that only certain pins cause a pin-change interrupt. The entire group of pins uses one interrupt vector but they don't all have to cause the interrupt.

What happens if you try this with SPI/Serial and pin change on those ports?

I don’t know. You could try it. :slight_smile:

So basically pin-change interrupts can be as fine-grained as external interrupts. You can choose to have only one pin cause an interrupt. However if the are all from the same "group" of pins only a single interrupt vector is called, and in that case (and that case alone) you need to decide inside the ISR which pin caused the interrupt.

You can mask out that only certain pins cause a pin-change interrupt. The entire group of pins uses one interrupt vector but they don't all have to cause the interrupt.

Nice - and as you have already said very useful

Mark

@snoozerman:

PCMSK0 |= bit (PCINT10); // want pin A2

To be sure, just assign, like this:

PCMSK1 = bit (PCINT10); // only want pin A2

Also, important!, it is PCMSK1 not PCMSK0. Read my page carefully where I list which masks apply to which pins.

D0    PCINT16 (PCMSK2 / PCIF2 / PCIE2)
D1    PCINT17 (PCMSK2 / PCIF2 / PCIE2)
D2    PCINT18 (PCMSK2 / PCIF2 / PCIE2)
D3    PCINT19 (PCMSK2 / PCIF2 / PCIE2)
D4    PCINT20 (PCMSK2 / PCIF2 / PCIE2)
D5    PCINT21 (PCMSK2 / PCIF2 / PCIE2)
D6    PCINT22 (PCMSK2 / PCIF2 / PCIE2)
D7    PCINT23 (PCMSK2 / PCIF2 / PCIE2)
D8    PCINT0  (PCMSK0 / PCIF0 / PCIE0)
D9    PCINT1  (PCMSK0 / PCIF0 / PCIE0)
D10   PCINT2  (PCMSK0 / PCIF0 / PCIE0)
D11   PCINT3  (PCMSK0 / PCIF0 / PCIE0)
D12   PCINT4  (PCMSK0 / PCIF0 / PCIE0)
D13   PCINT5  (PCMSK0 / PCIF0 / PCIE0)
A0    PCINT8  (PCMSK1 / PCIF1 / PCIE1)
A1    PCINT9  (PCMSK1 / PCIF1 / PCIE1)
A2    PCINT10 (PCMSK1 / PCIF1 / PCIE1)
A3    PCINT11 (PCMSK1 / PCIF1 / PCIE1)
A4    PCINT12 (PCMSK1 / PCIF1 / PCIE1)
A5    PCINT13 (PCMSK1 / PCIF1 / PCIE1)

Hi,
Ahh lots of feedback here - thanks a lot! Thanks for the correction to PCMSK1 - that was a copy paste error by me using PCMSK0 ::slight_smile: .
So it appears it is possible to use the I2C and pin change interrupt without any them affecting eachother - that’s great. But maybe it matter what library I use for the I2C bus. That is, if the lib relies on PORTC interupts or not I guess. Currently, I don’t use <Wire.h> for some reason I don’t fully remember anymore.

Good test. I should try something similar with the I2C lib I use.

Thanks!

I doubt another library uses interrupts when the hardware will do it for you. There is level detection going on in I2C which wouldn't be easy without hardware.