18F Code Snippet - Interrupts Intro

    Interrupts are one of the most daunting tasks for many. The idea isn't as a simple If Else EndIf statement, but none the less, with the right guidance, they are very simple to use. If you want an instantaneous response to an action, no matter where/what the program is doing, then an interrupt is the way to go. The issue is the fact the program can be pulled from anywhere, anytime, and although it will return to where it was pulled from, system variables can be corrupted and timing sensitive commands such as USART.Write might be affected.

    18F PIC's interrupts are slightly different to that of 16F's, but the concept is the same. That is, no matter where the program pointer is, if an interrupt occurs that has been enabled, then leave that part of the program and service the interrupt. 18F's can have high and low interrupts (so one interrupt can have priority over another) and automatic context saving to name a couple of differences compared to 16F's. Don't get to concerned yet, as Swordfish handles every aspect.

    Lets kick off by using a simple TMR2 interrupt. I like to use TMR2 as it is a maintenance free interrupt that occurs at every X time interval. You set up the interval time by initializing the Pre and Post Scalers, then when the Timer reaches a known value, an interrupt occurs and its reset.

    Setting up is the same, and I am using a 20Mhz crystal, and want a 1mS interrupt. Using the PIC TMR2 calculator makes the settings easy to configure.

Pre-Scale = 1:4
PR2 = 249
Post-Scale = 1:5

    Referring to the 18F452 datasheet, and the Swordfish help files, I have made a program that will interrupt the program every mS, and increment a register that records mS.

Device = 18F452
Clock = 20

Dim TMR2IE As PIE1.1,         // TMR2 interrupt enable
    TMR2IF As PIR1.1,         // TMR2 overflow flag
    TMR2ON As T2CON.2,        // Enables TMR2 to begin incrementing
    mS As Word,               // mS register
    Last_mS As Word,          // mS polling register
    Signal_Pin As PORTB.0     // Signal output to frequency meter
    
    
Interrupt TMR2_Interrupt()
    Save(0)                   // Back up system variables
    If TMR2IF = 1 Then        // Check if the interrupt was from TMR2
        TMR2IF = 0            // Clear the TMR2 interrupt flag
        Inc(mS)               // Increment the mS counter
    EndIf                     //
    Restore                   // Restore system variables
End Interrupt

Private Sub TMR2_Initialize()
    TMR2ON = 0                // Disable TMR2
    TMR2IE = 0                // Turn off TMR2 interrupts    
    T2CON.0 = 1               //  00 = Prescaler is 1
    T2CON.1 = 0               //  01 = Prescaler is 4
                          //  1x = Prescaler is 16 
    PR2 = 249                 // TMR2 Period register PR2   
    T2CON.3 = 0               //  0000 = 1:1 postscale
    T2CON.4 = 0               //  0001 = 1:2 postscale
    T2CON.5 = 1               //  0010 = 1:3 postscale...
    T2CON.6 = 0               //  1111 = 1:16 postscale    
    TMR2 = 0                  // Reset TMR2 Value    
    TMR2IE = 1                // Enable TMR2 interrupts
    TMR2ON = 1                // Enable TMR2 to increment
    Enable(TMR2_Interrupt)
End Sub

// Start Of Program...
mS = 0                        // Reset mS register 
Last_mS = 0                   // Reset mS polling register

TMR2_Initialize               // Setup and enable TMR2
Low(Signal_Pin)               // Make the signal an output, and set it low


While 1 = 1                   // Create an infinite loop
    Repeat                    // Poll the mS register and wait for a change
    
    Until mS <> Last_mS       //
    
    Last_mS = mS              // Update the polling register
    
    Signal_Pin = 1            // Create a pulse for the frequency counter
    Signal_Pin = 0
Wend

    Notice that I put the TMR2 setup code in a separate sub routine. This is is good practice as you can simply copy and paste (or include the file) for future use. To call the setup sub, I just type the sub name as if it were a command "TMR2_Initialize".

    The Save(0) and Restore commands are used to ensure that the compilers system registries/variables are saved then restored, as in a larger program, you have no idea where the interrupt could occur, and you don't want to corrupt anything if something important was happening.

    The program will loop for ever, waiting for mS to change, and when it does, toggle a Pin high then low to act like a signal. This signal is for my frequency counter as shown below. Notice the output is exactly 1000Hz, as it should be.

 

Note the PIC's power supply/oscillator are not shown

Interrupt Considerations - Important

    There are VERY important considerations with interrupts. Mainly is the length of the interrupt, followed closely to backing up variables.

    An Interrupt can happen anywhere anytime. You can control this to an extent, but lets say an interrupt occurs during a timing specific command like USART.Write. This is a great one to look at as both timing and variable restoring is required.

    The command USART.Write will send serial data that is very timing sensitive down a single wire. Should an interrupt occur mid way through the command, then the serial data will "lag" out as the interrupt handler is called. The time the interrupt takes should be kept to a bare minimum to ensure that other commands/functions that are timing specific work correctly. Simple increments of registers, or setting of bits is ideal, do not use any commands that will "suck" up processing time.

    Also, as the USART.Write command also uses several system registers for string handling etc, if you use any such commands in your ISR (Interrupt Service Routine), then you MUST save the system registries, and the restore them to their original values (as shown in the above example).

    IMPORTANT: Interrupts with timers occur an exact number of cycles each time. Using Pre-Scalers and Post-Scalers ensures that you give your program enough time between interrupts to service the main program and complete the interrupt.
    My above program will create an interrupt every 1mS. With my PIC running at 5MIPS (20Mhz / 4 = 5 Million Instructions Per Second), that means I allow for 5000 instructions (5MIPS = 200nS Per instruction, therefore 1mS = 5000 instructions) to be executed between interrupts. This is both enough for my interrupt handler to complete, and for a very satisfactory amount of time within the main program between ISR's.

 Skip Navigation Links.

Collapse Site Tutorial IndexSite Tutorial Index
Expand 16F PIC Examples16F PIC Examples
Collapse 18F PIC Examples18F PIC Examples
LED's
Switches
Expand 7 Segment Displays7 Segment Displays
LCD's
Expand 7 Segment Displays7 Segment Displays
ADC
ADC (Another Example)
EEPROM's
DS1307
Expand RS232 and UARTRS232 and UART
DS18B20
External ADC
Hall Effect Sensor
Pulse Width Modulation (PWM)
Infrared UART
Swordfish Modules
Collapse Code SnippetsCode Snippets
SF Libraries
18F Transition Guide
Internal Oscillator
PLL
Structures
Multi Tasking
Expand Internal TimersInternal Timers
Collapse InterruptsInterrupts
Intro
Context Saving
PORTB
Priority
Expand Handy TipsHandy Tips