attachInterrupt() calls wrong function upon interrupt on Arduino Mega2650

Hi All,
I have been having some issues with the attachInterrupt() function and have discovered a curiosity in the way that the compiler creates the interrupt table.
First off here are my calls to attachInterrupt():

//setup interrupts
attachInterrupt(3, INT1_ISR, FALLING);  
attachInterrupt(4, INT2_ISR, FALLING);

Here are my two ISRs:

void INT1_ISR()
{
  static unsigned long last_interrupt_time=0;
  unsigned long interrupt_time = millis();
  if((interrupt_time-last_interrupt_time)>500)
  {
    
    /* Interupt Code Goes Here */ 
    if(initFlag)
    {
      cli();
      stepperMotor.setCurrentPosition(-10*STEPS_PER_INCH);
      initFlag=false;
    }
    else
    {
      printStatus("Hard Stop 1", 0);
      stepperMotor.setCurrentPosition(0);
    }
  }
  last_interrupt_time = interrupt_time;
}

void INT2_ISR()
{
  static unsigned long last_interrupt_time=0;
  unsigned long interrupt_time = millis();
  if((interrupt_time-last_interrupt_time)>500)
  {
    /* Interupt Code Goes Here */ 
      printStatus("Hard Stop 2", 0);
      stepperMotor.setCurrentPosition(0);
  }
  last_interrupt_time = interrupt_time;
}

The issue that I have been having is that if either INT3 or INT4 triggers it will call INT1_ISR. If I switch the order of the interrupts(switch 3 and 4 in the function calls) then it will always call INT2_ISR. I checked on my board and there is no cross talk between the pins on the arduino. I checked to make sure that I didn't have any typos and for all the usual mistakes that can lead to this kind of issue. The next thing I did is disassemble the .elf
I traced through the execution and it looks like it is following proper function call protocols (as one would assume from a compiler) from the setup where the attachInterrupt() function is called all the way through the completion of the ISR. I was stumped for a couple of weeks but I noticed something odd about the interrupt table today.
Here is the interrupt table:

00000000 <__vectors>:
       0:	04 c1       	rjmp	.+520    	; 0x20a <__dtors_end>
       2:	00 00       	nop
       4:	0c 94 45 0c 	jmp	0x188a	; 0x188a <__vector_1>
       8:	0c 94 76 0c 	jmp	0x18ec	; 0x18ec <__vector_2>
       c:	0c 94 a7 0c 	jmp	0x194e	; 0x194e <__vector_3>
      10:	0c 94 d8 0c 	jmp	0x19b0	; 0x19b0 <__vector_4>
      14:	0c 94 09 0d 	jmp	0x1a12	; 0x1a12 <__vector_5>
      18:	0c 94 3a 0d 	jmp	0x1a74	; 0x1a74 <__vector_6>
      1c:	0c 94 6b 0d 	jmp	0x1ad6	; 0x1ad6 <__vector_7>
      20:	0c 94 9c 0d 	jmp	0x1b38	; 0x1b38 <__vector_8>
      24:	1f c1       	rjmp	.+574    	; 0x264 <__bad_interrupt>
      26:	00 00       	nop
      28:	1d c1       	rjmp	.+570    	; 0x264 <__bad_interrupt>
      2a:	00 00       	nop
      2c:	1b c1       	rjmp	.+566    	; 0x264 <__bad_interrupt>
      2e:	00 00       	nop
      30:	19 c1       	rjmp	.+562    	; 0x264 <__bad_interrupt>
      32:	00 00       	nop
      34:	17 c1       	rjmp	.+558    	; 0x264 <__bad_interrupt>
      36:	00 00       	nop
      38:	15 c1       	rjmp	.+554    	; 0x264 <__bad_interrupt>
      3a:	00 00       	nop
      3c:	13 c1       	rjmp	.+550    	; 0x264 <__bad_interrupt>
      3e:	00 00       	nop
      40:	11 c1       	rjmp	.+546    	; 0x264 <__bad_interrupt>
      42:	00 00       	nop
      44:	0f c1       	rjmp	.+542    	; 0x264 <__bad_interrupt>
      46:	00 00       	nop
      48:	0d c1       	rjmp	.+538    	; 0x264 <__bad_interrupt>
      4a:	00 00       	nop
      4c:	0b c1       	rjmp	.+534    	; 0x264 <__bad_interrupt>
      4e:	00 00       	nop
      50:	09 c1       	rjmp	.+530    	; 0x264 <__bad_interrupt>
      52:	00 00       	nop
      54:	07 c1       	rjmp	.+526    	; 0x264 <__bad_interrupt>
      56:	00 00       	nop
      58:	05 c1       	rjmp	.+522    	; 0x264 <__bad_interrupt>
      5a:	00 00       	nop
      5c:	0c 94 cd 0d 	jmp	0x1b9a	; 0x1b9a <__vector_23>
      60:	01 c1       	rjmp	.+514    	; 0x264 <__bad_interrupt>
      62:	00 00       	nop
      64:	0c 94 f4 0f 	jmp	0x1fe8	; 0x1fe8 <__vector_25>
      68:	0c 94 38 11 	jmp	0x2270	; 0x2270 <__vector_26>
      6c:	fb c0       	rjmp	.+502    	; 0x264 <__bad_interrupt>
      6e:	00 00       	nop
      70:	f9 c0       	rjmp	.+498    	; 0x264 <__bad_interrupt>
      72:	00 00       	nop
      74:	f7 c0       	rjmp	.+494    	; 0x264 <__bad_interrupt>
      76:	00 00       	nop
      78:	f5 c0       	rjmp	.+490    	; 0x264 <__bad_interrupt>
      7a:	00 00       	nop
      7c:	f3 c0       	rjmp	.+486    	; 0x264 <__bad_interrupt>
      7e:	00 00       	nop
      80:	f1 c0       	rjmp	.+482    	; 0x264 <__bad_interrupt>
      82:	00 00       	nop
      84:	ef c0       	rjmp	.+478    	; 0x264 <__bad_interrupt>
      86:	00 00       	nop
      88:	ed c0       	rjmp	.+474    	; 0x264 <__bad_interrupt>
      8a:	00 00       	nop
      8c:	eb c0       	rjmp	.+470    	; 0x264 <__bad_interrupt>
      8e:	00 00       	nop
      90:	0c 94 34 10 	jmp	0x2068	; 0x2068 <__vector_36>
      94:	0c 94 77 11 	jmp	0x22ee	; 0x22ee <__vector_37>
      98:	e5 c0       	rjmp	.+458    	; 0x264 <__bad_interrupt>
      9a:	00 00       	nop
      9c:	e3 c0       	rjmp	.+454    	; 0x264 <__bad_interrupt>
      9e:	00 00       	nop
      a0:	e1 c0       	rjmp	.+450    	; 0x264 <__bad_interrupt>
      a2:	00 00       	nop
      a4:	df c0       	rjmp	.+446    	; 0x264 <__bad_interrupt>
      a6:	00 00       	nop
      a8:	dd c0       	rjmp	.+442    	; 0x264 <__bad_interrupt>
      aa:	00 00       	nop
      ac:	db c0       	rjmp	.+438    	; 0x264 <__bad_interrupt>
      ae:	00 00       	nop
      b0:	d9 c0       	rjmp	.+434    	; 0x264 <__bad_interrupt>
      b2:	00 00       	nop
      b4:	d7 c0       	rjmp	.+430    	; 0x264 <__bad_interrupt>
      b6:	00 00       	nop
      b8:	d5 c0       	rjmp	.+426    	; 0x264 <__bad_interrupt>
      ba:	00 00       	nop
      bc:	d3 c0       	rjmp	.+422    	; 0x264 <__bad_interrupt>
      be:	00 00       	nop
      c0:	d1 c0       	rjmp	.+418    	; 0x264 <__bad_interrupt>
      c2:	00 00       	nop
      c4:	cf c0       	rjmp	.+414    	; 0x264 <__bad_interrupt>
      c6:	00 00       	nop
      c8:	cd c0       	rjmp	.+410    	; 0x264 <__bad_interrupt>
      ca:	00 00       	nop
      cc:	0c 94 74 10 	jmp	0x20e8	; 0x20e8 <__vector_51>
      d0:	0c 94 b6 11 	jmp	0x236c	; 0x236c <__vector_52>
      d4:	c7 c0       	rjmp	.+398    	; 0x264 <__bad_interrupt>
      d6:	00 00       	nop
      d8:	0c 94 b4 10 	jmp	0x2168	; 0x2168 <__vector_54>
      dc:	0c 94 f5 11 	jmp	0x23ea	; 0x23ea <__vector_55>
      e0:	c1 c0       	rjmp	.+386    	; 0x264 <__bad_interrupt>

What is weird is that the addresses for the interrupts on this table do not conform to the addresses listed on the interrupt table on page 105 of the ATMega2560 Datasheet.
Here is a link to the datasheet for those who don't have it on hand: http://www.atmel.com/images/doc2549.pdf

In the datasheet each vector takes two addresses in memory. For example INT3's location in the table in the datasheet is 0x08 and INT4's is 0x0A. However in the disassembled arduino code INT3's location is 0x0c and INT4's is 0x10. This change is giving me conflicting information as to what is going on.
First off, the jmp command, which is what is being used in both interrupt tables requires 4 bytes. However the same command is referenced as being used in the vector table in the datasheet and the addresses are completely different. I am wondering whether this difference is what is causing the error. The thing that has me thinking this is the cause is I have never seen an architecture which varies the size of the commands in the vector table. I know that RJMP only uses 1 word for the instruction and thus would fit better in the table in the datasheet but that is not what is used by the compiler nor is it the command mentioned in the datasheet. I don't understand how the processor would be able to discern which interrupt vector address to execute upon interrupts based upon the size of the command. What I am wondering if it is happening is if the interrupt vector is perhaps pointing to the wrong place and sliding down and executing the same interrupt both times.
Anyways I would be curious to hear thoughts about this problem. I am fairly experienced with both Arduino, AVR, micro-controller architectures and Assembly but it is completely possible I am off my rocker for this one.

Thanks,
Julian

I am fairly experienced with both Arduino, AVR, micro-controller architectures and Assembly but

you can't figure out how to use a space bar?

What does printStatus() do?

Why are you diddling with interrupts in INT1_ISR?

Why are those two interrupt service routines given such lousy names?

Which pins are your interrupting signals coming in on?

I have to agree with PaulS that identifying your interrupt handlers with numbers which are different to the number of the interrupt they handle does not help understand what they are for.

The interrupts are on pins 19 and 20. I was refering to them by the external int number.
http://arduino.cc/en/Reference/AttachInterrupt

Printstatus is a wrapper for the Serial.println function in order to give meaningful output.

I disabled the interrupts at the suggestion of a co-worker. I realize now that it is pointless but it doesn't change the result of the program execution as I added these in as a way to attempt to solve the bounce after the initial problem started.

They have lousy names because I am used to basing the names off which interrupt is running, not what the interrupt does. Different style of doing things but useful in some situations. And I apologize for the bad style. Arduino does not bring out the best in coding style from me.

Printstatus is a wrapper for the Serial.println function in order to give meaningful output.

Serial.print() or Serial.println() have no place in an ISR. They require interrupts being enable to shift data out. If the outgoing buffer gets full when interrupts are disabled, as they are in in ISR, there will never be more space available, since data is not being shuffled out, so the Arduino will hang.

I agree with you that Serial.printLn has no place in an interrupt. However in this particular case it was not printing enough to fill the buffer (64 bytes) and the interrupt is hardly ever triggered (once every hour or so). It was added in order to help with debugging. The same problem exists regardless of whether there is a print statement or not in the interrupt.

and the interrupt is hardly ever triggered (once every hour or so).

Then, why do you need interrupts? Interrupts are for things that have to be noticed RIGHT NOW! Once an hour is not something that needs to be dealt with immediately.

What board are you compiling for? Because when I compile your code I get the following IVT:

00000000 <__vectors>:
   0:	71 c0       	rjmp	.+226    	; 0xe4 <__ctors_end>
   2:	00 00       	nop
   4:	57 c1       	rjmp	.+686    	; 0x2b4 <__vector_1>
   6:	00 00       	nop
   8:	86 c1       	rjmp	.+780    	; 0x316 <__vector_2>
   a:	00 00       	nop
   c:	b5 c1       	rjmp	.+874    	; 0x378 <__vector_3>
   e:	00 00       	nop
  10:	e4 c1       	rjmp	.+968    	; 0x3da <__vector_4>
  12:	00 00       	nop
  14:	13 c2       	rjmp	.+1062   	; 0x43c <__vector_5>
  16:	00 00       	nop
  18:	42 c2       	rjmp	.+1156   	; 0x49e <__vector_6>
  1a:	00 00       	nop
  1c:	71 c2       	rjmp	.+1250   	; 0x500 <__vector_7>
  1e:	00 00       	nop
  20:	a0 c2       	rjmp	.+1344   	; 0x562 <__vector_8>
  22:	00 00       	nop
  24:	7f c0       	rjmp	.+254    	; 0x124 <__bad_interrupt>
  ...

I just tested those interrupts on my Mega, and they work fine. There was one surprise I had not noticed before. The two interrupt functions are called once by the "attachInterrupt()" function before any interrupts occur.

edit: What type of circuit are you using to trigger the interrupts? I'm using D25 (output) wired to D21 and D26 (output) wired to D20.

I compiled it for the atmega2560. Its strange that it would work fine for you SurferTim and not me. Thanks for the info though. I'll have to go back and see whether I can figure out what the deal is with my setup. Could you tell me which IDE version you are running? I tried it with multiple arduino megas and had the same result.
Thanks for the help.

PaulS -
Just because a hardware interrupt only triggers once every hour doesn't mean that when it does it doesn't need to be serviced immediately. For instance this one services limit switches on a gantry. If the stepper hits the limit, it needs to stop RIGHT NOW.

juliandeep:
it needs to stop RIGHT NOW.

How many nanoseconds does your gantry take to stop?

I did not use your code. I used mine. D25 controls INT2 (D21) and D26 controls INT3 (D20). The prestart is the variables before the attachInterrupt() calls and start is after the calls. The serial display is a bit confusing at first, but it is how my brain works. :~

int twoCount = 0;
int threeCount = 0;

void setup() {
  Serial.begin(9600);
  
  pinMode(25,OUTPUT);
  digitalWrite(25,HIGH);
  
  pinMode(26,OUTPUT);
  digitalWrite(26,HIGH);

  Serial.print("prestart two = ");
  Serial.print(twoCount);
  Serial.print("  prestart three = ");
  Serial.println(threeCount);
  
  attachInterrupt(2,interrupt2test,FALLING);
  attachInterrupt(3,interrupt3test,FALLING);

  Serial.print("start two = ");
  Serial.print(twoCount);
  Serial.print("  start three = ");
  Serial.println(threeCount);
}

void loop() {

  delay(2000);

  digitalWrite(25,LOW);
  digitalWrite(25,HIGH);
  Serial.print("two = ");
  Serial.print(twoCount);
  Serial.print("  three = ");
  Serial.println(threeCount);

  delay(2000);
  
  digitalWrite(26,LOW);
  digitalWrite(26,HIGH);
  Serial.print("three = ");
  Serial.print(threeCount);
  Serial.print("  two = ");
  Serial.println(twoCount);
}

void interrupt2test() {
  twoCount++;
}

void interrupt3test() {
  threeCount++;
}

Peter H - It needs to stop quickly enough such that polling it is not a good option.

I even added some of your interrupt code to mine to see if that affected it, but it didn't. Mine still works fine. ??

void interrupt2test() {
  twoCount++;

  static unsigned long last_interrupt_time=0;
  unsigned long interrupt_time = millis();
  if((interrupt_time-last_interrupt_time)>500)
  {
      Serial.println("Hard Stop 1");
  }
  last_interrupt_time = interrupt_time;
}

void interrupt3test() {
  threeCount++;
  static unsigned long last_interrupt_time=0;
  unsigned long interrupt_time = millis();
  if((interrupt_time-last_interrupt_time)>500)
  {
      Serial.println("Hard Stop 2");
  }
  last_interrupt_time = interrupt_time;
}

edit: I am compiling with the board set to "Arduino Mega 2560 or Mega ADK".

SurferTim - Thanks for trying to help. Unfortunately I can't post the whole code because it is for a company project and I can only post the isolated snippets that I did. My guess is it has something to do with either a namespace conflict, the dev enviornment, or the libraries that I am using. I was just posting here to see if I could figure out why the vector table was behaving as it did. Thanks again.

How big is your program? I know it is a Mega 2560, but they will still run out of SRAM eventually. You haven't run out of SRAM, have you? That causes some really weird stuff to happen.

I wanted to point out in case anyone is curious that I am using this library for stepper control:
http://www.airspayce.com/mikem/arduino/AccelStepper/
It is a large code base. Could it possibly be pushing the interrupt routines far enough down that the vector table switches to jmp from rjmp?

Julian

I appreciate that this is a diversion from your question, although sometimes avoiding a problem is as good as solving it.

juliandeep:
Peter H - It needs to stop quickly enough such that polling it is not a good option.

You can probably guess I'm skeptical about that, and it seems to me that this is an unfounded assumption on your part. To verify that assumption you'd need to know how quickly your sketch needs to react, which is why I asked the question.

Given that it is a moving mass subject to mechanical inertia I suspect you'll find that its response time is glacially slow relative to the Arduino's processor speed and that you can easily respond quickly enough without using interrupts.

Peter H- You do bring up a valid point. I don't know exactly how long it takes but polling also doesn't work well with our existing software architecture. That being said we do have a workaround for this problem using a single interrupt. The purpose of this post was to see if I could figure out what caused it in the first place and prevent myself from having this error again.

Just as a note the overall code size is not too large for the sram. I don't remember the exact number but it was something I checked.

Thanks

juliandeep:
The issue that I have been having is that if either INT3 or INT4 triggers it will call INT1_ISR. If I switch the order of the interrupts(switch 3 and 4 in the function calls) then it will always call INT2_ISR. I checked on my board and there is no cross talk between the pins on the arduino.

http://www.gammon.com.au/forum/?id=11488&reply=8#reply8

Does what you observe agree with this? ...

The developers of the Mega2560 decided to try to confuse us. I guess they succeeded. :wink: