5.9.1.2 Writing Dual-priority or Legacy Mode ISRs
If your project is using dual priority vectors, observe the following guidelines when writing interrupt service routines (ISRs).
Write one interrupt function using the __interrupt()
specifier to process
each priority being used. You can define at most two interrupt functions. Use
void
as the ISR return type and for the parameter list. Consider
implementing both interrupt functions, even if they are not needed, to handle accidental
triggering of unused interrupts, or use the -mundefints
option to provide
a default action (see Undefints Option).
Specify the priority each ISR handles, using any one of low_priority
or
__low_priority
; or high_priority
or
__high_priority
as argument to the __interrupt()
specifier. If no specifier argument is present, the ISR will default to being high
priority. It is recommended that you always specify the ISR priority to ensure your code is
readable.
Inside the ISR body, determine the source of the interrupt by checking the interrupt flag and the interrupt enable for each source that is to be processed and make the relevant interrupt code conditional on those being set. These checks can be placed in any order, but those appearing earlier in the ISR will be processed sooner, potentially improving response times.
At the appropriate point in your main-line 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.
TMR0IE
) and the interrupt flag bit (e.g., TMR0IF
).
Checking the interrupt enable flag is required since interrupt flags associated with a
peripheral can be asserted even if the peripheral is not configured to generate an
interrupt.void __interrupt(high_priority) tcInt_high(void) {
if (TMR0IE && TMR0IF) { // any timer 0 interrupts?
TMR0IF=0;
++tick_count;
}
if (TMR1IE && TMR1IF) { // any timer 1 interrupts?
TMR1IF=0;
tick_count += 100;
}
// process other interrupt sources here, if required
return;
}
void __interrupt(low_priority) tcInt_low(void) {
if (TMR2IE && TMR2IF) {
TMR2IF = 0;
tick_count -= 10;
}
}
For devices with the VIC module that are operating in Legacy mode, some features of the hardware interrupt vector tables are still usable. The above information applies to these ISRs, however, the following options are also available.
Consider how many hardware IVTs you require. Write at most two interrupt functions per
vector table to link to the high- and low-priority vectors, using any one of
low_priority
or __low_priority
; or
high_priority
or __high_priority
. Consider
implementing both interrupt functions for each table, to handle accidental triggering of
unused interrupts, or use the -mundefints
option to provide a default
action (see Undefints Option).
If desired, define a single char
parameter instead of using a
void
parameter list with the ISR. Inside the interrupt function,
determine the source of the interrupt using that parameter. It is recommended, however,
that the parameter list be set to void
if you want to ensure portability
with devices that do not have the VIC module.
In your project, set the MVECEN
configuration bit to OFF
.
If required, at the appropriate points in your code, select the required set of dual
vectors by writing to the IVTBASE
registers.
base()
or __base()
argument allows the ISR's base address to be specified. Note that this specifies the base
address of the vector table, not the address of a vector within that table. Thus, the
following shortened example shows the above function prototypes redefined for a table base
address of 0x2000. These ISRs will be linked to vector addresses 0x2008 and
0x2018.void __interrupt(__base(0x2000), __high_priority) tcInt_high(void)
{...}
void __interrupt(__base(0x2000), __low_priority) tcInt_low(void)
{...}
void __interrupt(__base(0x1000), __high_priority) tcInt_high_alt(void)
{...}
void __interrupt(__base(0x1000), __low_priority) tcInt_low_alt(void)
{...}
To make use of the ISRs above, the IVTBASE
register must
be written with either 0x1000 or 0x2000 at runtime to select which IVT is currently active.
Never write the IVTBASE
registers if interrupts are enabled. The initial
vector table can also be selected by using the -mivt
option (see Ivt Option).IRQ_TMR0
. To see the symbols applicable to
your device, open the or pic18_chipinfo.html file (located in the
docs directory of your compiler’s installation directory) in your
web browser and select your target
device.void __interrupt(__high_priority) tcInt_high(unsigned char src) {
switch(src) {
case IRQ_TMR0:
TMR0IF=0;
++tick_count;
break;
case IRQ_TMR1:
TMR1IF=0;
tick_count += 100;
break;
// process other interrupt sources in cases here, if required
}
return;
}