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.
BADISR_vect
which should be defined with
ISR() as such.
(The name BADISR_vect is actually an alias for __vector_default.
The latter must be used inside assembly code in case
<avr/interrupt.h> is not included.)
#include <avr/interrupt.h>
ISR(BADISR_vect) {
// user code here
}
ISR(XXX_vect, ISR_NOBLOCK) {
...
}
where XXX_vect is the name of a valid interrupt vector for the MCU type in question,
as explained below.
ISR(PCINT0_vect) {
...
// Code to handle the event.
}
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
This could for example be the case for interrupts that are solely enabled for the purpose of getting the controller out of sleep_mode().
A handler for such an interrupt vector can be declared using the EMPTY_INTERRUPT() macro:
EMPTY_INTERRUPT(ADC_vect);
One solution to this could be to implement the entire ISR as manual assembly code in a separate (assembly) file. See Combining C and assembly source files for an example of how to implement it that way.
Another solution is to still implement the ISR in C language but take over the
compiler's job of generating the prologue and epilogue.
This can be done using the ISR_NAKED attribute to the
ISR() macro.
Note that the compiler does not generate anything as prologue or epilogue,
so the final reti()
must be provided by the actual implementation.
SREG must be manually saved if the ISR code modifies it, and the
compiler-implied assumption of __zero_reg__ always being 0
could be wrong (e. g. when interrupting right after of a MUL instruction).
ISR(TIMER1_OVF_vect, ISR_NAKED) {
PORTB |= _BV(0); // results in SBI which does not affect SREG
reti();
}
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.
ISR (vector, attributes)
#include <avr/interrupt.h>
ISR (ADC_vect) {
uint8_t i=adc_writeindex;
adc_ringbuf[i] = ADC; // Save the ADC value while next conversion may be in progress
if (++i>=elemof(adc_ringbuf)) i=0;
adc_writeindex=i;
}
The attributes are optional and alter the behaviour and resultant generated code of the interrupt routine. Multiple attributes may be used for a single function, with a space seperating each attribute.
Valid attributes are ISR_BLOCK, ISR_NOBLOCK, ISR_NAKED and ISR_ALIASOF(vect).
INT0_vect, INT1_vect, etc.
vector must be one of the interrupt vector names that are valid for the particular MCU type.
__vector_default,
the vector executed when an ISR fires with no accompanying ISR handler.
This may be used along with the ISR() macro
to create a catch-all for undefined but used ISRs for debugging purposes.The default handler jumps to address zero, i.e. performs like chip reset.
#include <avr/interrupt.h>
ISR(BADISR_vect) {
DDRB |= _BV(2);
PORTB |= _BV(2); // Light up a red lamp for my bug
for(;;) wdt_reset(); // don't reti but require reset by user
}
ISR_BLOCK
ISR_NOBLOCK
This may be used to create nested ISRs, however care should be taken to avoid stack overflows, or to avoid infinitely entering the ISR for those cases where the AVR hardware does not clear the respective interrupt flag before entering the ISR.
#include <avr/interrupt.h>
ISR (PCINT0_vect, ISR_NOBLOCK) { // Some trigger
OCR1B = rand(); // Put a random value to PWM DAC; rand() does lengthy processing
}
ISR_NAKED
#include <avr/interrupt.h>
ISR (INT0_vect, ISR_NAKED) {
PORTB |= _BV(3); // Evaluates to 'sbi PORTB,3', flags remain untouched
reti();
}
ISR_ALIASOF (target_vector)
#include <avr/interrupt.h> ISR (INT1_vect, ISR_ALIASOF (INT0_vect)); // map INT1 to INT0
ISR_ALIAS (vector, target_vector)
ISR(..., ISR_ALIASOF(...)) is recommended.
#include <avr/interrupt.h>
ISR (INT0_vect) {
PORTB = 42;
}
ISR_ALIAS (INT1_vect, INT0_vect);
EMPTY_INTERRUPT (vector)
#include <avr/interrupt.h> EMPTY_INTERRUPT (ADC_vect); // the main loop will wake from sleep()
cli()
sei()
#include <avr/interrupt.h> cli(); g_index++; // atomic increment of a volatile memory variable sei();
reti()
This macro actually compiles into a single line of assembly, so there is no function call overhead.
SIGNAL (vector)
This is the same as the ISR macro without optional attributes.
SIGNAL() in new code.
Use ISR() instead.