Documentation on AVR C-library for Interrupts
Obtained from http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
Global manipulation of the interrupt flag
The global interrupt flag is maintained in the I bit of the status register (SREG).
Frequently, interrupts are being disabled for periods of time in order to perform certain
operations without being disturbed; see Problems with reordering code for things to be
taken into account with respect to compiler optimizations.
define sei()
define cli()
Macros for writing interrupt handler functions
define ISR(vector, attributes)
define SIGNAL(vector)
define EMPTY_INTERRUPT(vector)
define ISR_ALIAS(vector, target_vector)
define reti()
define BADISR_vect
ISR attributes
define ISR_BLOCK
define ISR_NOBLOCK
define ISR_NAKED
define ISR_ALIASOF(target_vector)
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
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 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 is
not included.)
include
ISR(BADISR_vect)
{
// 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:
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.
Two vectors sharing the same code
In some circumstances, the actions to be taken upon two different interrupts might be
completely identical so a single implementation for the ISR would suffice. For example, pin-
change interrupts arriving from two different ports could logically signal an event that is
independent from the actual port (and thus interrupt vector) where it happened. Sharing
interrupt vector code can be accomplished using the ISR_ALIASOF() attribute to the ISR
macro:
ISR(PCINT0_vect)
{
…
// Code to handle the event.
}
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
Note:
There is no body to the aliased ISR.
Note that the ISR_ALIASOF() feature requires GCC 4.2 or above (or a patched version of
GCC 4.1.x). See the documentation of the ISR_ALIAS() macro for an implementation which
is less elegant but could be applied to all compiler versions.
Empty interrupt service routines
In rare circumstances, in interrupt vector does not need any code to be implemented at all.
The vector must be declared anyway, so when the interrupt triggers it won’t execute the
BADISR_vect code (which by default restarts the application).
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);
Note:
There is no body to this macro.
Manually defined ISRs
In some circumstances, the compiler-generated prologue and epilogue of the ISR might not
be optimal for the job, and a manually defined ISR could be considered particularly to
speedup the interrupt handling.
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();
}
Choosing 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“).
Vector name Old vector name Description
ADC_vect SIG_ADC ADC Conversion Complete
ANALOG_COMP_vect SIG_COMPARATOR Analog Comparator
EE_READY_vect SIG_EEPROM_READY EEPROM Ready
INT0_vect SIG_INTERRUPT0 External Interrupt 0
INT1_vect SIG_INTERRUPT1 External Interrupt Request 1
INT2_vect SIG_INTERRUPT2 External Interrupt Request 2
PCINT0_vect SIG_PIN_CHANGE0 Pin Change Interrupt Request 0
PCINT1_vect SIG_PIN_CHANGE1 Pin Change Interrupt Request 1
PCINT2_vect SIG_PIN_CHANGE2 Pin Change Interrupt Request 2
SPI_STC_vect SIG_SPI Serial Transfer Complete
SPM_READY_vect SIG_SPM_READY Store Program Memory Read
TIMER0_COMPA_vect SIG_OUTPUT_COMPARE0A
TimerCounter0 Compare Match
A
TIMER0_COMPB_vect
SIG_OUTPUT_COMPARE0B,
SIG_OUTPUT_COMPARE0_B
Timer Counter 0 Compare Match
B
TIMER0_OVF_vect SIG_OVERFLOW0 Timer/Counter0 Overflow
TIMER1_CAPT_vect SIG_INPUT_CAPTURE1 Timer/Counter Capture Event
TIMER1_COMPA_vect SIG_OUTPUT_COMPARE1A
Timer/Counter1 Compare Match
A
TIMER1_COMPB_vect SIG_OUTPUT_COMPARE1B
Timer/Counter1 Compare
MatchB
TIMER1_OVF_vect SIG_OVERFLOW1 Timer/Counter1 Overflow
TIMER2_COMPA_vect SIG_OUTPUT_COMPARE2A
Timer/Counter2 Compare Match
A
TIMER2_COMPB_vect SIG_OUTPUT_COMPARE2B
Timer/Counter2 Compare Match
A
TIMER2_OVF_vect SIG_OVERFLOW2 Timer/Counter2 Overflow
TWI_vect SIG_2WIRE_SERIAL 2-wire Serial Interface
USART_RX_vect
SIG_USART_RECV,
SIG_UART_RECV
USART, Rx Complete
USART_TXC_vect
SIG_USART_TRANS,
SIG_UART_TRANS
USART, Tx Complete
USART_TX_vect
SIG_USART_TRANS,
SIG_UART_TRANS
USART, Tx Complete
USART_UDRE_vect
SIG_USART_DATA,
SIG_UART_DATA
USART Data Register Empty
WDT_vect
SIG_WDT,
SIG_WATCHDOG_TIMEOUT
Watchdog Timeout Interrupt
Define Documentation
define BADISR_vect
include
This is a vector which is aliased to __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.
define cli (
)
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. However, the macro also
implies a memory barrier which can cause additional loss of optimization.
In order to implement atomic access to multi-byte objects, consider using the macros from
, rather than implementing them manually with cli() and sei().
define EMPTY_INTERRUPT (
vector )
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,
attributes
)
Introduces an interrupt handler function (interrupt service routine) that runs with global
interrupts initially disabled by default with no attributes specified.
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).
vector must be one of the interrupt vector names that are valid for the particular MCU type.
define ISR_ALIASOF (
target_vector )
The ISR is linked to another ISR, specified by the vect parameter. This is compatible with
GCC 4.2 and greater only.
Use this attribute in the attributes parameter of the ISR macro.
define ISR_BLOCK
Identical to an ISR with no attributes specified. Global interrupts are initially disabled by the
AVR hardware when entering the ISR, without the compiler modifying this state.
Use this attribute in the attributes parameter of the ISR macro.
define ISR_NAKED
ISR is created with no prologue or epilogue code. The user code is responsible for
preservation of the machine state including the SREG register, as well as placing a reti() at
the end of the interrupt routine.
Use this attribute in the attributes parameter of the ISR macro.
define ISR_NOBLOCK
ISR runs with global interrupts initially enabled. The interrupt enable flag is activated by the
compiler as early as possible within the ISR to ensure minimal processing delay for nested
interrupts.
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.
Use this attribute in the attributes parameter of the ISR macro.
define reti (
)
Returns from an interrupt routine, enabling global interrupts. This should be the last
command executed before leaving an ISR defined with the ISR_NAKED attribute.
This macro actually compiles into a single line of assembly, so there is no function call
overhead.
define sei (
)
Enables interrupts by setting the global interrupt mask. This function actually compiles into a
single line of assembly, so there is no function call overhead. However, the macro also
implies a memory barrier which can cause additional loss of optimization.
In order to implement atomic access to multi-byte objects, consider using the macros from
, rather than implementing them manually with cli() and sei().
Automatically generated by Doxygen 1.7.6.1 on Tue Jan 3 2012.