27

I have an Arduino Uno (awesome little device!). It has two interrupts; let's call them 0 and 1. I attach a handler to interrupt 0 and a different one to interrupt 1, using attachInterrupt() : http://www.arduino.cc/en/Reference/AttachInterrupt.

Interrupt 0 is triggered and it calls its handler, which does some number crunching. If interrupt 0's handler is still executing when interrupt 1 is triggered, what will happen?

Will interrupt 1 interrupt interrupt 0, or will interrupt 1 wait until interrupt 0's handler is done executing?

Please note that this question specifically relates to Arduino.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Laplante
  • 29,338
  • 17
  • 103
  • 134

3 Answers3

28

On Arduino (aka AVR) hardware, nested interrupts don't happen unless you intentionally create the conditions to allow it to happen.

From avr-lib:

The AVR hardware clears the global interrupt flag in SREG before entering an interrupt vector. Thus, normally interrupts will remain disabled inside the handler until the handler exits, where the RETI instruction (that is emitted by the compiler as part of the normal function epilogue for an interrupt handler) will eventually re-enable further interrupts. For that reason, interrupt handlers normally do not nest. For most interrupt handlers, this is the desired behaviour, for some it is even required in order to prevent infinitely recursive interrupts (like UART interrupts, or level-triggered external interrupts). In rare circumstances though it might be desired to re-enable the global interrupt flag as early as possible in the interrupt handler, in order to not defer any other interrupt more than absolutely needed. This could be done using an sei() instruction right at the beginning of the interrupt handler, but this still leaves few instructions inside the compiler-generated function prologue to run with global interrupts disabled.

(source: http://linux.die.net/man/3/avr_interrupts )

gpcz
  • 806
  • 6
  • 8
  • 1
    Is that text really correct? I don't know AVR instruction set, but shouldn't it be a "cli" instruction? Aka "clear the global interrupt mask", not "sei" aka "set the global interrupt mask". That's how it works on Freescale MCUs anyway, they are using those two asm instructions as well. – Lundin Feb 25 '11 at 10:42
  • 2
    SEI = Set Global Interrupt Flag. This will globally enable interrupts. (source (PDF warning): http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf page 132) – gpcz Feb 25 '11 at 11:04
  • 6
    Ok. How wonderfully moronic of Freescale and Atmel to both use the very same instruction names, but with inverted meanings :) – Lundin Feb 25 '11 at 14:06
  • 4
    Note that this is a similar situation to every CPU I've dealt with - interrupts are disabled on entry to an ISR. The software must explicitly re-enable them if it wants to permit nested IRQ handling, and the software needs to properly prepare hardware/stacks/whatever for nested IRQs to work before re-enabling interrupts. – Michael Burr Feb 26 '11 at 23:38
  • 2
    @Lundin You're complaining about **THAT** about Freescale documentation? When the **FAR** more **heinous** problem of Bit Order is in question? Not **ONLY** do Freescale use **SMALLER** numbers for the **MOST SIGNIFICANT BITS**, they **START** at **ONE!!!!!** So my (sensible) `31`-`0` bitmap is documented by Freescale as **`1`-`32`** Sorry, but `CLI` is the **LEAST** of Freescale's problems... – John Burger Jul 07 '16 at 14:12
  • 1
    @JohnBurger This was not a documentation issue, but a CPU core design issue. IIRC all Freescale register documentation for Power PC parts enumerate the bits backwards like you say. All Power PC documentation, not just from Freescale, is written in equally stupid ways. Most other Freescale part manuals enumerate bits properly, MSB -> LSB. So I think you are maybe barking up the wrong tree... though of course Freescale was one of the major culprits behind the design of Power PC to begin with. – Lundin Jul 07 '16 at 14:26
  • @Lundin Actually, when they give a mnemonic a name that _is_ a documentation issue. I'm not saying that they're not allowed to buck a trend, but bucking an entire industry? I hoped that you'd take the emphases (it's a word! Look it up!) in the above as hyperbole... – John Burger Jul 07 '16 at 14:39
  • [User *kelin* claimed your answer was out of date](https://stackoverflow.com/revisions/35172143/1). Can you address that in your answer? That is, ***not*** by adding an update section, but changing the answer (the history information should only be in the revision history, not in the current answer. The explanation for the change can be put in the revision summary and/or comments here). If you need to acknowledge user *kelin*'s answer, do it in comments (and/or the edit summary), not in the answer. – Peter Mortensen May 26 '19 at 12:41
8

Will interrupt 1 interrupt interrupt 0, or will interrupt 1 wait until interrupt 0's handler is done executing?

Unless you specifically re-enable interrupts inside an ISR (Interrupt Service Routine) then whatever interrupt is currently running completes, plus one more machine code instruction, before the next interrupt is serviced.

Most interrupts set a flag inside the processor, which is checked between instructions, to see if the interrupt should be serviced. Flags are checked in priority order. On the Uno that is:

 1  Reset 
 2  External Interrupt Request 0  (pin D2)          (INT0_vect)
 3  External Interrupt Request 1  (pin D3)          (INT1_vect)
 4  Pin Change Interrupt Request 0 (pins D8 to D13) (PCINT0_vect)
 5  Pin Change Interrupt Request 1 (pins A0 to A5)  (PCINT1_vect)
 6  Pin Change Interrupt Request 2 (pins D0 to D7)  (PCINT2_vect)
 7  Watchdog Time-out Interrupt                     (WDT_vect)
 8  Timer/Counter2 Compare Match A                  (TIMER2_COMPA_vect)
 9  Timer/Counter2 Compare Match B                  (TIMER2_COMPB_vect)
10  Timer/Counter2 Overflow                         (TIMER2_OVF_vect)
11  Timer/Counter1 Capture Event                    (TIMER1_CAPT_vect)
12  Timer/Counter1 Compare Match A                  (TIMER1_COMPA_vect)
13  Timer/Counter1 Compare Match B                  (TIMER1_COMPB_vect)
14  Timer/Counter1 Overflow                         (TIMER1_OVF_vect)
15  Timer/Counter0 Compare Match A                  (TIMER0_COMPA_vect)
16  Timer/Counter0 Compare Match B                  (TIMER0_COMPB_vect)
17  Timer/Counter0 Overflow                         (TIMER0_OVF_vect)
18  SPI Serial Transfer Complete                    (SPI_STC_vect)
19  USART Rx Complete                               (USART_RX_vect)
20  USART, Data Register Empty                      (USART_UDRE_vect)
21  USART, Tx Complete                              (USART_TX_vect)
22  ADC Conversion Complete                         (ADC_vect)
23  EEPROM Ready                                    (EE_READY_vect)
24  Analog Comparator                               (ANALOG_COMP_vect)
25  2-wire Serial Interface  (I2C)                  (TWI_vect)
26  Store Program Memory Ready                      (SPM_READY_vect)

(Note that Reset cannot be masked).

Conceivably a low-level interrupt might be in progress (eg. TIMER0_OVF_vect). While that is busy doing its stuff multiple other interrupt events might occur (and set the corresponding bits in the CPU). They will be serviced in the above order, not in the order in which they actually occur in time.

There are hardware registers that can be written to, to cancel a pending interrupt - that is, to clear the flag.


The reason for mentioning "one more machine code instruction" is that the processor is designed to guarantee that when it transitions from interrupts not enabled, to interrupts enabled, one more instruction is always executed.

This lets you write code like this:

  interrupts ();             // guarantees next instruction executed
  sleep_cpu ();              // sleep now

Without that, an interrupt might occur before going to sleep. Which means you never wake, because you were relying upon the interrupt occuring during sleep, not before it.


How wonderfully moronic of Freescale and Atmel to both use the very same instruction names, but with inverted meanings

That is why I prefer the mnemonics of interrupts and noInterrupts because the intent there is very clear. These are implemented by defines in the core include files.

Nick Gammon
  • 1,173
  • 10
  • 22
2

The documentation mention that Arduino interrupts have priority:

If your sketch uses multiple ISRs, only one can run at a time. Other interrupts will be executed after the current one finishes in an order that depends on the priority they have.

It also provides a link for additional information:

For more information on interrupts, see Nick Gammon's notes.

According to sections What is interrupt priority? and Can interrupts occur while interrupts are disabled?, we can conclude that:

  1. Interrupts are based on the list of flags. When an event occurs the corresponding flag is set.
  2. If the ISR can't fire right now, it can be called at any time later because the flag is saved.
  3. There is a list of all available interrupts, and it generally depends on the chip. The higher up the list, the higher the priority.

So, different interrupts will not interrupt each other. They will be executed according to their priority.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kelin
  • 11,323
  • 6
  • 67
  • 104