# Interrupts and Signals

## Detailed Description

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

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__((interrupt))`.

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 one of two macros, INTERRUPT() and SIGNAL(). These macros 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/signal.h>

    INTERRUPT(SIG_ADC)
    {
        // user code here
    }

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

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 either SIGNAL() or INTERRUPT() as such.

    #include <avr/signal.h>

    SIGNAL(__vector_default)
    {
        // user code here
    }

The interrupt is chosen by supplying one of the symbols in following table. Note that every AVR device has a different interrupt vector table so some signals might not be available. Check the data sheet for the device you are using.

[FIXME: Fill in the blanks! Gotta read those durn data sheets ;-)]

Note:
  The SIGNAL() and INTERRUPT() macros currently cannot spell-check the argument passed to them. Thus, by misspelling one of the names below in a call to SIGNAL() or INTERRUPT(), a function will be created that, while possibly being usable as an interrupt function, is not actually wired into the interrupt vector table. No warning will be given about this situation.

Signal Name | Description
SIG_2WIRE_SERIAL | 2-wire serial interface (aka. I²C [tm])
SIG_ADC | ADC Conversion complete
SIG_COMPARATOR | Analog Comparator Interrupt
SIG_EEPROM_READY | Eeprom ready
SIG_FPGA_INTERRUPT0 |
SIG_FPGA_INTERRUPT1 |
SIG_FPGA_INTERRUPT2 |
SIG_FPGA_INTERRUPT3 |
SIG_FPGA_INTERRUPT4 |
SIG_FPGA_INTERRUPT5 |
SIG_FPGA_INTERRUPT6 |
SIG_FPGA_INTERRUPT7 |
SIG_FPGA_INTERRUPT8 |
SIG_FPGA_INTERRUPT9 |
SIG_FPGA_INTERRUPT10 |
SIG_FPGA_INTERRUPT11 |
SIG_FPGA_INTERRUPT12 |
SIG_FPGA_INTERRUPT13 |
SIG_FPGA_INTERRUPT14 |
SIG_FPGA_INTERRUPT15 |
SIG_INPUT_CAPTURE1 | Input Capture1 Interrupt
SIG_INPUT_CAPTURE3 | Input Capture3 Interrupt
SIG_INTERRUPT0 | External Interrupt0
SIG_INTERRUPT1 | External Interrupt1
SIG_INTERRUPT2 | External Interrupt2
SIG_INTERRUPT3 | External Interrupt3
SIG_INTERRUPT4 | External Interrupt4
SIG_INTERRUPT5 | External Interrupt5
SIG_INTERRUPT6 | External Interrupt6
SIG_INTERRUPT7 | External Interrupt7
SIG_OUTPUT_COMPARE0 | Output Compare0 Interrupt
SIG_OUTPUT_COMPARE1A | Output Compare1(A) Interrupt
SIG_OUTPUT_COMPARE1B | Output Compare1(B) Interrupt
SIG_OUTPUT_COMPARE1C | Output Compare1(C) Interrupt
SIG_OUTPUT_COMPARE2 | Output Compare2 Interrupt
SIG_OUTPUT_COMPARE3A | Output Compare3(A) Interrupt
SIG_OUTPUT_COMPARE3B | Output Compare3(B) Interrupt
SIG_OUTPUT_COMPARE3C | Output Compare3(C) Interrupt
SIG_OVERFLOW0 | Overflow0 Interrupt
SIG_OVERFLOW1 | Overflow1 Interrupt
SIG_OVERFLOW2 | Overflow2 Interrupt
SIG_OVERFLOW3 | Overflow3 Interrupt
SIG_PIN |
SIG_PIN_CHANGE0 |
SIG_PIN_CHANGE1 |
SIG_RDMAC |
SIG_SPI | SPI Interrupt
SIG_SPM_READY | Store program memory ready
SIG_SUSPEND_RESUME |
SIG_TDMAC |
SIG_UART0 |
SIG_UART0_DATA | UART(0) Data Register Empty Interrupt
SIG_UART0_RECV | UART(0) Receive Complete Interrupt
SIG_UART0_TRANS | UART(0) Transmit Complete Interrupt
SIG_UART1 |
SIG_UART1_DATA | UART(1) Data Register Empty Interrupt
SIG_UART1_RECV | UART(1) Receive Complete Interrupt
SIG_UART1_TRANS | UART(1) Transmit Complete Interrupt
SIG_UART_DATA | UART Data Register Empty Interrupt
SIG_UART_RECV | UART Receive Complete Interrupt
SIG_UART_TRANS | UART Transmit Complete Interrupt
SIG_USART0_DATA | USART(0) Data Register Empty Interrupt
SIG_USART0_RECV | USART(0) Receive Complete Interrupt
SIG_USART0_TRANS | USART(0) Transmit Complete Interrupt
SIG_USART1_DATA | USART(1) Data Register Empty Interrupt
SIG_USART1_RECV | USART(1) Receive Complete Interrupt
SIG_USART1_TRANS | USART(1) Transmit Complete Interrupt
SIG_USB_HW |

## Global manipulation of the interrupt flag

The global interrupt flag is maintained in the I bit of the status register (SREG).
#define | sei() __asm__ __volatile__ ("sei" ::)
#define | cli() __asm__ __volatile__ ("cli" ::)

## Allowing specific system-wide interrupts

In addition to globally enabling interrupts, each device's particular interrupt needs to be enabled separately if interrupts for this device are desired. While some devices maintain their interrupt enable bit inside the device's register set, external and timer interrupts have system-wide configuration registers.

Example:

        // Enable timer 1 overflow interrupts.
        timer_enable_int(_BV(TOIE1));

        // Do some work...

        // Disable all timer interrupts.
        timer_enable_int(0);

Note:
  Be careful when you use these functions. If you already have a different interrupt enabled, you could inadvertantly disable it by enabling another intterupt.

#define | enable_external_int(mask) (__EICR = mask)
static __inline__ void | timer_enable_int (unsigned char ints)

## Macros for writing interrupt handler functions

#define | SIGNAL(signame)
#define | INTERRUPT(signame)
#define | EMPTY_INTERRUPT(signame)

## Define Documentation

#define cli | (

)

__asm__ __volatile__ ("cli" ::)

    #include <avr/interrupt.h>

Disables all interrupts by clearing the global interrupt mask. This function actually compiles into a single line of assembly, so there is no function call overhead.

#define EMPTY_INTERRUPT | ( | signame |  | ) |

Value:

    void signame (void) __attribute__ ((naked));    \
    void signame (void) { __asm__ __volatile__ ("reti" ::); }

    #include <avr/signal.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(SIG_ADC);

#define enable_external_int | ( | mask |  | ) | (__EICR = mask)

    #include <avr/interrupt.h>

This macro gives access to the `GIMSK` register (or `EIMSK` register if using an AVR Mega device or `GICR` register for others). Although this macro is essentially the same as assigning to the register, it does adapt slightly to the type of device being used. This macro is unavailable if none of the registers listed above are defined.

#define INTERRUPT | ( | signame |  | ) |

Value:

    void signame (void) __attribute__ ((interrupt));        \
    void signame (void)

    #include <avr/signal.h>

Introduces an interrupt handler function that runs with global interrupts initially enabled. This allows interrupt handlers to be interrupted.

#define sei | (

)

__asm__ __volatile__ ("sei" ::)

    #include <avr/interrupt.h>

Enables interrupts by clearing the global interrupt mask. This function actually compiles into a single line of assembly, so there is no function call overhead.

#define SIGNAL | ( | signame |  | ) |

Value:

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

    #include <avr/signal.h>

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

## Function Documentation

static __inline__ void timer_enable_int | ( | unsigned char | ints | ) | `[static]`

    #include <avr/interrupt.h>

This function modifies the `timsk` register. The value you pass via `ints` is device specific.

Automatically generated by Doxygen 1.4.1 on 21 Oct 2005.