5.9 Interrupts

The MPLAB XC8 compiler incorporates features allowing interrupts to be fully handled from C code. Interrupt functions are often called Interrupt Service Routines, or ISRs.

The operation of interrupts is handled differently by the different device families. Most Baseline devices do not implement interrupts at all; Mid-range devices have one vector location which is linked to all interrupt sources; some PIC18 devices have two independent interrupt vectors, one assigned to low-priority interrupt sources, the other to high-priority sources; and some PIC18 devices implement a vectored interrupt controller (VIC) module with support for one or more interrupt vector tables (IVTs), which can be populated with the addresses of high- or low-priority interrupt functions.

The operation of the IVT on devices with a VIC module can be disabled by clearing the MVECEN configuration bit. The device is then said to be operating in legacy mode, operating with dual priorities and dual vector locations. This bit is also used by the compiler to determine how interrupt functions should be programmed, so ensure it is set to OFF for legacy operation. Although the vector table is disabled in this mode, the vector locations are still relocatable. By default the vector location will be 0x8 and 0x18, the same for regular PIC18 devices without the VIC module.

The priority scheme implemented by PIC18 devices can also be disabled by clearing the IPEN SFR bit. Such devices are then said to be operating in Mid-range compatibility mode and utilize only one interrupt vector, located at address 0x8.

The following are the general steps you need to follow to use interrupts. More detail about these steps is provided in the sections that follow.

For Enhanced Baseline devices with interrupts, Mid-range devices, or PIC18 devices operating in Mid-range compatibility mode:

  • Write one interrupt function to process all interrupt sources.
  • At the appropriate point in your main-line code, unmask the interrupt sources required by setting their interrupt enable bit in the corresponding SFR.
  • At the appropriate point in your code, enable the global interrupt enable to allow interrupts to be generated.

For PIC18 devices without the VIC module, or PIC18 devices operating in legacy mode:

  • Plan the priorities to be assigned to each interrupt source. If the device is operating in legacy mode, determine the number of sets of dual interrupt vectors you require.
  • Program the MVECEN configuration bit if appropriate. A vector table will be encoded by the compiler if this bit is not set to OFF for devices with the VIC module.
  • Write one interrupt function to process each priority being used. You can define at most two interrupt functions, or two interrupt functions per vector set for devices operating in legacy mode. Consider implementing both interrupt functions to handle accidental triggering of unused interrupts, or use the -mundefints option to provide a default action (see 4.6.1.26 Undefints Option).
  • Write code to assign the required priority to each interrupt source by writing the appropriate bits in the SFRs.
  • If the device is operating in legacy mode and if required, at the appropriate points in your code, select the required set of dual vectors by writing to the IVTBASE registers. Never write the IVTBASE registers if interrupts are enabled. The initial vectors can also be selected by using the -mivt option (see 4.6.1.12 Ivt Option).
  • At the appropriate point in your code, enable the interrupt sources required.
  • At the appropriate point in your code, enable the global interrupt enable.

For devices using the VIC module:

  • Plan the priorities associated with each interrupt source and determine the number of interrupt vector tables you require.
  • Write as many interrupt functions as required. For fast interrupt response times, write a dedicated function for each interrupt source, although multiple sources can be processed by one function, if desired. Consider one or more additional functions to handle accidental triggering of unused interrupt sources, or use the -mundefints option to provide a default action (see 4.6.1.26 Undefints Option).
  • Write code to assign the required priority to each interrupt source by writing the appropriate bits in the SFRs.
  • If you are using more than one interrupt vector table, at the appropriate points in your code, select the required IVT by writing to the IVTBASE registers. Never write the IVTBASE registers if interrupts are enabled. The initial IVT can also be selected by using the -mivt option (see 4.6.1.12 Ivt Option).
  • At the appropriate point in your code, enable the interrupt sources required.
  • At the appropriate point in your code, enable the global interrupt enable.

Interrupt functions must not be called directly from C code (due to the different return instruction that is used), but interrupt functions can call other functions, both user-defined and library functions.

Interrupt code is the name given to any code that executes as a result of an interrupt occurring, including functions called from the ISR and library code. Interrupt code completes at the point where the corresponding return from interrupt instruction is executed. This contrasts with main-line code, which, for a freestanding application, is usually the main part of the program that executes after Reset.