Configuring megaAVR® Interrupts
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.
#include Files
#include Standard Headers
The application must include header files avr/io.h and avr/interrupt.h as shown in the following code:
#include <avr/interrupt.h>
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 datasheet. Its construction is defined below.
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
Review the Following Examples
ISR( ) Macro
The following code example shows how to use the ISR() macro to define an interrupt function:
{
/* Hardware auto-clears the interrupt flag (most interrupt sources) */
/* Clear the cause of the interrupt (required by some interrupt sources) */
/* ISR-specific processing */
}
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 datasheet, as shown (partially) in the following example for ATmega328PB:
Attributes
ISR( ) attributes provide further instruction to the compiler regarding how to set up 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.
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.
ISR_NAKED
ISR is created with no prologue or epilogue code. The user code is responsible for the 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:
{
...
// Code to handle the event.
}
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
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:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <avr/interrupt.h>
ISR(TIMER1_COMPA_vect, ISR_BLOCK)
{
PORTB ^= (1 << PORTB5); // Toggle LED0
}
int main(void)
{
// Initialization
// Set LED as output
DDRB |= (1 << PORTB5); // Configure PB5 as digital output
PORTB &= ~(1 << PORTB5); // Set initial level for PB5
// Set up Timer/Counter1
TCCR1B |= (1 << WGM12 ); // Configure timer 1 for CTC mode
OCR1A = 25000; // Set CTC compare value to 10Hz (100mS)
// at 16MHz AVR clock, with a prescaler of 64
TIMSK1 |= (1 << OCIE1A ); // Enable CTC interrupt
TCCR1B |= ((1 << CS10 ) | (1 << CS11 )); // Start Timer/Counter1 at F_CPU/64
// Enable all interrupts
sei();
while(1);
}
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 the accompanying image
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):
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <avr/interrupt.h>
ISR(TIMER1_COMPA_vect, ISR_BLOCK)
{
PORTB ^= (1 << PORTB5); // Toggle LED0
}
int main(void)
{
// Initialization
// Set LED as output
DDRB |= (1 << PORTB5); // Configure PB5 as digital output
PORTB &= ~(1 << PORTB5); // Set initial level for PB5
// Set up Timer/Counter1
TCCR1B |= (1 << WGM12 ); // Configure timer 1 for CTC mode
OCR1A = 25000; // Set CTC compare value to 10Hz (100mS)
// at 16MHz AVR clock, with a prescaler of 64
TIMSK1 |= (1 << OCIE1A ); // Enable CTC interrupt
TCCR1B |= ((1 << CS10 ) | (1 << CS11 )); // Start Timer/Counter1 at F_CPU/64
// Enable all interrupts
sei();
while(1);
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <avr/interrupt.h>
ISR(TIMER1_COMPA_vect, ISR_BLOCK)
{
PORTB ^= (1 << PORTB5); // Toggle LED0
}
int main(void)
{
// Initialization
// Set LED as output
DDRB |= (1 << PORTB5); // Configure PB5 as digital output
PORTB &= ~(1 << PORTB5); // Set initial level for PB5
// Set up Timer/Counter1
TCCR1B |= (1 << WGM12 ); // Configure timer 1 for CTC mode
OCR1A = 25000; // Set CTC compare value to 10Hz (100mS)
// at 16MHz AVR clock, with a prescaler of 64
TIMSK1 |= (1 << OCIE1A ); // Enable CTC interrupt
TCCR1B |= ((1 << CS10 ) | (1 << CS11 )); // Start Timer/Counter1 at F_CPU/64
// Enable all interrupts
sei();
while(1);
}