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" ::)

Macros for writing interrupt handler functions

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

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.


__inline__ void timer_enable_int (unsigned char ints)


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 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

__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.3.6 on 21 Jan 2005.