Interrupts on PIC18F252 Part 2 Interrupts Programming in C Language Programming interrupts in C language using XC8 compiler is significantly simplified compared to C18 compiler. This note explains the process. Use the old Lecture 5 note in case you are using C18 compiler instead of the XC8 currently available on the machines in the laboratory. In its most basic form setting up the interrupts under XC8 compiler is reduced to the use of the keyword interrupt before the function which should be called when an interrupt happens. This function is usually referred to as Interrupt Service Routine (ISR). In addition to this, interrupt needs to be configured (enabled) at the start of the program. Thus the three steps necessary when writing the interrupt based program outlined at the end of the previous lecture note are now reduced to two: 1. Writing the ISR. 2. Enabling desired interrupt(s) (by setting the appropriate interrupt enabling bits in various microcontroller registers). In the following example, simple, Timer 2, C program discussed in the previous lectures is modified to include the use of interrupts instead of explicit calls to delay() function. Thus, we will turn this ordinary C function into Timer 2 interrupt service routine (ISR) in two easy steps. As a reminder, here is the original program, discussed in the previous lectures.
// File Name: PICprog2.c // Purpose: // Second PIC program. // This version uses Timer 2 to generate a // square wave signal on the pin 0 of port B. // The frequency of this signal is determined // by the loading of Timer 2, i.e. by duration // of delay() function // B. Vuksanovic 01/09/2013 #include <p18f252.h> void delay(void) // set the Timer 2 to produce certain delay // by loading T2CON and PR2 registers T2CON = 0x49; PR2 = 0x7C; T2CONbits.TMR2ON = 1; // start the Timer 2 while(!pir1bits.tmr2if); // wait for timer flag T2CONbits.TMR2ON = 0; // stop the Timer 2 PIR1bits.TMR2IF = 0; // clear timer flag void main (void) TRISB = 0x00; // configure portb as output while(1) PORTBbits.RB0 = 1; // set RB0 to 1 delay(); // wait PORTBbits.RB0 = 0; // set RB0 to 0 delay(); // wait again and loop back Let us rewrite this function by making just one modification to it moving the task of toggling the RB0 pin from the main program to delay() function.
// File Name: PICprog3_1.c // Purpose: // Third PIC program. // This version demonstrates the first step of // writing an interrupt driven program. // Intended interrupt task - in this case toggling // the pin 0 of port B is moved from the main function // to Timer 2 function. // Note - this is not an interrupt driven program yet. // It's just a first step towards it. // B. Vuksanovic 01/09/2013 #include <p18f252.h> void delay(void) // set the Timer 2 to produce certain delay // by loading T2CON and PR2 registers T2CON = 0x49; PR2 = 0x7C; T2CONbits.TMR2ON = 1; // start the Timer 2 while(!pir1bits.tmr2if); // wait for timer flag if(portbbits.rb0 == 1) PORTBbits.RB0 = 0; else PORTBbits.RB0 = 1; // i.e. future interrupt task T2CONbits.TMR2ON = 0; // stop the Timer 2 PIR1bits.TMR2IF = 0; // clear timer flag void main (void) TRISB = 0x00; // configure portb as output while(1) delay(); // just wait
Before introducing the interrupts in this program, we will rewrite this program in order to include the future interrupt task (toggling pin 0 of port B) in the delay() function. Thus, lines from the above program PORTBbits.RB0 = 1; and PORTBbits.RB0 = 0; are replaced with the line PORTBbits.RB0 = ~PORTBbits.RB0; which has the same effect. This line is then moved from main() into delay() function (future ISR). Here is the modified version of the program. (Note there are no interrupts in the program yet.)
// File Name: PICprog3_2.c // Purpose: // Third PIC program. // This version demonstrates the first step of // writing an interrupt driven program. // Intended interrupt task - in this case toggling // the pin 0 of port B is moved from the main function // to Timer 2 function and a small modification to this task // introduced in order to simplify the overall code. // Note - this is not an interrupt driven program yet. // It's just a first step towards it. // B. Vuksanovic 01/09/2013 #include <p18f252.h> void delay(void) // set the Timer 2 to produce certain delay // by loading T2CON and PR2 registers T2CON = 0x49; PR2 = 0x7C; T2CONbits.TMR2ON = 1; // start the Timer 2 while(!pir1bits.tmr2if); PORTBbits.RB0 = ~PORTBbits.RB0; // do the "task" // i.e. future interrupt task // wait for timer flag T2CONbits.TMR2ON = 0; // stop the Timer 2 PIR1bits.TMR2IF = 0; // clear timer flag void main (void) TRISB = 0x00; // configure portb as output while(1) delay(); // just wait
Finally, word interrupt is inserted in front of the name of the function delay() to turn it into ISR. Name of the function is also appropriately changed to timer2_isr(). The actual ISR name makes no difference to program operation (common misconception amongst many students) but it is good to give the function some meaningful name. Timer 2 interrupt configuration lines are also added at the start of the program to obtain the complete, interrupt driven program. Here is the final version of this program. Explanations for interrupt configuration lines are provided in the code and the registers containing relevant bits are given at the end of this note.
// File Name: PICprog3_3.c // Purpose: // Third PIC program. // This is the interrupt driven "toggle RB0" program. // PICprog3_1 has been modified, by including all necessary // Timer 2 interrupt configurations. // B. Vuksanovic 01/09/2013 #include <p18f252.h> // Timer 2 Interrupt Service Routine void interrupt timer2_isr(void) T2CONbits.TMR2ON = 0; // stop the Timer 2 // do the interrupt "task" // i.e. change the state of RB0 pin // to generate the square wave on RB0 PORTBbits.RB0 = ~PORTBbits.RB0; T2CON = 0x49; PR2 = 0x7C; PIR1bits.TMR2IF = 0; T2CONbits.TMR2ON = 1; // set Timer 2 to produce // 1 ms delay @ 20 MHz // clear Timer 2 interrupt flag // start Timer 2 again void main (void) TRISB = 0x00; // configure Port B pins for output PORTB = 0; // initialise RB0 to 0 RCONbits.IPEN = 1; // enable interrupt priority scheme IPR1bits.TMR2IP = 1; // configure TMR2 interrupt to high priority PIR1bits.TMR2IF = 0; // clear TMR2 interrupt flag INTCONbits.GIEH = 1; // enable high-priority interrupts PIE1bits.TMR2IE = 1; // enable TMR2 overflow interrupt T2CON = 0x49; // set Timer 2 to produce PR2 = 0x7C; // 1 ms delay @ 20 MHz T2CONbits.TMR2ON = 1; // start Timer while(1) // wait for interrupts in infinite loop // some other useful work can now be done here :-)
Following some suggestions from students during the lecture, improved version of this program is given below. Try to understand and check those modifications. // File Name: PICprog3_4.c // Purpose: // Third PIC program. // This is the interrupt driven "toggle RB0" program. // PICprog3_1 has been modified, by including all necessary // Timer 2 interrupt configurations. // B. Vuksanovic 01/09/2013 #include <p18f252.h> // Timer 2 Interrupt Service Routine void interrupt timer2_isr(void) T2CONbits.TMR2ON = 0; // stop the Timer 2 // do the interrupt "task" // i.e. change the state of RB0 pin // to generate the square wave on RB0 PORTBbits.RB0 = ~PORTBbits.RB0; PIR1bits.TMR2IF = 0; T2CON = 0x4D; // clear Timer 2 interrupt flag // set Timer 2 to produce // 1 ms delay @ 20 MHz // and restart Timer 2 at the same time void main (void) TRISB = 0x00; // configure Port B pins for output PORTB = 0; // initialise RB0 to 0 RCONbits.IPEN = 1; // enable interrupt priority scheme IPR1bits.TMR2IP = 1; // configure TMR2 interrupt to high priority PIR1bits.TMR2IF = 0; // clear TMR2 interrupt flag INTCONbits.GIEH = 1; // enable high-priority interrupts PIE1bits.TMR2IE = 1; // enable TMR2 overflow interrupt T2CON = 0x4D; // set Timer 2 to produce PR2 = 0x7C; // 1 ms delay @ 20 MHz // and start the Timer 2 at the same time while(1) // wait for interrupts in infinite loop // some other useful work can now be done here :-)
To do the task of configuring interrupts properly, we also need to have some understanding and knowledge of SFRs related to interrupts operation on PIC18F252 microcontroller. Following few pages list majority of the SFRs (Special Function Registers) involved in interrupt configuration and operation on PIC18F252 microcontroller with some explanations of relevant bits. (Copied from Microchip s PIC18Fxx2 Data Sheet). There are even more registers related to interrupts on PIC18F252, but we will not need them to get our first (Timer 2) interrupt driven program running. More explanations are provided in the Lecture note no 6. T2CON: (TIMER2 CONTROL REGISTER) This register just configures the Timer 2 operation and has been discussed in earlier lectures. Timer 2 needs to be configured in the same way again so T2CON is listed here again for your reference. PR2: (TIMER 2 PERIOD REGISTER) Nothing too interesting here (Microchip did not even produce a detailed figure of this register). PR2 is just an 8-bit readable and writable register. It is the second register used for Timer 2 configuration. It is called Timer2 period register because Timer2 (when switched on) increments from 00h until it matches PR2 and then resets to 00h on the next increment cycle. So Timer 2 does not really overflow. More accurately, as explained in PIC18Fxx2 Data Sheet when Timer 2 reaches the count stored in PR2 TMR2 to PR2 match occurres. Result is still the same Timer 2 interrupt flag (TMR2IF bit from PIR1 register) is set.
PIR1: (PERIPHERAL INTERRUPT REQUEST (FLAG) REGISTER 1)
RCON: IPR1: (PERIPHERAL INTERRUPT PRIORITY REGISTER 1)
INTCON:
PIE1: