PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Mega) pins

I already thought of a bug in v. 2.13beta. If you detach an interrupt and attach it again, and give it a new user function, it will not attach the new user function- it will use the first user function that you attached.

However you are able to change the mode at this point (RISING, FALLING, or CHANGE).

Version 2.15beta is out now, and I allow for attaching a new user function. I don't know if anyone would really do that, but there should be no nasty surprises in the code, and attachInterrupt() will thus do exactly what you tell it to do in the argument list.

URL: Google Code Archive - Long-term storage for Google Code Project Hosting.

  1. goto loop: ? why not use break?

addpcint

	if (firstPin != NULL) {
		tmp=firstPin;
		if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); }
		while (tmp->next != NULL) {
			tmp=tmp->next;
			if (tmp->arduinoPin == arduinoPin)  { enable(tmp, userFunc, mode); return(0); }
		};
	}

shorter == smaller footprint? faster?

	if (firstPin != NULL) {
		tmp=firstPin;
                do {
        		if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0);
                        tmp=tmp->next;
                } while (tmp != NULL);
	}

Thanks for the input. This saved 12 bytes; I haven't measured the speed:

    if (firstPin != NULL) {
        tmp=firstPin;
        do {
            if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); }
            if (tmp->next == NULL) break;
            tmp=tmp->next;
        } while (true);
    }

I have also converted the goto to a do/while/break situation. No change to interrupt speed, but I guess it looks better :-).

                } while (tmp != NULL);

This won't work because tmp will become null, which will exit us from the loop.

I need tmp->next later in the code, therefore I need the last non-null value of tmp.

oops,
I figured that out too and you replied while I removed my previous post,

Version 2.19beta has been released. The library now includes Sanguino support. There have been a number of recent bugfixes, so be sure to look at the Release Notes. If you have any library prior to version 2.17beta, it is highly recommended that you upgrade to 2.17beta or later.

hey,

I like to use the PINCHANGE Library with the Arduino DUE.
But I still get this failure:

In file included from FirstTask.ino:24:
.....\Arduino\libraries\PinChangeInt/PinChangeInt.h:103: fatal error: new.h: No such file or directory
compilation terminated.

Maybe some know this failure?

I need to use the pinchange lib because I need to know of many pins, which pin has changed without using for every pin a own function like this:

     attachInterrupt(1, action1, HIGH);
attachInterrupt(2, action2, HIGH);

Maybe someone knows also an alternative solution :slight_smile:

(very little Due experience)
you could try to uncomment it , and see if it is needed for the DUE
The DUe is an ARM processor while the lib was originally written for the AVR.

just a thought!

MorgKrig:
hey,

I like to use the PINCHANGE Library with the Arduino DUE.
But I still get this failure:

In file included from FirstTask.ino:24:

.....\Arduino\libraries\PinChangeInt/PinChangeInt.h:103: fatal error: new.h: No such file or directory
compilation terminated.



Maybe some know this failure?

I need to use the pinchange lib because I need to know of many pins, which pin has changed without using for every pin a own function like this:


attachInterrupt(1, action1, HIGH);
attachInterrupt(2, action2, HIGH);




Maybe someone knows also an alternative solution :-)

Not sure why you would need that.
The attach interrupt doc at http://arduino.cc/en/Reference/AttachInterrupt clearly states:

The Arduino Due board has powerful interrupt capabilities that allows you to attach an interrupt function on all available pins. You can directly specify the pin number in attachInterrupt().

The overhead of creating a function for each pin is probably nothing compared to linking and porting the pinchangeint lib.
Not that I would care about the size of the program or data when writing code for the due.
Best regards
Jantje

After some time, I have updated the library. There is a significant fix submitted by jrhelbert which makes the two Port J pins work properly with the Mega. See Google Code Archive - Long-term storage for Google Code Project Hosting..

Downloads available at Service End for Bintray, JCenter, GoCenter, and ChartCenter | JFrog

Bleeding edge source available at GitHub - GreyGnome/PinChangeInt: Pin Change Interrupt library for the Arduino

Note: I don't have a Mega, so I didn't test ports J and K. If you use it, please let me know if it works. Thanks.

Maybe somebody can help me?
I have this code, it is used to read a rotary encoder with an Arduino Uno. I want to make this work on an Arduino Mega.

  PORTC |= _BV(PORTC0) | _BV(PORTC1) | _BV(PORTC2) | _BV(PORTC3);   // enable pullup for pins
  PCMSK1 = _BV(PCINT8) | _BV(PCINT9) | _BV(PCINT10) | _BV(PCINT11); // enable button pin change interrupt A0 A1 A2 A3
  PCICR = _BV(PCIE1);

I can not find any help about converting this to a working Mega sketch. Can you help me?

First you have to study the Mega pinout and figure out which pins and ports are available to you. Have you done this?

It looks like you have two rotary encoders (4 pins total). Is that correct?

I tried to use this lib, but sadly doesn't work as expected, so moved to http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html . It works fine and I got all Analog pins on my Mega 2560 working as interrupt. but there I got other problem.... I lost Serial1, Serial2, Serial3.
None other than Serial0 Serial3 works with PCINT1 to PCINT23, with the ATMEL datasheet for 2560 it has stated that Serial3 (ATMEL's RXD3/TXD3) are on PCINT9 and PCINT10 , so there are chances that Serial3 wont work with manipulated PCICR setting. Yet we have 2 other Serial Ports (Serial1 and Serial2) which haven't specified on any PCINT pins.
Please guide me about this.
This is where I Initialise Interrupts

[code]void InitialiseInterrupt(){
  cli();		// switch interrupts off while messing with their settings  

  PCICR =0x06;          // Enable PCIE2 and PCIE1
  PCMSK1 = 0b11111111; // Enabling A0 to A7 pins on Mega 2560 (PCINT23:16)
  PCMSK2 = 0b11111111; // Enabling A8 to A15 pins on Mega 2560(PCINT15:8)
  sei();		// turn interrupts back on
}

also as per the ATMEL's pdf you dont have any PCINT on ADC0...ADC7 pins. yet those are working

the full code is

#include <TimerOne.h>
#include <Encoder.h>
#include <SPI.h>

Encoder xenc(18,19);
int iPin[] = {54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69}; //{"A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15"};
int iLast[16];
void setup()
{
  SPI.begin();
  Serial.begin(9600);
  Serial.println("Boe");
  Serial2.begin(9600);
  InitialiseIO();
  InitialiseInterrupt();
Timer1.initialize(100000);
Timer1.attachInterrupt(Timer1_tick);
}
void Timer1_tick(){
  float z = xenc.read();
digitalWrite(13,!digitalRead(13));
SPI.transfer(z);
}
void serialEvent(){
Serial2.print("A");
}
void loop() {
  /* Nothing to do: the program jumps automatically to Interrupt Service Routine "blink"
   in case of a hardware interrupt  */
}  

void InitialiseIO(){
  pinMode(13,OUTPUT);
  digitalWrite(13,LOW);
  pinMode(A0, INPUT);	   // Pin A0 is input to which a switch is connected
  digitalWrite(A0, HIGH);   // Configure internal pull-up resistor
  pinMode(A1, INPUT);	   // Pin A1 is input to which a switch is connected
  digitalWrite(A1, HIGH);   // Configure internal pull-up resistor
  pinMode(A2, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A2, HIGH);   // Configure internal pull-up resistor
   pinMode(A3, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A3, HIGH);   // Configure internal pull-up resistor
   pinMode(A4, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A4, HIGH);   // Configure internal pull-up resistor
   pinMode(A5, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A5, HIGH);   // Configure internal pull-up resistor
   pinMode(A6, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A6, HIGH);   // Configure internal pull-up resistor
   pinMode(A7, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A7, HIGH);   // Configure internal pull-up resistor
  pinMode(A8, INPUT);	   // Pin A0 is input to which a switch is connected
  digitalWrite(A8, HIGH);   // Configure internal pull-up resistor
  pinMode(A9, INPUT);	   // Pin A1 is input to which a switch is connected
  digitalWrite(A9, HIGH);   // Configure internal pull-up resistor
  pinMode(A10, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A10, HIGH);   // Configure internal pull-up resistor
   pinMode(A11, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A11, HIGH);   // Configure internal pull-up resistor
   pinMode(A12, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A12, HIGH);   // Configure internal pull-up resistor
   pinMode(A13, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A13, HIGH);   // Configure internal pull-up resistor
   pinMode(A14, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A14, HIGH);   // Configure internal pull-up resistor
   pinMode(A15, INPUT);	   // Pin A2 is input to which a switch is connected
  digitalWrite(A15, HIGH);   // Configure internal pull-up resistor
  
}

void InitialiseInterrupt(){
  cli();		// switch interrupts off while messing with their settings  

  PCICR =0x06;          // Enable PCINT1 interrupt
  PCMSK1 = 0b11111111;
  PCMSK2 = 0b11111111;
  sei();		// turn interrupts back on
}
/*
ISR(PCINT1_vect) {    // Interrupt service routine. Every single PCINT8..14 (=ADC0..5) change
            // will generate an interrupt: but this will always be the same interrupt routine
  if (digitalRead(A0)==0)  Serial.println("A0");
  if (digitalRead(A1)==0)  Serial.println("A1");
  if (digitalRead(A2)==0)  Serial.println("A2");
   if (digitalRead(A3)==0)  Serial.println("A3");
  if (digitalRead(A4)==0)  Serial.println("A4");
  if (digitalRead(A5)==0)  Serial.println("A5");
   if (digitalRead(A6)==0)  Serial.println("A6");
  if (digitalRead(A7)==0)  Serial.println("A7");

}
ISR(PCINT2_vect) {    // Interrupt service routine. Every single PCINT8..14 (=ADC0..5) change
            // will generate an interrupt: but this will always be the same interrupt routine
  if (digitalRead(A8)==0)  Serial.println("A8");
  if (digitalRead(A9)==0)  Serial.println("A9");
  if (digitalRead(A10)==0)  Serial.println("A10");
   if (digitalRead(A11)==0)  Serial.println("A11");
  if (digitalRead(A12)==0)  Serial.println("A12");
  if (digitalRead(A13)==0)  Serial.println("A13");
   if (digitalRead(A14)==0)  Serial.println("A14");
  if (digitalRead(A15)==0)  Serial.println("A15");

}*/
ISR(PCINT1_vect) {
  for (int q = 0; q <= 7; q++) {
    int u = digitalRead(iPin[q]);
    String OP = "IP";

    if (iLast[q] != u) {
      OP += q;
      OP += u;
      OP += "!";
      Serial.print(OP);
    }
    iLast[q] = u;
  }

}
ISR(PCINT2_vect) {
  for (int q = 8; q <= 15; q++) {
    int u = digitalRead(iPin[q]);
    String OP = "IP";

    if (iLast[q] != u) {
      OP += q;
      OP += u;
      OP += "!";
      Serial.print(OP);
    }
    iLast[q] = u;
  }

}

[/code]

Don't do a Serial.print() inside an ISR. If it does work, consider yourself lucky and stop doing it as soon as possible because things are (were?) problematic when printing from an ISR (but supposedly fixed in IDE 1.5; see Fix lockup when writing to HardwareSerial with interrupts disabled · matthijskooijman/Arduino@eb86af7 · GitHub ).

Regarding the rest of your question, you stated that you tried the PinChangeInt library and it didn't work for you. This thread is about the PinChangeInt library. The code you speak of was written by someone else so I don't think this is the appropriate place for this question, but perhaps someone will lend you the time and help you out.

Announcing the release of version 2.40-rc2 for the Arduino PinChangeInt library.

See Google Code Archive - Long-term storage for Google Code Project Hosting..
Downloads available at Service End for Bintray, JCenter, GoCenter, and ChartCenter | JFrog
Bleeding edge source available at GitHub - GreyGnome/PinChangeInt: Pin Change Interrupt library for the Arduino

See the RELEASE_NOTES for detailed information. In short, some of the changes in recent history include:

The library is now licensed under the Apache 2.0 License. I wanted to free the code up for any use, and Chris J. Kiick and Lex Talionis graciously agreed.

Cleaned up some code, and added the following #define's for ease-of-use:

#define        detachPinChangeInterrupt(pin)
#define        attachPinChangeInterrupt(pin,userFunc,mode)
#define        getInterruptedPin()

Cleaned up the README for better display on GitHub.

Version 2.40-rc1 Fri Nov 7 07:26:36 CST 2014
I'm going to the "-rcX" numbering format like the Linux kernel. That is, 2.40-rc1 is the first "release
candidate" on the way to a stable 2.40 release.

Beginning with this release, only Arduino >= 1.00 is supported. The older Arduinos are left to the
dustbin of history...

I have BIG news: Jan Baeyens ("jantje") has graciously DONATED an Arduino Mega ADK to the PinChangeInt project!!! Wow, thanks Jan! This makes the 2560-based Arduino Mega a first class supported platform- I will be able to test it and verify that it works. I have done so in this release.

To that end, The PinChangeIntExample has been modified to work properly with the Arduino Mega. Thanks, Jan!

Many thanks to JRHelbert for fixing the PJ0 and PJ1 interrupt PCMSK1 issue on the Arduino Mega! This was a great coup, in my humble opinion. Mega interrupt users are indebted to you, jrhelbert.

Great Library,
Any chance to add support in it for the Atmel 1284p micro controller?

I have a board with a 1284p (moteino mega) that I can test it with.

Thanks

tomega3:
Great Library,
Any chance to add support in it for the Atmel 1284p micro controller?

I have a board with a 1284p (moteino mega) that I can test it with.

Thanks

It would probably be pretty easy, since the library already has Sanguino support. Could you take the following code and add/modify the 1284 to the #define and see if it works just like that?

#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)

just change it to

#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284__) || (defined __AVR_ATmega1284P__)

(that final "P" may need to be a "p". Could you check both?)

Thanks.

Hello devs,

I'm having a curious interrupt-dropping problem using PinChangeInt 2.40-rc2 on Arduino 1.0.5.

I'm using it like many people to capture R/C servo pulsewidths. In a previous version of my board code which was written last year with Arduino 1.0.1 (and whichever version of PinChangeInt was back then), I was capturing 3 (some times 4) channels of servo inputs without issue.

Right now, I'm facing a weird issue where it seems to be dropping ISR functions or otherwise failing to execute them. The code and loop structure that reads the R/C servo pulsewidths is basically identical - it's mostly copy and paste from the v1 board with variable names changed.

Here's the board and schematic. The pins called AIN0:2 are the inputs, and there is nothing else connected to those pins.

Here's the code segment that I isolated into its own sketch to try and determine if other timers, ISRs, etc. were causing issues. "Combine" is a pin I'm using temporarily as an output to scope the ISR timing.

//#define NO_PORTB_PINCHANGES
//#define NO_PORTD_PINCHANGES
//#define NO_PIN_STATE      
//#define NO_PIN_NUMBER
#include <PinChangeInt.h>

#define COMBINE 5

#define RCIN0 16 //Analog pin 2 is also called Digital pin 16
#define RCIN1 17 // ... 
#define RCIN2 18 // ...

#define SLOW_LOOP_PRESCALER 10000

volatile unsigned long slow_loop_prescale_counter = 0;


//RC channel collection variables
volatile unsigned long temp_ch1_pw, temp_ch2_pw, temp_ch3_pw; //Used for counting the duration of the pulse width
volatile signed int vol_ch1_pw, vol_ch2_pw, vol_ch3_pw; //Used to communicate the pulse width to the main loop
signed int ch1_pw, ch2_pw, ch3_pw; //A buffered copy of the volatile variable to be used during the loop

//RISING EDGE. Captures a system clock reading when channel goes high
void rch1() {
  temp_ch1_pw = micros();
}

//FALLING EDGE. Takes another reading when channel goes low, finds delta time
void fch1() {
  vol_ch1_pw = (signed int)(micros() - temp_ch1_pw);
}

void rch2() {
  PORTD |= (1 << 5);
  temp_ch2_pw = micros();
  PORTD &= ~(1 << 5); 
}

void fch2() {
  PORTD |= (1 << 5);
  vol_ch2_pw = (signed int)(micros() - temp_ch2_pw);
  PORTD &= ~(1 << 5); 
}

void rch3() {
  temp_ch3_pw = micros();
}

void fch3() {
  vol_ch3_pw = (signed int)(micros() - temp_ch3_pw);
}

//Other R/C-only functions
//boolean check_rc_pulsewidth(signed int pulsewidth) {
//  
//  return (pulsewidth > RC_ABS_MIN && pulsewidth < RC_ABS_MAX);
//}

void setup() {
  
  Serial.begin(57600);
  
  //Inputs, analog
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);
  pinMode(A4, INPUT);
  pinMode(A5, INPUT);
  
  pinMode(COMBINE, OUTPUT);

  PCintPort::attachInterrupt(RCIN0,rch1,RISING); //CH1 (steer): rising edge
  PCintPort::attachInterrupt(RCIN0,fch1,FALLING); //CH1  falling edge
  PCintPort::attachInterrupt(RCIN1,rch2,RISING); //CH2 (go): rising edge
  PCintPort::attachInterrupt(RCIN1,fch2,FALLING); //CH2: falling edge
  PCintPort::attachInterrupt(RCIN2,rch3,RISING); //CH3 (invert): rising edge
  PCintPort::attachInterrupt(RCIN2,fch3,FALLING); //CH3: falling edge
  
}

void loop() {
  
  if(micros() - slow_loop_prescale_counter > SLOW_LOOP_PRESCALER) {
    slow_loop_prescale_counter = micros();
    
//    noInterrupts();
    ch1_pw = vol_ch1_pw;
    ch2_pw = vol_ch2_pw;
//    interrupts();
//    
    Serial.print(ch1_pw);
    Serial.print("\t");
    Serial.print(ch2_pw);
    Serial.print("\n");
  }
}

Here's the result I'm getting. It's erratic for the most part, and contains positives and negatives indicating what I think is the unsigned long intermediate variable overflowing the signed int variable repeatedly. A good signal in this case would be approximately 1000 to 2000 (microseconds).

What is curious to me was that the scope trace some times showed two pulses - one at the rising edge ISR and one at the falling edge ISR. The temporal spacing changes as I modulate the input pulse signal, which is a good sign...

However, very frequently, the second pulse is nonexistent. I'm certain it's not an artifact of the oscilloscope, because on a "real" Tek scope I have, it's clear that there are several two-pulse trains and several one-pulse erroneous ones.

For sanity and a control test, I popped the code into a fresh Arduino Nano on a breadboard, by itself, and got the exact same erratic numbers and missing interrupts.

Any insight you have would be appreciated. I'm confident it's one of two things - either I got something very simple during the porting from version 1 board to version 2 board completely wrong (the more likely scenario, as I'm not an embedded programmer by hobby or trade...) or something change between early-mid 2013 PinChangeInt & Arduino and early 2015 PinChangeInt & Arduino that is causing issues.

Hope this is enough info. Thanks!