My ISR is not getting called

I'm using a Mega 2560 and trying to attach interrupts on six digital pins. Also, my code is utilizing the PcInt code snippet: Arduino Playground - PcInt

The problem is that the ISR doesn't seem to change the volatile int variable's value as it's supposed to do. I've added a Serial.println() to the ISR but this doesn't generate an output either.

What's wrong with my code?

I've marked the line where my code begins with the comment MY CODE STARTS HERE:

#include <String.h>
#include "pins_arduino.h"
/*
 * an extension to the interrupt support for arduino.
 * add pin change interrupts to the external interrupts, giving a way
 * for users to have interrupts drive off of any pin.
 * Refer to avr-gcc header files, arduino source and atmega datasheet.
 */

/*
 * Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
 * The PCINT corresponding to the pin must be enabled and masked, and
 * an ISR routine provided.  Since PCINTs are per port, not per pin, the ISR
 * must use some logic to actually implement a per-pin interrupt service.
 */

/* Pin to interrupt map:
 * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
 * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
 * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
 */
volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,
  &PCMSK1,
  &PCMSK2
};

static int PCintMode[24];

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = { 
  NULL };

volatile static uint8_t PCintLast[3];

/*
 * attach an interrupt to a specific pin using pin change interrupts.
 */
 void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

// -- Fix by Baziki. In the original sources it was a little bug, which cause analog ports to work incorrectly.
  if (port == 1) {
     slot = port * 8 + (pin - 14);
  }
  else {
     slot = port * 8 + (pin % 8);
  }
// --Fix end
  PCintMode[slot] = mode;
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      // Trigger interrupt if mode is CHANGE, or if mode is RISING and
      // the bit is currently high, or if mode is FALLING and bit is low.
      if ((PCintMode[pin] == CHANGE
          || ((PCintMode[pin] == RISING) && (curr & bit))
          || ((PCintMode[pin] == FALLING) && !(curr & bit)))
          && (PCintFunc[pin] != NULL)) {
        PCintFunc[pin]();
      }
    }
  }
}


SIGNAL(PCINT0_vect) {
  PCint(0);
}
SIGNAL(PCINT1_vect) {
  PCint(1);
}
SIGNAL(PCINT2_vect) {
  PCint(2);
}




/*
 * MY CODE STARTS HERE
 */


const int VAL_A = 120;
const int VAL_B = 60;
const int VAL_C = 30;
const int VAL_D = 25;
const int VAL_E = 10;
const int VAL_F = 5;

volatile long myValue = 0;



    
void valADetected(void) {
    Serial.println("ISR!");
  myValue = myValue + VAL_A;
}

void valBDetected(void) {
    Serial.println("ISR!");
  myValue = myValue + VAL_B;
}

void valCDetected(void) {
    Serial.println("Coin!");
  myValue = myValue + VAL_C;
}

void valDDetected(void) {
    Serial.println("ISR!");
  myValue = myValue + VAL_D;
}

void valEDetected(void) {
    Serial.println("ISR!");
  myValue = myValue + VAL_E;
}

void valFDetected(void) {
    Serial.println("ISR!");
  myValue = myValue + VAL_F;
}




void setup()
{
  Serial.begin(9600);
  
  pinMode(22, INPUT);
  pinMode(24, INPUT);
  pinMode(26, INPUT);
  pinMode(42, INPUT);
  pinMode(43, INPUT);
  pinMode(47, INPUT);
  delay(3000);
  
  PCattachInterrupt(47, valADetected, FALLING);
  PCattachInterrupt(43, valBDetected, FALLING);
  PCattachInterrupt(42, valCDetected, FALLING);
  PCattachInterrupt(26, valDDetected, FALLING);
  PCattachInterrupt(24, valEDetected, FALLING);
  PCattachInterrupt(22, valFDetected, FALLING);
}

void loop()
{
    Serial.println(myValue, DEC);
    Serial.println();
    
    delay(1000);
}
 * Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.

But, you don't have a ATMega168 (or 328 which has the same pins).

What's wrong with my code?

Possibly nothing. You haven't described what is connected to the pins that is supposed to generate the interrupt, so maybe that's where the problem is.

I've added a Serial.println() to the ISR but this doesn't generate an output either.

Serial output is interrupt driven. Interrupts are disabled during an ISR. So, no output is not surprising.

I have an external device that is most of the time pulled up (active high). On certain events, this device pulls its output pin down to ground (250 ms). I've tested it with a multimeter, it's doing its job.

So what alternative do I have for working with interrupts on the Mega if I want to use these pins? Basically to my knowledge, all pins on the ATmega2560 are capable for external interrupts.

Dizplai:
Basically to my knowledge, all pins on the ATmega2560 are capable for external interrupts.

Not true. Also, the pin -> port mapping is quite different on the 328 based Arduinos (e.g., Uno) and the 1280/2560 based Megas.

Copy the image below and study it. You will see that for D0-D13 on the Mega, only D0,10-D13 support pcints, for example.

Also, you will want to bone up on the distinction between an external int and a pcint for avr uCs.

None of this is conceptually hard, but you do need to be on top of it if you expect anything to work. Certainly not a cut and paste job.

I also recommend downloading the datasheet for the 2560 from Atmel.

Two comments:

  1. make sure global interrupts is enabled;
  2. your code is way to complicated. You can greatly simplify it by reading the datasheet.

dhenry:

  1. make sure global interrupts is enabled;

Global interrupts are enabled by default.

I suggest you look here:

http://playground.arduino.cc/Main/PinChangeInt

That claims to support the Mega. Just use the library, don't copy and paste the library inside your own code.

The Mega supports a number (8 by the looks) of external interrupts (mapped to specific pins). To use those you don't need the pin change interrupt library. Not all remaining pins are supported for pin change interrupts.