AVR® Interrupt operation must be carefully initialized by the application developer. This page summarizes the key initialization and usage steps required for using interrupts in an application.
Further information regarding interrupt usage is provided in the Interrupt Module section of the AVR-LIBC Library.
Step 1. #include Standard Headers
The application must include header files avr/io.h and avr/interrupt.h as shown below:
The avr/interrupt.h header file provides several macros intended to simplify the application of interrupts in an application, such as macros for globally enabling/disabling interrupts (I-bit in the Status Register), as well as a macro for assigning an interrupt function to a specific interrupt vector:
- sei( )
- cli( )
- ISR(vector_id, attributes)
The vector_id macros are defined in the processor-specific header file (included via avr/io.h), as well as in the device data sheet. Its construction is defined below.
Step 2. Provide Interrupt Service Routine
An interrupt handler function is different to an ordinary function in that it handles the context save and restore to ensure that upon return from interrupt, the program context is maintained. A different code sequence is used to return from these functions as well.
There are several actions that the compiler needs to take to generate an interrupt service routine:
- The compiler has to be told to use an alternate form of return instruction (RETI vs. RET)
- The compiler has to be told about any specific additional options
- Enable nesting of interrupts
- Options for generation of prologue/epilogue code
- The function needs to be linked to a specific interrupt vector.
Several handler function attributes are provided to the application developer, enabling these options.
- The ISR( ) macro is provided to ease the definition of interrupt handler functions with attributes
For all interrupt vectors without specific handlers, a default interrupt handler will be installed: The default interrupt handler will reset the device.
An application may override the default handler and provide an application-specific default interrupt handler by using the BADISR_vect vector_id inside the ISR( ) macro.
ISR( ) Macro
The following code example shows how to use the ISR() macro to define an interrupt function:
The various parameters will now be more fully described.
vector_id
This identifier is a concatenation of a Vector Source ID and _vect. Vector Source IDs are found in the device data sheet, as shown (partially) in the following example for ATmega328PB:
Misspelt vector_ids will still generate a function, however, it will not be wired into the interrupt vector table. The compiler will generate a warning if it detects a suspiciously looking name.
Attributes
ISR( ) attributes provide further instruction to the compiler regarding how to setup the interrupt function.
ISR_BLOCK
Global interrupts are initially disabled by the AVR hardware when entering the ISR. This setting does not modify this state.
This attribute is identical to an ISR( ) macro with no attribute specified
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.
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.
ISR_ALIASOF(vector_id)
This may be used to define additional vectors that share the same handler. The following example aliases the PCINT1 vector to the PCINT0 handler:
ISR( ) Example
In this code example, we highlight the required header files and correct ISR definition of a handler function for the Timer/Counter1 Clear-Timer-On-Compare (CTC) mode interrupt source. The handler toggles LED0 on the ATmega328PB Xplained Mini every 100 mS:
Step 3. Configure the Peripheral
Next, you must configure the peripheral to generate interrupt request events.
For example, the ATmega328PB contains several Timer/Counter peripherals modules. Each module has a mode called Clear Timer on Compare (CTC) that, when properly initialized, will periodically trigger a Timer1 Output Compare Match Flag signal in the TC1 interrupt flag register (TIFR1) register as shown:
In this example, we will initialize Timer/Counter1 in CTC mode to generate interrupt requests every 100 mS, given a prescaled input of 250 kHz (16 MHz/64):
This is an example of a non-persistent interrupt. The TIFR1.OCFA flag is automatically cleared by hardware on entry to the handler.
The TIFR1.OCFA flag can also be cleared manually by writing a logic "1" to the bit location.
Step 4. Enable All Interrupts
Finally, we need to globally enable all enabled peripheral interrupts by setting the Global Interrupt Enable I-bit in the Status Register (SREG).
The AVR-LIBC interrupt library provides two handy macro functions for this:
- sei( ) to Enable interrupts globally
- cli( ) to Disable interrupts globally