Interrupts in Embedded C for Microcontrollers- Explained
In this article, we go over what interrupts are and how we can code for them in C for embedded applications.
What is an Interrupt?
An interrupt is a change of flow, or interruption in the program operation causedc by an external or internal hardware source. An interrupt, is a hardware-generated function call.
The result of an interrupt is that it will cause the flow of execution to pause while the interrupt function, called the interrupt service routine (ISR), is executed. Upon completion of the ISR, the program flow will resume, continuing from where it was interrupted.
In an AVR, an interrupt will cause the status register and program counter to be placed on the stack, and, based on the source of the interrupt, the program counter will be assigned a value from a table of addresses. These addresses are referred to as vectors.
Once a program has been redirected by interrupt vectoring, it can be returned to normal operation through machine instruction RETI (RETurn from Interrupt). The RETI instruction
restores the status register to its pre-interrupt value and sets the program counter to the next machine instruction following the one that was interrupted.
AVR Interrupts
There are many sources of interrupts available on the AVR microcontroller. The larger the AVR, the more interrupt sources that are available.
Listed below are some of the possibilities. These definitions are usually found in a header file, at the top of a program, and are specific to a given microprocessor. These headers are found in a header for an ATMega128.
#define EXT_INT0 2
#define EXT_INT1 3
#define EXT_INT2 4
#define EXT_INT3 5
#define EXT_INT4 6
#define EXT_INT5 7
#define EXT_INT6 8
#define EXT_INT7 9
#define TIM2_COMP 10
#define TIM2_OVF 11
#define TIM1_CAPT 12
#define TIM1_COMPA 13
#define TIM1_COMPB 14
#define TIM1_OVF 15
#define TIM0_COMP 16
#define TIM0_OVF 17
#define SPI_STC 18
#define USART0_RXC 19
#define USART0_DRE 20
#define USART0_TXC 21
#define ADC_INT 22
#define EE_RDY 23
#define ANA_COMP 24
#define TIM1_COMPC 25
#define TIM3_CAPT 26
#define TIM3_COMPA 27
#define TIM3_COMPB 28
#define TIM3_COMPC 29
#define TIM3_OVF 30
#define USART1_RXC 31
#define USART1_DRE 32
#define USART1_TXC 33
#define TWI 34
#define SPM_RDY 35
This list gives a series of vector indices associated with a name describing the interrupt source.
How to Create a Interrupt Service Routine (ISR)
So the above list defines all the interrupts for the microcontroller.
Now in order to actually create the code to run one of these interrupts, we need to create an ISR.
To create an ISR, the function that is called by the interrupt system is declared using the reserved keyword interrupt as a function type modifier.
This code:
interrupt [EXT_INT0] void external_into0 (void)
{
//whatever is in this function will execute. Called automaticcally on external interrupt 0
}
This function will be called and execute when external interrupt 0 is triggered.
A second example:
interrupt [TIM0_OVF] void timer0_overflow (void)
{
//called automatically on TIMER0 overflow
}
This interrupt will be executed on a TIMER0 overflow.
The interrupt keyword is followed by an index, which is the vector location of the interrupt source. The vector numbers start with [1], but since the first vector is the reset vector, the actual interrupt vectors available to the programmer begin with [2].
ISRs can be executed at any time, once the interrupt sources are initialized and the global interrupts are enabled.
The ISR cannot return any values, as it is always declared at type void. This is also why nothing can be passed to the ISR.
Interrupts in an embedded environment can genuinely create a real-time execution. It is not uncommon in a peripheral-rich system to see an empty while(1) loop in the main() function. In these cases, main() simply initializes the hardware and the interrupts perform all the necessary tasks when they need to happen.