5.9.1.4 Writing Hardware Interrupt Vector Table ISRs
If your project is using a device with the VIC module and you intended to use one or more hardware interrupt vector tables (hardware IVTs), observe the following guidelines when writing interrupt service routines (ISRs).
Write as many ISRs as required using the __interrupt()
specifier, using
void
as the ISR return type and specifying a parameter list of either
void
or one char
parameter if you need to identify the
interrupt source.
Devices that have the VIC module identify each interrupt with a number.
This number can be specified with the irq()
argument to
__interrupt()
if the vector table is enabled, or you can use a compiler-defined
symbol that equates to that number. You can see a list of all interrupt numbers, symbols
and descriptions by opening the files pic_chipinfo.html or
pic18_chipinfo.html in your favorite web browser, and selecting
your target device. Both of these files are located in the docs
directory under your compiler’s installation directory.
Assign the interrupt priority to the function’s source, using either
low_priority
or __low_priority
, or
high_priority
or __high_priority
. If an ISR does not
specify a priority, it will default to being high priority. It is recommended that you
always specify the ISR priority to ensure your code is readable.
If the ISR processes more than one source, determine the source of the interrupt from the function’s parameter, if specified, or by checking the interrupt flag and the interrupt enable for each source that is to be processed.
At the appropriate point in your code, assign the required priority to each interrupt source by writing the appropriate bits in the SFRs. Additionally, enable the interrupt sources required and the global interrupt enable.
TMR0
and
TMR1
were used as the interrupt
numbers.void __interrupt(__irq(TMR0), __high_priority) tc0Int(void) {
TMR0IF=0;
++tick_count;
return;
}
void __interrupt(__irq(TMR1), __high_priority) tc1Int(void) {
TMR1IF=0;
tick_count += 100;
return;
}
irq()
argument and using a function parameter to hold the source number, such as in the following
example.void __interrupt(__irq(TMR0,TMR1), __high_priority) tInt(unsigned char src) {
switch(src) {
case IRQ_TMR0:
TMR0IF=0;
++tick_count;
break;
case IRQ_TMR1:
TMR1IF=0;
tick_count += 100;
break;
}
return;
}
The VIC module will load the parameter, in this example, src
,
with the interrupt source number when the interrupt occurs.The special interrupt source symbol default
or
__default
can be used to indicate that the ISR will be linked to any
interrupt vector not already explicitly specified using irq()
. You can
also populate unused vector locations by using the -mundefints
option (see
Undefints Option).
By default, the interrupt vector table will be located at an address equal
to the reset value of the IVTBASE register, which is the legacy address of 0x8. The
base()
or __base()
argument to
__interrupt()
can be used to specify a different table base address for that
function. This argument can take one or more comma-separated addresses. The base address
cannot be set to an address lower than the reset value of the IVTBASE register.
By default, and if it is required, the compiler will initialize the
IVTBASE register in the runtime startup code. You can disable this functionality by turning
off the -mivt
option (see Ivt Option). This option also
allows you to specify an initial address for this register, which will position the vector
table ready for mainline code. If vectored interrupts are enabled but you do not specify an
address using this option, the vector table location will be set to the lowest table
address used in the program, as specified by the __base()
arguments to
__interrupt()
.
If you use the __base()
argument to implement more than
one table of interrupt vectors, you must ensure that you allocate sufficient memory for
each table. The compiler will emit an error message if it detects an overlap of any
interrupt vectors.
defIsr()
, which appears in both vector tables. For
these ISRs to become active, the IVTBASE register must first be loaded with either 0x100 or
0x200. Changing the address in this register at runtime allows you to select which vector
table is
active.void __interrupt(__irq(TMR0,TMR1), __base(0x100)) timerIsr(void) {
// process timer interrupts here
}
void __interrupt(__irq(TMR0,TMR1), __base(0x200)) altTimerIsr(void) {
// process timer interrupts here
}
void __interrupt(__irq(default), __base(0x100,0x200), low_priority) defIsr(void) {
// process extraneous low priority interrupts here
}