4.8.1.1 Writing ISRs for a Full Vector Table
If your project is using the full vector table, observe the following guidelines when writing an ordinary ISR.
Enable the CCI (Ext Option). Write as many ISRs as required using the __interrupt()
specifier, using void
as the ISR return type and as the parameter list. Consider implementing default interrupt functions to handle accidental triggering of unused interrupts.
The __interrupt()
specifier takes a numerical argument to indicate the interrupt number, hence the interrupt source. Macros whose names ends with vect_num
are available once you have included <xc.h>
in your program and can be used to indicate the interrupt number in a more readable way.
At the appropriate point in your code, enable the interrupt sources required and the global interrupt enable.
-mext=cci
option.#include <xc.h>
void __interrupt(SPI_STC_vect_num) spi_Isr(void) {
process(SPI_ClientReceive());
return;
}
More complex interrupt function definitions can be created using macros and attributes defined by <avr/interrupt.h>
, which are shown in the following examples.
EMPTY_INTERRUPT()
macro and an interrupt source argument.#include <avr/interrupt.h>
EMPTY_INTERRUPT(INT2_vect);
BADISR_vect
, can be used with the ISR()
macro to define a function that can process any otherwise undefined interrupts. Without this function defined, an undefined interrupt will trigger a device reset. For example:#include <avr/interrupt.h>
ISR(BADISR_vect) {
// place code to process undefined interrupts here
return;
}
sei
instruction to your ISR to re-enable the global interrupt flag; however, there is an argument you can use with the ISR()
macro to have this instruction added by the compiler to the beginning of the interrupt routine. For example:#include <avr/interrupt.h>
ISR(IO_PINS_vect, ISR_NOBLOCK)
{ ... }
The ISR_NOBLOCK
expands to __attribute__((interrupt))
, which can be used instead.__interrupt()
and enabling the CCI) for one vector then reuse that ISR for other vector definitions using the ISR_ALIASOF()
argument.#include <xc.h>
void __interrupt(PCINT0_vect_num)
{ ... }
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
ISR_NAKED
argument, as shown in this example.#include <avr/interrupt.h>
ISR(TIMER1_OVF_vect, ISR_NAKED)
{
PORTB |= _BV(0); // results in SBI which does not affect SREG
reti();
}
Note that the compiler will not generate any context switch code, including the final return from interrupt instruction, so you must write any relevant switching code and the reti
instruction. The SREG register must be manually saved if it is modified by the ISR, and the compiler-implied assumption of __zero_reg__
always being 0 could be wrong, for example, when an interrupt occurs right after a mul
instruction.