I had a hard time figuring out how pin-change interrupts work. They are rather simple, but difficult to generalize. I got them working for some sonar sensors on an Arduino Mega board. Thought I'd post here to help others.
To summarize, You only need to do three simple things to set up pin-change interrupts:
PCICR |= 1; // enable PCI0 pin group interrupt
PCMSK0 |= 0b1100; // enable PC interrupts on arduino pins 50,51, which are in group PCI0
...
/// Pin-Change interrupt, for group PCI0 (arduino mega pins 13..10, and 50..53)
ISR(PCINT0_vect) {
// do what you need to do when one of the pins in this group changes.
// usually... check to see which pin changed here... then do something.
}
There are three interrupt "groups". I can post tables for which pins are in which group later, if desired.
The three low bits of PCICR control which of these groups have pin-change interrupts enabled.
Each of these groups has a mask, for which pins in this group will trigger the interrupt.
PCMSK0, PCMSK1, PCMSK2.
Each group gets one interrupt vector... so when the interrupt is triggered, your
code must figure out which pin changed, and take appropriate action.
The interrupt vector for each PCI group is:ISR(PCINT0_vect), ISR(PCINT1_vect), and ISR(PCINT2_vect) respectively.
Here is some sample code, which used pin-change interrupts to record the timing for a set of sonar sensors.
We need to note the time of the falling edge of the monitored pins.
// settings for mega testing an HC-SFR0 sonar
//
// when user sends a character over serial, report range timings
//
// This version tries to use Pin-Change interrupts to get sonar timings
#define PIN_TRIGGER 31
#define PIN_ECHO0 50
#define PIN_ECHO1 51
#define PIN_ECHO2 52
#define PIN_ECHO3 53
#define NUM_SONAR_SENSORS 2 // assume starting pins 50-53, then 13..10
#define TIMEOUT_US 30000
//----------------------------------------------- SonarState
const char TAB = 't';
class SonarState {
public:
byte pinEcho[NUM_SONAR_SENSORS], nDone;
bool done;
int dt[NUM_SONAR_SENSORS];
unsigned long ping, start;
long nPulses;
void begin()
{
start = ping = 0;
dt[0]=dt[1]=0;
pinEcho[0]=PIN_ECHO0;
pinEcho[1]=PIN_ECHO1;
nPulses = 0;
nDone = 0;
done = false;
pinMode(PIN_TRIGGER,OUTPUT);
pinMode(PIN_ECHO1,INPUT);
pinMode(PIN_ECHO0,INPUT);
digitalWrite(PIN_TRIGGER,LOW);
PCMSK0 |= 0b1100; // enable PC interrupts on arduino pins 50,51
}
void sendPing()
{
digitalWrite(PIN_TRIGGER,HIGH);
start = micros();
nPulses++;
dt[0] = dt[1] = 0;
nDone = 0;
done = false;
delayMicroseconds(2); // whole pulse must be at least 10us, but delay can be less because of overhead and above code
digitalWrite(PIN_TRIGGER,LOW);
ping = micros();
// consider a delay here, to allow echo pins to go high?
PCICR |= 1; // enable PCI0 pin group interrupt
}
// too much time passed. reset even if not done, and set timeout vals
void timeout(int longTime)
{
PCICR &= 0xfe; // disable PCI0 pin group interrupt
done = true;
nDone = NUM_SONAR_SENSORS;
for (int i=0; i < NUM_SONAR_SENSORS; i++)
if (dt[i] == 0) dt[i] = longTime;
}
/// blocks until reading is complete
void getReadingBLOCK()
{
sendPing();
int et;
do {
et = (int)(micros() - ping);
} while ((!done) && (et < TIMEOUT_US));
if (!done) timeout(et);
}
// convert current dt[idx] to cm
int cm(byte idx)
{
int t = dt[idx];
if ((t<=0)||(t>=TIMEOUT_US)) return(9999);
return( (int)((t-464)/58) );
}
/// diagnostic method to do a sonar reading, and print results
void printReading()
{
getReadingBLOCK();
Serial.print(nPulses); Serial.print(") ");
Serial.print(start); Serial.print(TAB);
Serial.print(ping-start);
for (int i=0; i < NUM_SONAR_SENSORS; i++)
{
Serial.print(TAB); Serial.print(dt[i]);
Serial.print("(");Serial.print(cm(i));Serial.print(")");
}
Serial.println();
}
} Sonar;
//------------------------------------------ setup/loop
void setup()
{
cli(); // disable interrupts
Sonar.begin();
sei(); // enable interrupts?
Serial.begin(9600);
}
/// Pin-Change interrupt, for arduino pins 50..53, and 13..10
ISR(PCINT0_vect) {
unsigned long t = micros();
int dt = t - Sonar.ping;
// It has been measured as taking 464us from falling edge of trigger
// to rising edge of echo duration pulse
if (dt < 470) return; // still waiting for leading edge
// check which pin fell here, and note time
Sonar.nDone = 0;
for (int i=0; i < NUM_SONAR_SENSORS; i++)
{
if (Sonar.dt[i] == 0)
{
if (digitalRead(Sonar.pinEcho[i]) == LOW)
{
Sonar.dt[i] = dt;
Sonar.nDone++;
}
}
else Sonar.nDone++;
}
if (Sonar.nDone == NUM_SONAR_SENSORS)
{
Sonar.done = true;
PCICR &= 0xfe; // disable PCI0 pin group interrupt
}
}
void loop()
{
if (Serial.read() < 0) return; // no update command
Sonar.printReading();
}