<avr/interrupt.h>: Interrupts(3)

NAME
<avr/interrupt.h>: Interrupts -

Detailed Description

Note:
    This discussion of interrupts was originally taken from Rich Neswold's document. See Acknowledgments.

  Introduction to avr-libc's interrupt handling

It's nearly impossible to find compilers that agree on how to handle interrupt code. Since the C language tries to stay away from machine dependent details, each compiler writer is forced to design their method of support.

In the AVR-GCC environment, the vector table is predefined to point to interrupt routines with predetermined names. By using the appropriate name, your routine will be called when the corresponding interrupt occurs. The device library provides a set of default interrupt routines, which will get used if you don't define your own.

Patching into the vector table is only one part of the problem. The compiler uses, by convention, a set of registers when it's normally executing compiler-generated code. It's important that these registers, as well as the status register, get saved and restored. The extra code needed to do this is enabled by tagging the interrupt function with __attribute__((signal)).

These details seem to make interrupt routines a little messy, but all these details are handled by the Interrupt API. An interrupt routine is defined with ISR(). This macro register and mark the routine as an interrupt handler for the specified peripheral. The following is an example definition of a handler for the ADC interrupt.

#include <avr/interrupt.h>
ISR(ADC_vect)
{
    // user code here
}

Refer to the chapter explaining assembler programming for an explanation about interrupt routines written solely in assembler language.

  Catch-all interrupt vector

If an unexpected interrupt occurs (interrupt is enabled and no handler is installed, which usually indicates a bug), then the default action is to reset the device by jumping to the reset vector. You can override this by supplying a function named __vector_default which should be defined with ISR() as such.

#include <avr/interrupt.h>
ISR(__vector_default)
{
    // user code here
}

  Nested interrupts

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. The compiler can be instructed to insert an SEI instruction right at the beginning of an interrupt handler by declaring the handler the following way:

void XXX_vect(void) __attribute__((interrupt));
void XXX_vect(void) {
  ...
}

where XXX_vect is the name of a valid interrupt vector for the MCU type in question, as explained below.

  Chosing the vector: Interrupt vector names

The interrupt is chosen by supplying one of the symbols in following table.

There are currently two different styles present for naming the vectors. One form uses names starting with SIG_, followed by a relatively verbose but arbitrarily chosen name describing the interrupt vector. This has been the only available style in avr-libc up to version 1.2.x.

Starting with avr-libc version 1.4.0, a second style of interrupt vector names has been added, where a short phrase for the vector description is followed by _vect. The short phrase matches the vector name as described in the datasheet of the respective device (and in Atmel's XML files), with spaces replaced by an underscore and other non-alphanumeric characters dropped. Using the suffix _vect is intented to improve portability to other C compilers available for the AVR that use a similar naming convention.

The historical naming style might become deprecated in a future release, so it is not recommended for new projects.

Note:
    The ISR() macro cannot really spell-check the argument passed to them. Thus, by misspelling one of the names below in a call to ISR(), a function will be created that, while possibly being usable as an interrupt function, is not actually wired into the interrupt vector table. The compiler will generate a warning if it detects a suspiciously looking name of a ISR() function (i.e. one that after macro replacement does not start with '__vector_').

  Macros for writing interrupt handler functions
#define ISR(vector)
#define SIGNAL(signame)
#define EMPTY_INTERRUPT(vector)

Define Documentation

  #define EMPTY_INTERRUPT(vector)

Value:

void vector (void) __attribute__ ((naked));    void vector (void) { __asm__ __volatile__ ('reti' ::); }

 #include <avr/interrupt.h>

Defines an empty interrupt handler function. This will not generate any prolog or epilog code and will only return from the ISR. Do not define a function body as this will define it for you. Example:

 EMPTY_INTERRUPT(ADC_vect);

  #define ISR(vector)

Value:

void vector (void) __attribute__ ((signal));         void vector (void)

 #include <avr/interrupt.h>

Introduces an interrupt handler function (interrupt service routine) that runs with global interrupts initially disabled.

vector must be one of the interrupt vector names that are valid for the particular MCU type.

  #define SIGNAL(signame)

Value:

void signame (void) __attribute__ ((signal));            void signame (void)

 #include <avr/interrupt.h>

Introduces an interrupt handler function that runs with global interrupts initially disabled.

This is the same as the ISR macro.

Note:
    Do not use anymore in new code, it will be deprecated in a future release.