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 ;-)]
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);
| |
void | timer_enable_int (unsigned char ints) |
|
#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. |
|
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: (SIG_ADC); |
|
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. |
|
#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. |
|
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. |
|
#include <avr/interrupt.h>
This function modifies the |