Multiple Hardware Interrupts for Arduino UNO

Hi.
I have seen a ton of examples but still in doubt. Does anyone have the time to write a full program (tiny even so) with all the pins D2 to D13 as interrupts? I want to serial.print ANY pin D2..D13 that was changed from LOW to HIGH. I know it use PCINTx stuff but I only see examples with only one interrupt. I want to see and complete example with ALL Digital pins of the UNO. Is it possible? Thank you very much. (NB: All the pins are pinMode(x,INPUT) with no PULLHIGH! Is it possible?)

Every controller port (8 bits) has its own PCINT. In the dedicated ISR you read the current port state, compare it to the previous state, and figure out which bits (pins) have changed.

PCINTerrupts are independent from the pin mode, trigger even for output pins. See the PCINT sections in the data sheets.

What is the code doing that the interrupts will interrupt?
Loop() checking those I/Os runs fast if that is the whole task.

Hi.
Can I see a full example of all Digital pins of the UNO getting Interrupted and Arduino Serial.printing the pin that was changed from LOW to HIGH?
Basically, I want to have 2 Arduinos. One that read every Digital Pin (with ISR) while doing some stuff.
When a pin is detected RISING then he outputs via serial pin 0 and 1 to another Arduino. The second one reads the messages from the first one and execute some I/O routines.

Why making life so difficult on yourself?

  1. why do you think you need interrupts on the first Arduino? If you can't explain why you need interrupts, more than likely you don't.
  2. why doesn't that first Arduino do those routines as well? Again if can't explain why it must be done on a separate controller, you more than likely don't need to do this.

Serial communications is slow. Interrupts are used for very fast signals. That alone doesn't add up, and makes it very much feel like an xy problem.

Hi.
Maybe I don't need interrupts and a DigitalRead on all pins could do the trick but it's not really the point. I want to see a full program using all the Digital pins PCINTx interrupted. All I've seen is snippets that use only one of the Digital pin as interrupt. I havent managed to make such a program and I don't think it's very difficult doing one BUT I can't manage doing so. Grrrrrrrrrrrrr. Can anyone help and do a full Digital interrupt so I can have a look and understand better how it's done. Thank you in advance :slight_smile:

You have "external interrupts" that are linked to pins 2 and 3. Those can trigger on change, rising, falling, and have their own interrupt vector.

All other pins (including the analog ones) have pin change interrupts. Those are on a per-register basis, so all pins in register D trigger the same interrupt vector. They must be enabled on a per pin basis, and will trigger on change.

In general you will want to try to limit pin change interrupts to one per register. In rare cases it may be advantageous to have more than one interrupt in a single register, usually it just adds overhead.

It's indeed not that hard, as soon as you understand how those interrupts work and you enable the ones you want to use (you have to do this explicitly). Post the code you have so far if you want us to have a look at it.

Maybe this'll get you started. Assume 8-bit registers - keep in the back of your mind that not all port registers are eight bits. One register holds the last state of the input (interrupting) register. When a new interrupt invokes the ISR XOR the previous register with the current contents of the input register using a third register to hold the result. Any '1' in this 'scratchpad' register will indicate a bit which changed from the last read. From there you can employ other instructions to determine bit position (value) and combinations of boolean operations to determine whether the bit rose or fell. And from that you can go to selecting, or not, some function to be performed to respond to a particular interrupt.

Hi.
This is my code (example from Nick Gammon site) but it does't work in my Proteus simulation. What is wrong with it? I don't have my Arduino so I can't really test the code in a real Arduino. Can someone help?

/*
 Example copied from Nick Gammon site
 http://www.gammon.com.au/interrupts
 Changes where made to original POST
*/

volatile byte IntCalled=0;

const byte LEDOnOff=A0;

unsigned long time=0;


ISR (PCINT0_vect)
{
 if (bitRead(PORTD,2)) IntCalled=2;
 if (bitRead(PORTD,3)) IntCalled=3;
 if (bitRead(PORTD,4)) IntCalled=4;
 if (bitRead(PORTD,5)) IntCalled=5;
 if (bitRead(PORTD,6)) IntCalled=6;
 if (bitRead(PORTD,7)) IntCalled=7;
}

ISR (PCINT2_vect)
{
 if (bitRead(PORTB,0)) IntCalled=8;
 if (bitRead(PORTB,1)) IntCalled=9;
 if (bitRead(PORTB,2)) IntCalled=10;
 if (bitRead(PORTB,3)) IntCalled=11;
 if (bitRead(PORTB,4)) IntCalled=12;
 if (bitRead(PORTB,5)) IntCalled=13;
}


void setup () 
{
 pinMode (LEDOnOff, OUTPUT);
 digitalWrite (LEDOnOff, LOW);  	// Led Blink Off

 PCMSK2 |= bit (PCINT16); // Pin D0
 PCMSK2 |= bit (PCINT17); // Pin D1
 PCMSK2 |= bit (PCINT18); // Pin D2
 PCMSK2 |= bit (PCINT19); // Pin D3
 PCMSK2 |= bit (PCINT20); // Pin D4
 PCMSK2 |= bit (PCINT21); // Pin D5
 PCMSK2 |= bit (PCINT22); // Pin D6
 PCMSK2 |= bit (PCINT23); // Pin D7
 PCIFR  |= bit (PCIF2);   // clear any outstanding interrupts
 PCICR  |= bit (PCIE2);   // enable pin change interrupts for D0 to D7

 PCMSK0 |= bit (PCINT0); // Pin D8
 PCMSK0 |= bit (PCINT1); // Pin D9
 PCMSK0 |= bit (PCINT2); // Pin D10
 PCMSK0 |= bit (PCINT3); // Pin D11
 PCMSK0 |= bit (PCINT4); // Pin D12
 PCMSK0 |= bit (PCINT5); // Pin D13
 PCIFR  |= bit (PCIF0);   // clear any outstanding interrupts
 PCICR  |= bit (PCIE0);   // enable pin change interrupts for D8 to D13
}

void loop () 
{
 if (IntCalled!=0)
 {
  time=millis()+1000;
  Serial.print("Interrupt called ");
  Serial.println(IntCalled);
  digitalWrite (LEDOnOff, HIGH);
 }
 if (time==millis()) digitalWrite(LEDOnOff, LOW);
}

What does it do and how is it different from what you expect it to do?

Hi.
It should Serial print "Interrupt called x". "x" should be the pin that triggered the interrupt. It does nothing in Proteus. I press a push button (+5v to pin 3 for example) and I should see a serial message "Interrupt called 3" for example but nothing is received so... I think somethin is wrong with the code or... something is wrong with the Proteus simulation. Any one to test the code with a real Arduino and a switch?

Probably both.

Your code should print hundreds of lines of "Interrupt called x" where x is the latest interrupt to be called, until the next interrupt is set. This as you don't set IntCalled to 0 in that if block.

if (time==millis()) digitalWrite(LEDOnOff, LOW);

You should say >= millis() here so it still works if you happen to miss that one millisecond.

Another thing: a pin change interrupt is called upon CHANGE. You should therefore check for which pin changed, rather than which pin is HIGH. This as you may have multiple pins HIGH, and the one that changed triggering the interrupt may have actually gone LOW.

Hi.
Good points! I wasn't worried about those but thanks for the find. the question still remains... why doesn't this code works? What's wrong with it? Any ideas?

Slow down and learn the basics.

Too many bad habits

Try this example.

//
//LarryD
//Port_Interrupts.ino
//Switches are wired from the INPUT pin to GND
//Inputs have pullups turned on.
//

//when set there was an interrupt on D2 through D13
volatile byte Flag;

boolean timingFlag = false;

//LED toggels for 1 second on an interrupt
const byte LEDOnOff = A0;

//for timing
unsigned long timeMillis;

//****************************************************************************
void setup ()
{
  Serial.begin(9600);

  pinMode (LEDOnOff, OUTPUT);
  digitalWrite (LEDOnOff, LOW);

  for (int x = 2; x <= 13; x++)   //switches wired from input to GND
  {
    pinMode(x, INPUT_PULLUP);
  }

  //D00 and D01 used for Serial communications
  //PCMSK2 |= bit (PCINT16); // Pin D0
  //PCMSK2 |= bit (PCINT17); // Pin D1
  PCMSK2 |= bit (PCINT18); // Pin D02
  PCMSK2 |= bit (PCINT19); // Pin D03
  PCMSK2 |= bit (PCINT20); // Pin D04
  PCMSK2 |= bit (PCINT21); // Pin D05
  PCMSK2 |= bit (PCINT22); // Pin D06
  PCMSK2 |= bit (PCINT23); // Pin D07
  PCIFR  |= bit (PCIF2);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE2);   // enable pin change interrupts for D00 to D07

  PCMSK0 |= bit (PCINT0);  // Pin D08
  PCMSK0 |= bit (PCINT1);  // Pin D09
  PCMSK0 |= bit (PCINT2);  // Pin D10
  PCMSK0 |= bit (PCINT3);  // Pin D11
  PCMSK0 |= bit (PCINT4);  // Pin D12
  PCMSK0 |= bit (PCINT5);  // Pin D13
  PCIFR  |= bit (PCIF0);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE0);   // enable pin change interrupts for D08 to D13
}

//****************************************************************************
void loop ()
{
  //have we had an interrupt?
  if (Flag == 1)
  {
    Flag = 0;

    //check for the bit that caused the interrupt
    byte interrupt = checkBit();

    if (interrupt != 0)
    {
      Serial.print("Interrupt called = ");
      Serial.println(interrupt);

      //enable timing
      timingFlag = true;
      //time the LED turned ON
      timeMillis = millis();
      
      //turn the LED ON
      digitalWrite (LEDOnOff, HIGH);
    }
  }

  //are we timing and has the interval finished?
  if (timingFlag == true && millis() - timeMillis >= 1000 )
  {
    //turn the LED OFF
    digitalWrite(LEDOnOff, LOW);

    //disable timing
    timingFlag = false;
  }
  
} //END of loop()


//****************************************************************************
byte checkBit()
{
  //check PIND D02-D07
  for (byte x = 2; x <= 7; x++)
  {
    //check for LOW pin
    byte value = PIND;
    if (bitRead(value, x) == 0)
    {
      return x;
    }
  }

  //check PINB D08-D13
  for (byte x = 0; x <= 5; x++)
  {
    //check for LOW pin
    byte value = PINB;
    if (bitRead(value, x) == 0)
    {
      //offset by 8 bits
      return x + 8;
    }
  }
  
  //must have gone HIGH
  return 0;
}

//****************************************************************************
ISR (PCINT2_vect) //D02-07
{
  //get out quick
  Flag = 1;
}

//****************************************************************************
ISR (PCINT0_vect) //D08-13
{
  //get out quick
  Flag = 1;
}

There's a very strong smell of x-y about this thread.

Using PORTD and PORTB instead of PIND and PINB is definitely: X versus Y

Hi.
I used LarryD code and done some alterations to the switch so he can behave as LOW when pushed and NOTHING! The code doesn't work. Can anyone try the code in a real Arduino because with Proteus 7.9 I'm getting nowhere. Thank you for the support so far :wink:

I just tried the code I offered.

This was put on an UNO.

The messages monitored with IDE serial monitor, works 100%.

It must be your simulator. :frowning:

Edit BTW:

  • In your sketch, you are using PORTD and PORTB when you should have used PIND and PINB
  • Also you had no Serial.begin(9600); in your setup.
  • Your if (time==millis()) digitalWrite(LEDOnOff, LOW); as wrong and so is the subsequent
    if (time>=millis()) digitalWrite(LEDOnOff, LOW);

Hi.
Thanks LarryD. So, it seems that the Proteus 7 simulator is not working properly with this piece of code (maybe my code was +-good also). At least we reach somewhere. Thank for your help ALL of you and a special thank to LarryD. I consider this post closed :slight_smile:

Personally, I don't trust simulators, especially going as far as simulating the operation of a microcontroller.
Those things are so intricate, with so many options, that it's hard to completely simulate them. You also don't get to see real life effects such as bouncing contacts and various analog effects that mess up your digital signals.