Jump to content


View Other Content



Search Articles



Recent Comments


- - - - -

PIC18 Interrupts




Introduction to Interrupts

PIC18 microcontrollers have high priority and low priority interrupts. When a high or low priority interrupt occurs, the program counter is loaded with the address of the respective interrupt vector and the program branches to the interrupt vector. The high priority interrupt vector is 8h, and the low priority interrupt vector is 18h.

The ten registers used to control interrupts on the PIC18F2455/2550/4455/4550 are:
  • RCON – Reset Control register
    • This register contains the interrupt priority enable bit (IPEN) along with some other bits that can be used to determine why a device reset occurred.
  • INTCON, INTCON2, INTCON3 – Interrupt control registers
    • These registers contain the GIEH and GEIL bits, used to enable interrupts, along with interrupt enable, priority, and flag bits for various interrupt sources.
  • PIR1, PIR2 – Peripheral Interrupt Request registers
    • The PIR registers contain the interrupt flags for the “peripheral” interrupt sources, which include: The ADC, UART, CCP, USB, TMR3, TMR2, TMR1, and TMR0 modules.
  • PIE1, PIE2 – Peripheral Interrupt Enable registers
    • The PIE registers contain the enable bits for the peripheral interrupt sources. Setting a bit in the register enables the associated interrupt source, while clearing a bit disables interrupt for that source.
  • IPR1, IPR2 – Interrupt Priority Registers
    • If the IPEN bit is set then interrupt priority is enabled and these registers are used to set the priority of the individual interrupt sources. Setting a source’s associated IPR bit makes that source a high priority interrupt, while clearing it makes it low priority.
Every interrupt source has three bits in the above registers (three bits total, not three in each register..) which are used to set the interrupt source’s priority to high or low, enable or disable interrupts for the source, and to set or clear the interrupt flag for the source. (note: clear the interrupt flag before enabling interrupts for it!)


Interrupt Priority

When interrupt priority is enabled, all enabled interrupts must be configured as "high priority" or "low priority." Interrupt priority allows a more important interrupt (high priority) to interrupt a not-as-important interrupt (low priority). In most cases, interrupt priority is not needed and is not recommended because of performance consequences. This great PIC FAQ says the following:

Quote

Should I use interrupt priorities on the PIC18?
Probably not. ... The disadvantages to using interrupt priorities are many:
  • More interrupt setup code
  • More complex ISR declaration
  • Higher latency for high-priority interrupts due to the necessary springboard GOTO
  • Much higher latency for low-priority interrupts since they can't use the fast register stack
  • Increased code space usage due to all of the above.
  • More hardware and software stack space is required.
Even ignoring the disadvantages of interrupt priorities, few applications actually need them to perform correctly. A well-written interrupt handler can process all of its interrupts quickly enough to prevent any interrupts from being lost.


A typical example of a situation that needs interrupt priorities is high-speed clock coming in on an INT pin that has very tight timing requirements. In such a case, it makes sense to enable interrupt priorities, and configure the INT interrupt as the only high-priority interrupt. Using multiple high-priority interrupts ... is generally pointless.

If interrupt priority is enabled, because the high interrupt vector is small in size, usually a goto instruction is placed in the interrupt vector to call the actual interrupt handler. If a high priority interrupt occurs while the low priority interrupt service routine (ISR) is being executed, the program will branch to the high priority ISR and return when it’s done.

Interrupt diagram:

Posted Image


Using interrupts

1. Set interrupt priority

The Interrupt Priority Enable (IPEN) bit determines whether the Interrupt Priority feature is enabled or not. The GIE/GIEH bit and the PEIE/GEIL bit have different meanings depending on the IPEN bit. Set

2. Enable unmasked interrupts

When IPEN = 1:
Interrupt priority is enabled and GIEH and GIEL allow you to enable high priority interrupts, low priority interrupts, or both. A source’s interrupt priority is determined by its associated bit in the IPR registers.

Set the following bits:

GIEH (INTCON<7>) – Global Interrupt Enable High: enables high priority interrupts (1), or disables interrupts (0).
GIEL (INTCON<6>) ­­­– Global Interrupt Enable Low: enables low priority interrupts (1), or disables low priority interrupts (0).

When IPEN = 0:
Interrupt priority is disabled; the interrupt priority registers become irrelevant and all interrupts branch to the high priority interrupt vector.

Set the following bits:

GIE (INTCON<7>) – Global Interrupt Enable: enables interrupts (1), or disables interrupts (0).
PEIE (INTCON<6>) ­­­– Peripheral Interrupt Enable: enables peripheral interrupts (1), disables peripheral interrupts (0).

3. Unmask interrupts

Now that interrupts are enabled, and interrupt priority has been configured, it's time to unmask (enable) individual interrupt sources. This is done by setting the interrupt source's enable bit, which is located in either INTCON, INTCON2, INTCON3, PIE1, or PIE2. Clear the source's interrupt flag before enabling it.

4. Set interrupt source priorities

If interrupt priority is enabled, then each interrupt source must have its interrupt priority bit set to either 1 (high priority) or 0 (low priority). This step is not necessary if interrupt priority is not enabled.

The Interrupt Service Routine
As previously mentioned, the low priority and high priority interrupt vectors are located at 8h and 18h respectively. The MPLAB C18 C Compiler User’s Guide shows how to use the #pragma statement to put code in the interrupt vectors. Here’s an example of how you might write your interrupt service routines.

#pragma code isr = 0x08 // Store the below code at address 0x08
#pragma interrupt isr
 
void isr (void)
{
    // interrupt code
}
 
#pragma code // Return to the default code section

The #pragma code statement followed by an address tells the compiler that the code to follow needs to be stored in a specific location in memory – in this case, the interrupt vector. Inside the interrupt vector resides our Interrupt Service Routine (ISR), which is where the interrupt handling occurs. Once inside the interrupt service routine, check the interrupt flags for unmasked interrupt sources to see which one is set, thus determining which source interrupted. Make sure to clear the interrupt flag before exiting the ISR!

A full example - using interrupts with the PIC18 USART module
#include "p18f4550.h"
 
/***************************
*   Device configuration   *
***************************/
#pragma config FOSC = HSPLL_HS	// Using 20 MHz crystal with PLL
#pragma config PLLDIV = 5 		// Divide by 5 to provide the 96 MHz PLL with 4 MHz input
#pragma config CPUDIV = OSC1_PLL2 // Divide 96 MHz PLL output by 2 to get 48 MHz system clock
#pragma config FCMEN = OFF // Disable Fail-Safe Clock Monitor
#pragma config IESO = OFF  // Disable Oscillator Switchover mode
#pragma config PWRT = OFF  // Disable Power-up timer
#pragma config BOR = OFF   // Disable Brown-out reset
#pragma config WDT = OFF   // Disable Watchdog timer
#pragma config MCLRE = ON  // Enable MCLR Enable
#pragma config LVP = OFF   // Disable low voltage ICSP
#pragma config ICPRT = OFF // Disable dedicated programming port (only on 44-pin devices)
#pragma config CP0 = OFF   // Disable code protection
 
// Function declarations
void config(void);
 
void main (void)
{
    config();
    
    while(1)
    {
        // program loop
    }    
}
 
void config (void)
{
    // USART configuration
    TXSTAbits.TX9 = 0;  // 8-bit transmission
    TXSTAbits.TXEN = 1; // transmit enabled
    TXSTAbits.SYNC = 0; // asynchronous mode
    TXSTAbits.BRGH = 1; // high speed
    RCSTAbits.SPEN = 1; // enable serial port - configures RX/DT and TX/CK pins as serial port pins
    RCSTAbits.RX9 = 0;  // 8-bit reception
    RCSTAbits.CREN = 1; // enable receiver
    BAUDCONbits.BRG16 = 0; // 8-bit baud rate generator
    SPBRG = 25; // write the decimal value 25 to the baud rate generator - value calculated using formula from table 20-1 in the PIC18F4550 datasheet
    
    TRISCbits.RC6 = 1;
    TRISCbits.RC7 = 1;
    
    // interrupts / USART interrupts configuration
    RCONbits.IPEN = 0;   // disable interrupt priority
    INTCONbits.GIE = 1;  // enable interrupts
    INTCONbits.PEIE = 1; // enable peripheral interrupts.
    PIE1bits.RCIE = 1; // enable USART receive interrupt
}
 
// start ISR code
#pragma code isr = 0x08 // store the below code at address 0x08
#pragma interrupt isr
 
void isr (void)
{
    if(PIR1bits.RCIF == 1) // if the USART receive interrupt flag has been set
    {
        if(PIR1bits.TXIF == 1) // check if the TXREG is empty
        {
   	     TXREG = RCREG; // echo received data back to sender
        }
    }    
}
 
#pragma code // return to the default code section
// end ISR code



0 Comments


or Sign In