Which interrupt occurred?

Is there a simple way to find out by software, which interrupt source caused an interrupt during a program loop?

Just curious about this.

Eg:

     void setup() {  
      Serial.begin(115200);
      Serial.println(F("Setup started"));
      TIMSK2 &= ~(1<<TOIE2);  
      TCCR2A &= ~((1<<WGM21) | (1<<WGM20));  
      TCCR2B &= ~(1<<WGM22);  
      ASSR &= ~(1<<AS2);  
      TIMSK2 &= ~(1<<OCIE2A);  
      bitWrite(TCCR2B, CS20, 1); // counting 16 Mhz == no prescaler
      bitWrite(TCCR2B, CS21, 0);
      bitWrite(TCCR2B, CS22, 0);
      TIMSK2 |= (1<<TOIE2); 
      Serial.println(F("timer2 is counting"));
      delay(1000); 
      
byte c1, c2, d;
while (1) {
  c1 = TCNT2;
  c2 = TCNT2;
  d = c2 - c1;
  if ((d > 2) && (d != 27)) break; // 27 is timer2 interrupt below. 98 timer0, ...
}
Serial.print(F("There was an interrupt lasting x cycles, x = "));
Serial.println(d - 2, DEC);
Serial.println(F(" The interrupt was caused by ...?"));
}
void loop(){}

ISR(TIMER2_OVF_vect){ // takes 27-2 cycles
}

It is of course possible to disable specific interrupt sources one at a time and finally find the probable one after several uploads and elimination attempts. But is there a simple way?

E.g. timer0 gave an interrupt lasting about 98 cycles typically.

You've lost me. Why cause interrupts without interrupt handlers? They tell you which interrupt occurred.

http://gammon.com.au/interrupts

But is there a simple way?

Of course there is. Register separate interrupt handlers for each interrupt. Then, the correct ISR will be called when an interrupt, of any type, happens.

[quote author=Nick Gammon link=topic=189184.msg1399547#msg1399547 date=1379748134] You've lost me. Why cause interrupts without interrupt handlers? They tell you which interrupt occurred.

http://gammon.com.au/interrupts [/quote] Sorry being too terse there. Arduino code and libraries have some interrupt handlers in use even in the simple user sketches, e.g. timer0. It is possible to find out them by studying the library source code. But I thought that there might be an easier and 100 % sure way to see when and how long they really execute. One could with the above program find out the length of an interrupt during its execution but not recognize the specific interrupt vector.

Timer2 (empty) interrupt routine was there for measuring purposes: the main loop could measure how many machine cycles the routine took, without looking at the source code and counting instructions and their lengths. A naked interrupt routine could have been there instead, ISR(TIMER2_OVF_vect, ISR_NAKED){}.

I think that idea would fail rather spectacularly. If you were targetting a particular interrupt, a very tight loop of polling the flat which indicates the interrupt has occurred might tell you that it was scheduled but nothing more. To target all the flags (like, all 25 of them on an Atmega328) would take so long the figures would be meaningless.

And in any case that wouldn't tell you when the ISR actually started, and when it actually ended.

I have some figures here: http://www.gammon.com.au/interrupts

I worked out in general how long it takes to enter an ISR, and added caveats that there is an unknown interval on top of that because (perhaps) another ISR is already executing.

If you want to know how long a particular interrupt actually takes (apart from counting clock cycles) you could toggle an LED on at the start of the ISR and off again at the end, then allow for the (couple of) clock cycles that would take, and then add in the instruction cycles you can see have been generated before the compiler hits that toggle instruction.

Let's step back a bit. Why do you want to know this? Apart from the information already in the link I gave above.

[quote author=Nick Gammon link=topic=189184.msg1400298#msg1400298 date=1379799556] I think that idea would fail rather spectacularly. If you were targetting a particular interrupt, a very tight loop of polling the flat which indicates the interrupt has occurred might tell you that it was scheduled but nothing more. To target all the flags (like, all 25 of them on an Atmega328) would take so long the figures would be meaningless.

And in any case that wouldn't tell you when the ISR actually started, and when it actually ended.

I have some figures here: http://www.gammon.com.au/interrupts ... Let's step back a bit. Why do you want to know this? Apart from the information already in the link I gave above. [/quote] Actually I wanted to study whether polling flags or using interrupts in capacitance measuring would give more accurate results. When polling, unknown interrupts might intervene, if allowed.

It is possible to know the start and ending time of an unknown interrupt using timer2 at 16 MHz. It is done in the above working sketch in the instructions

c1 = TCNT2; // the last instruction before a possible interrupt c2 = TCNT2; // the first instruction after the possible interrupt

If an interrupt does not occur during the first line, the difference c2-c1 is 2 cycles of 62.5 ns. If there is an interrupt, the difference is more. E.g. in case of timer0 interrupt, the difference is 100, and thus the duration of that interrupt is 98 cycles at 16 Mhz.

It is possible to check these results by counting the instructions of the interrupt routine as you have done in your excellent pages about interrupts. When we take an assembler list, we find that the optimizer has often more or less substantially speeded up the interrupt code by omitting unnecessary instructions, e.g. saving only registers which need to be saved.

It is possible to know the start and ending time of an unknown interrupt using timer2 at 16 MHz. It is done in the above working sketch in the instructions

c1 = TCNT2; // the last instruction before a possible interrupt c2 = TCNT2; // the first instruction after the possible interrupt

Unless you disable interrupts (not a good idea), how do you limit where interrupts can occur?

PaulS:
Unless you disable interrupts (not a good idea), how do you limit where interrupts can occur?

The example above does not try to catch all interrupts during its while-loop. But if it should do it, I would propose this (the whole sketch is here for easy copy/paste/execute):

    void setup() {  
      Serial.begin(115200);
      Serial.println(F("Setup started"));
      TIMSK2 &= ~(1<<TOIE2);  
      TCCR2A &= ~((1<<WGM21) | (1<<WGM20));  
      TCCR2B &= ~(1<<WGM22);  
      ASSR &= ~(1<<AS2);  
      TIMSK2 &= ~(1<<OCIE2A);  
      bitWrite(TCCR2B, CS20, 1); // counting 16 Mhz == no prescaler
      bitWrite(TCCR2B, CS21, 0);
      bitWrite(TCCR2B, CS22, 0);
      TIMSK2 |= (1<<TOIE2); 
      Serial.println(F("timer2 is counting"));
      delay(1000); // if you remove this, you get a serial comm interrupt below

  
byte c1, c2, d;
noInterrupts();
while (1) {
  interrupts(); // 1 cycle. in effect after next instruction
  c1 = TCNT2;// 2 cycles. this is still executed before any interrupt
                  // an interrupt can occur here
  noInterrupts(); // 1 cycle. this is in effect immediately
  c2 = TCNT2; // 2 cycles
  d = c2 - c1; // d gets normally value 3 (cycles)
  if ((d > 3) && (d != 28)) break; // d ==3, when no interrupt, 
   // and d == 28, when timer2 interrupt (ISR is below)
}
interrupts();
Serial.print(F("There was an interrupt lasting x cycles, x = "));
Serial.println(d - 3, DEC);
Serial.println(F(" The interrupt was caused by ...?"));
}
void loop(){}

ISR(TIMER2_OVF_vect){ // takes 25 = 28 - 3 cycles
}

For me it was instructional to find from the avr datasheet and elsewhere:

  1. interrupts() takes only 1 cycle, and it will be in effect after NEXT machine instruction.

  2. noInterrupts() is in effect immediately even if there would be an interrupt request during its own execution.

  3. when returning from an interrupt one machine instruction is executed even in the case that another interrupt has occurred earlier and is pending.

One could modify the above program to execute in a loop and store all different interrupt execution times into an array. By examining the assembly code we find that almost all the interrupt vector entries are filled with “bad_request” (remarks about interrupt at the right are my additions):

   0:	0c 94 6e 00 	jmp	0xdc	; 0xdc <__ctors_end>      reset
   4:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
   8:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
   c:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  10:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  14:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  18:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  1c:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  20:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  24:	0c 94 99 00 	jmp	0x132	; 0x132 <__vector_9>   timer2 overflow
  28:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  2c:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  30:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  34:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  38:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  3c:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  40:	0c 94 10 01 	jmp	0x220	; 0x220 <__vector_16>  timer0 overlow
  44:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  48:	0c 94 ed 01 	jmp	0x3da	; 0x3da <__vector_18>  usart receive
  4c:	0c 94 3b 02 	jmp	0x476	; 0x476 <__vector_19>  usart udre
  50:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  54:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  58:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  5c:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  60:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>
  64:	0c 94 96 00 	jmp	0x12c	; 0x12c <__bad_interrupt>

I added a loop to record interrupt lengths in machine cycles and their counts during 10 seconds. Here are the results without timer2:

Duration  Count
cycles    pcs
67	1
83	38
98	9532
102	234

98 and 102 cycle interrupts are probably from timer0 overflow, but what would be an easy “automatic” way to find out 83 and 67…
PS. Something strange with timer0? Sum of interrupts should(?) be 10 000 and it is not. 10000-(9532+234) = 234 !

  1. interrupts() takes only 1 cycle, and it will be in effect after NEXT machine instruction.

  2. noInterrupts() is in effect immediately even if there would be an interrupt request during its own execution.

  3. when returning from an interrupt one machine instruction is executed even in the case that another interrupt has occurred earlier and is pending.

Yep. That's in the datasheet and mentioned at some length on my page:

http://www.gammon.com.au/interrupts

In fact (1) and (3) above are manifestations of the same issue. The processor always executes one instruction (before handling any outstanding interrupts) after interrupts are turned on, if they were previously off. Changing the interrupt flag in SREG is another case.