Skip to main content
Engineering LibreTexts

29.1: Introduction

  • Page ID
  • Interrupts may be thought of as small snippets of code that are of such high priority that the main program flow is momentarily halted so that the interrupt code can be run. Once the interrupt code (referred to as an interrupt service routine, or ISR) is finished, the main code picks up where it left off. When dealing with interrupts always keep in mind that they could happen at any time, even in the middle of a line of C code (this is due to the fact that a single line of C might generate several lines of native machine code and the interrupt could occur in between machine code instructions).

    Interrupts usually come from two sources: an externally triggered event such as a physical pin changing state or via an internally triggered event. For example, an external switch could be used to force the program to reset itself from the start. Alternately, the overflow and compare match events of internal timer/counters are often used to create internal interrupts that can control the timing of other events.

    When an interrupt occurs, the current state and location in the executing code is saved. The microcontroller then jumps to the starting address of the ISR. The ISR code is executed and upon completion program flow reverts back to where it left off in the main sequence. As the main program flow is being halted temporarily, ISRs are designed to be short and to execute quickly so as to have minimal timing impact on the main code. As most microcontrollers have numerous sources for interrupts, interrupts are usually prioritized, some being more important than others. This means it is possible for one interrupt to interrupt another interrupt (i.e., nested interrupts having different priorities). In this way, something like a fire alarm sensor could take precedence over a more mundane sensor input such as a volume control.

    It is quite possible for a complicated application to have over a dozen possible interrupt sources. Each of these will have its own ISR. The starting locations of the ISRs are found in a vector table. A vector table is basically an array of pointers. In this case, these pointers point to the starting address of some code rather than to the addresses of variables. Some programmers will use the word “vector” as a verb as in “the code flow vectors off to the ISR”.

    To simplify programming, the names of the ISRs are predetermined. As a programmer you simply need to fill in the code for the ISR. This might be as simple as setting a global variable. When the program is compiled, the predetermined name is noted in your code and then expanded into the vector table for you automatically. Generally speaking, ISRs do not take arguments nor do you need to create function prototypes for them. A list of ISR names for the ATmega 328P series is shown in Figure \(\PageIndex{1}\). This list is taken directly from the hardware profile file include/avr/iom328p.h. Note that each ISR name ends in _vect while the first portion of the name indicates the hardware with which it associated. A quick scan of the list should reveal a number of hardware elements covered earlier such as the three timer/counters and the analog to digital converter.

    Finally, before these interrupts can be used the desired interrupt must be enabled by setting the appropriate bit in the associated register (EIMSK, External Interrupt MaSK; TIMSKx, Timer Interrupt MaSKx; etc., see the Appendix and following examples for details). Of course, the global interrupt enable must also be set (which can be accomplished via the sei() call) although this is already the case in the Arduino system.

    Figure \(\PageIndex{1}\): ISR vector table names

    /* Interrupt Vector 0 is the reset vector. */
    #define INT0_vect         _VECTOR(1)   /* External Interrupt Request 0 */
    #define INT1_vect         _VECTOR(2)   /* External Interrupt Request 1 */
    #define PCINT0_vect       _VECTOR(3)   /* Pin Change Interrupt Request 0 */
    #define PCINT1_vect       _VECTOR(4)   /* Pin Change Interrupt Request 1 */
    #define PCINT2_vect       _VECTOR(5)   /* Pin Change Interrupt Request 2 */
    #define WDT_vect          _VECTOR(6)   /* Watchdog Time-out Interrupt */
    #define TIMER2_COMPA_vect _VECTOR(7)   /* Timer/Counter2 Compare Match A */
    #define TIMER2_COMPB_vect _VECTOR(8)   /* Timer/Counter2 Compare Match B */
    #define TIMER2_OVF_vect   _VECTOR(9)   /* Timer/Counter2 Overflow */
    #define TIMER1_CAPT_vect  _VECTOR(10)  /* Timer/Counter1 Capture Event */
    #define TIMER1_COMPA_vect _VECTOR(11)  /* Timer/Counter1 Compare Match A */
    #define TIMER1_COMPB_vect _VECTOR(12)  /* Timer/Counter1 Compare Match B */
    #define TIMER1_OVF_vect   _VECTOR(13)  /* Timer/Counter1 Overflow */
    #define TIMER0_COMPA_vect _VECTOR(14)  /* Timer/Counter0 Compare Match A */
    #define TIMER0_COMPB_vect _VECTOR(15)  /* Timer/Counter0 Compare Match B */
    #define TIMER0_OVF_vect   _VECTOR(16)  /* Timer/Counter0 Overflow */
    #define SPI_STC_vect      _VECTOR(17)  /* SPI Serial Transfer Complete */
    #define USART_RX_vect     _VECTOR(18)  /* USART, Rx Complete */
    #define USART_UDRE_vect   _VECTOR(19)  /* USART, Data Register Empty */
    #define USART_TX_vect     _VECTOR(20)  /* USART, Tx Complete */
    #define ADC_vect          _VECTOR(21)  /* ADC Conversion Complete */
    #define EE_READY_vect     _VECTOR(22)  /* EEPROM Ready */
    #define ANALOG_COMP_vect  _VECTOR(23)  /* Analog Comparator */
    #define TWI_vect          _VECTOR(24)  /* Two-wire Serial Interface */
    #define SPM_READY_vect    _VECTOR(25)  /* Store Program Memory Read */