Amicus18 Interrupts made easy A lot of users find interrupts particularly confusing, and in truth, Microchip have not helped in this assumption with their bewildering array of devices, registers and bits that require setting, clearing or reading, each of which usually change from device to device. However, the problems associated with the sentence above are circumvented when using the Amicus18 platform, as it implements a maximum of 2 powerfull microcontroller devices, each with a known format and a known set of registers and bits. This has allowed the creation of an interrupt managment mechanism that makes using interrupts as easy as falling of a log. With the PIC18F25K20 device that the Amicus18 comes equiped with as standard, there are 20 ways to trigger an interrupt, the PIC18F25K22 device has an additional 14 interrupt triggers, making a total of 34. Keeping track of all these trigger methods and actually implementing them can be tricky with any microcontroller language. But with the inclusion of the Interrupt Manager include file in your program, life gets easier. Take a look at the simple program below: ' ' Demonstrate the use of the interrupt manager ' $define Handle_TMR0 ' Handle a Timer0 overflow interrupt Include "Interrupt_Manager.inc" ' Load the interrupt manager into the program Include "Timers.inc" ' Load the Timer macros into the program GoTo Main ' Jump over the interrupt handler '-------------------------------------------------------------------- ' High Priority Hardware Interrupt Handler ' Interrupt's on a Timer0 Overflow ' Transmit text to the serial terminal ' ISR_TMR0(Start) HRSOut "Timer0\r" ' Display text ISR_TMR0(Exit) ' Exit the interrupt, clearing the Timer0 flag '-------------------------------------------------------------------- ' Main Program Loop ' Main: ' ' Configure Timer0 for: ' Clear TMR0L\H registers ' 16-bit operation ' Internal clock source ' 1:128 Prescaler ' OpenTimer0(TIMER_INT_OFF & T0_16BIT & T0_SOURCE_INT & T0_PS_1_128) Int_TMR0(Enable) ' Enable a Timer0 overflow interrupt Int_Global(Enable) ' Enable global interrupts Stop ' Stop the main program The program handles a timer0 overflow interrupt which transmits text to the serial terminal. It really doesn't get much simpler than that! The interrupt handler subroutine is fully managed, meaning you can use any BASIC command in it; within reason of course. Interrupt manager usage. The interrupt manager must be informad as to which interrupt it must handle. These are in the form of a pre-processor $define. There are 20 defines for the standard 18F25K20 microcontroller, and an addtional 14 for the 18F25K22 microcontroller. These are: ' ' 18F25K20 and 18F25K22 interrupt handler defines ' $define Handle_PORTB ' Handle a change on PORTB interrupt $define Handle_INT0 ' Handle an INT0 external event interrupt $define Handle_INT1 ' Handle an INT1 external event interrupt $define Handle_INT2 ' Handle an INT2 external event interrupt $define Handle_TMR0 ' Handle a Timer0 overflow interrupt $define Handle_TMR1 ' Handle a Timer1 overflow interrupt $define Handle_TMR2_Match ' Handle a Timer2 to PR2 match interrupt $define Handle_TMR3 ' Handle a Timer3 overflow interrupt $define Handle_ADC ' Handle an ADC completed interrupt $define Handle_RX1 ' Handle a USART1 byte received interrupt $define Handle_TX1 ' Handle a USART1 byte transmitted interrupt $define Handle_SSP1 ' Handle an SSP1 interrupt $define Handle_CCP1 ' Handle a CCP1 interrupt $define Handle_CCP2 ' Handle a CCP2 interrupt $define Handle_OSC ' Handle an Oscillator Fail interrupt $define Handle_COMP1 ' Handle a comparitor 1 interrupt $define Handle_COMP2 ' Handle a comparitor 2 interrupt $define Handle_FLASH ' Handle a flash or eeprom write interrupt $define Handle_BUS ' Handle an MSSP1 Bus Collision interrupt $define Handle_VOLTS ' Handle a Low Voltage Detected interrupt ' ' 18F25K22 Interrupt handler defines ' $define Handle_RX2 ' Handle a USART2 byte received interrupt $define Handle_TX2 ' Handle a USART2 byte transmitted interrupt $define Handle_SSP2 ' Handle an SSP2 interrupt $define Handle_BUS2 ' Handle an MSSP2 Bus Collision interrupt $define Handle_TMR1_Gate ' Handle a Timer1 gate interrupt $define Handle_TMR3_Gate ' Handle a Timer3 gate interrupt $define Handle_TMR5_Gate ' Handle a Timer5 gate interrupt $define Handle_CTMU ' Handle a CTMU (Charge Time Measurement Unit) interrupt $define Handle_CCP3 ' Handle a CCP3 interrupt $define Handle_CCP4 ' Handle a CCP4 interrupt $define Handle_CCP5 ' Handle a CCP5 interrupt $define Handle_TMR4_Match ' Handle a Timer4 to PR4 match interrupt $define Handle_TMR5 ' Handle a Timer5 overflow interrupt $define Handle_TMR6_Match ' Handle a Timer6 to PR6 match interrupt Of course, it is very doubtful that a program will be created that uses all of the interrupt triggers, so you must choose which ones are appropriate for the program and only issue those. Note that the handler defines must be issued in the program before the interrupt manager include file is loaded, so that it can pick up its defines. Each handler define has an associated handler subroutine that must be used in the program. each handler subroutine has a start and end wrapper. These are: ISR_PORTB(pStart or pExit) ' PORTB change interrupt handling subroutine wrapper ISR_INT0(pStart or pExit) ' INT0 interrupt handling subroutine wrapper ISR_INT1(pStart or pExit) ' INT1 interrupt handling subroutine wrapper ISR_INT2(pStart or pExit) ' INT2 interrupt handling subroutine wrapper ISR_TMR0(pStart or pExit) ' Timer0 overflow interrupt handling subroutine wrapper ISR_TMR1(pStart or pExit) ' Timer1 overflow interrupt handling subroutine wrapper ISR_TMR2_Match(pStart or pExit) ' Timer2 to PR2 match interrupt handling subroutine wrapper ISR_TMR3(pStart or pExit) ' Timer3 overflow interrupt handling subroutine wrapper ISR_ADC(pStart or pExit) ' ADC interrupt handling subroutine wrapper ISR_RX1(pStart or pExit) ' UART1 receive interrupt handling subroutine wrapper ISR_TX1(pStart or pExit) ' UART1 transmit interrupt handling subroutine wrapper ISR_SSP(pStart or pExit) ' SSP1 interrupt handling subroutine wrapper ISR_CCP1(pStart or pExit) ' CCP1 interrupt handling subroutine wrapper ISR_CCP2(pStart or pExit) ' CCP2 interrupt handling subroutine wrapper ISR_OSC(pStart or pExit) ' Oscillator Fail interrupt handling subroutine wrapper ISR_COMP1(pStart or pExit) ' Comparitor 1 interrupt handling subroutine wrapper ISR_COMP2(pStart or pExit) ' Comparitor 2 interrupt handling subroutine wrapper ISR_FLASH(pStart or pExit) ' Eeprom or Flash memory write interrupt handling subroutine wrapper ISR_BUS(pStart or pExit) ' MSSP 1 bus collision interrupt handling subroutine wrapper ISR_VOLTS(pStart or pExit) ' Low Voltage interrupt handling subroutine wrapper 18F25K22 subroutine wrappers ISR_RX2(pStart or pExit) ' UART2 receive interrupt handling subroutine wrapper ISR_TX2(pStart or pExit) ' UART2 transmit interrupt handling subroutine wrapper ISR_SSP2(pStart or pExit) ' SSP2 interrupt handling subroutine wrapper ISR_BUS2(pStart or pExit) ' MSSP 2 bus collision interrupt handling subroutine wrapper ISR_TMR1_Gate(pStart or pExit) ' Timer1 gate interrupt handling subroutine wrapper ISR_TMR3_Gate(pStart or pExit) ' Timer3 gate interrupt handling subroutine wrapper ISR_TMR5_Gate(pStart or pExit) ' Timer5 gate interrupt handling subroutine wrapper ISR_CTMU(pStart or pExit) ' CTMU (Charge Time Measurement Unit) interrupt handling subroutine wrapper ISR_CCP3(pStart or pExit) ' CCP3 interrupt handling subroutine wrapper ISR_CCP4(pStart or pExit) ' CCP4 interrupt handling subroutine wrapper ISR_CCP5(pStart or pExit) ' CCP5 interrupt handling subroutine wrapper ISR_TMR4_Match(pStart or pExit) ' Timer4 to PR4 match interrupt handling subroutine wrapper ISR_TMR5(pStart or pExit) ' Timer5 overflow interrupt handling subroutine wrapper ISR_TMR6_Match(pStart or pExit) ' Timer6 to PR6 match interrupt handling subroutine wrapper USe ISR_xxxx(Start) at the beginning of the interrupt handler, and ISR_xxxx(Exit) at its end. I know there seems a lot, but you will probably not use most of them. So to create a Timer0 overflow interrupt do the following: Place the handler define at the top of the program: $define Handle_TMR0 Add the interrupt manager macros into the program: Include "Interrupt Manager.inc" Add the timer macros into the program: Include "Timers.inc" Jump over the handler to the main program Goto Main Inform the compiler where to start the interrupt handler subroutine for a timer0 overflow: ISR_TMR0(Start) The text "Start" informs the compiler that this is the beginning of the handler subroutine. Write your interrupt code..... Inform the compiler where the interrupt handler ends. Notice the parameter is now "Exit": ISR_TMR0(Exit) This also resets the interrupt's associated flag. i.e. for timer0 it will reset TMR0IF. Place the label for the main program: Main: Configure timer0 OpenTimer0(TIMER_INT_OFF & T0_16BIT & T0_SOURCE_INT & T0_PS_1_128) The interrupt is now handled, timer0 is configured but the interrupt is not enabled. In order to do this, there are 36 options (20 for the 18F25K20, an extra 14 for the 18F25K22, and 2 for interrupts in general). These are listed below: Int_TMR0(pEnable or pDisable) ' Enable or Disable the Timer0 overflow interrupt Int_TMR1(pEnable or pDisable) ' Enable or Disable the Timer1 overflow interrupt Int_TMR2_Match(pEnable or pDisable) ' Enable or Disable the Timer2 to PR2 match interrupt Int_TMR3(pEnable or pDisable) ' Enable or Disable the Timer3 overflow interrupt Int_INT0(pEnable or pDisable) ' Enable or Disable the INT0 interrupt Int_INT1(pEnable or pDisable) ' Enable or Disable the INT1 interrupt Int_INT2(pEnable or pDisable) ' Enable or Disable the INT2 interrupt Int_CCP1(pEnable or pDisable) ' Enable or Disable the CCP1 interrupt Int_CCP2(pEnable or pDisable) ' Enable or Disable the CCP2 interrupt Int_PORTB(pEnable or pDisable) ' Enable or Disable the PORTB change interrupt Int_ADC(pEnable or pDisable) ' Enable or Disable the ADC interrupt Int_RX(pEnable or pDisable) ' Enable or Disable the UART1 receive interrupt Int_TX(pEnable or pDisable) ' Enable or Disable the UART1 transmit interrupt Int_SSP(pEnable or pDisable) ' Enable or Disable the SSP1 interrupt Int_OSC(pEnable or pDisable) ' Enable or Disable the Oscillator Fail interrupt Int_Comp1(pEnable or pDisable) ' Enable or Disable the Comparitor 1 interrupt Int_Comp2(pEnable or pDisable) ' Enable or Disable the Comparitor 2 interrupt Int_FLASH(pEnable or pDisable) ' Enable or Disable the Eeprom and Flash Write interrupt Int_BUS(pEnable or pDisable) ' Enable or Disable the MSSP1 Bus collision interrupt Int_VOLTS(pEnable or pDisable) ' Enable or Disable the Low Voltage interrupt For the 18F25K22 device Int_RX2(pEnable or pDisable) ' Enable or Disable the UART2 receive interrupt Int_TX2(pEnable or pDisable) ' Enable or Disable the UART2 transmit interrupt Int_SSP2(pEnable or pDisable) ' Enable or Disable the SSP2 interrupt Int_BUS2(pEnable or pDisable) ' Enable or Disable the MSSP2 Bus collision interrupt Int_TMR1_Gate(pEnable or pDisable) ' Enable or Disable the Timer1 Gate interrupt Int_TMR3_Gate(pEnable or pDisable) ' Enable or Disable the Timer3 Gate interrupt Int_TMR5_Gate(pEnable or pDisable) ' Enable or Disable the Timer5 Gate interrupt Int_CTMU(pEnable or pDisable) ' Enable or Disable the CTMU (Charge Time Measurement Unit) interrupt Int_CCP3(pEnable or pDisable) ' Enable or Disable the CCP3 interrupt Int_CCP4(pEnable or pDisable) ' Enable or Disable the CCP4 interrupt Int_CCP5(pEnable or pDisable) ' Enable or Disable the CCP5 interrupt Int_TMR6_Match(pEnable or pDisable) ' Enable or Disable the Timer6 to PR6 match interrupt Int_TMR5(pEnable or pDisable) ' Enable or Disable the Timer5 overflow interrupt Int_TMR4_Match(pEnable or pDisable) ' Enable or Disable the Timer4 to PR4 match interrupt Int_Peripheral(pEnable or pDisable) ' Enable or Disable peripheral interrupts Int_Global(pEnable or pDisable) ' Enable or Disable global interrupts For a timer0 overflow interrupt, choose from the list above Int_TMR0 with the parameter set to Enable. i.e. Int_TMR0(Enable). This has set the flag for a timer0 overflow interrupt, but the global interrupt mechanism is still not enabled. From the list above, choose Int_Global(Enable). So the whole program looks like: $define Handle_TMR0 Include "Interrupt Manager.inc" Include "Timers.inc" Goto Main ISR_TMR0(Start) ' Place your interrupt code here..... ISR_TMR0(Exit) Main: OpenTimer0(TIMER_INT_OFF & T0_16BIT & T0_SOURCE_INT & T0_PS_1_128) Int_TMR0(Enable) Int_Global(Enable) How long would the above program have taken without the aid of the interrupt manager? We'll now create a special event interrupt that triggers when CCP2 reached Timer3. Place the CCP2 event handler define at the top of the program: $define Handle_CCP2 Add the interrupt manager macros into the program: Include "Interrupt Manager.inc" Add the timer macros into the program: Include "Timers.inc" Create a 16-bit variable from registers CCP2L and CCP2H: Dim wCCP2 as CCP2L.Word Jump over the handler to the main program: Goto Main Inform the compiler where to start the interrupt handler subroutine for a CCP2 match: ISR_CCP2(Start) The text "Start" informs the compiler that this is the beginning of the handler subroutine. Write your interrupt code..... Inform the compiler where the interrupt handler ends.Notice the parameter is now "Exit": ISR_CCP2(Exit) This also resets the interrupt's associated flag. i.e. for CCP2 it will reset CCP2IF. Place the label for the main program: Main: Configure CCP2 for compare mode: CCP2CON = %00001011 Configure timer3: OpenTimer3(TIMER_INT_OFF & T3_16BIT_RW & T3_PS_1_8 & T3_SYNC_EXT_OFF & T3_SOURCE_INT & T3_SOURCE_CCP) Load registers CCP2L\H with the value to match. The values below will create an interrupt every 1000us (i.e. 1ms): wCCPR2 = (1000 / 8) * 16 Enable the CCP2 match interrupt: Int_CCP2(Enable) Enable global interrupts: Int_Global(Enable) Using the template above, the following program creates an interrupt every millisecond. Within the interrupt, a 32-bit variable is incremented. The foreground program transmits the millisecond count to the serial terminal: ' ' Create a Compare Interrupt using the Special Event mechanism ' Compares the 16-bit value of Timer3 with the contents of 16-bit registers CCPR2L\H ' Triggers the interrupt when Timer3 reaches the value held in CCPR2L\H ' Timer3 is automatically reset in hardware ' ' Uses the interrupt manager routines for easier use of high priority interrupts ' $define Handle_CCP2() ' Handle the CCP2 interrupt Include "Interrupt_Manager.inc" ' Load the interrupt manager routines into the program Include "Timers.inc" ' Include the Timer macros into the program Dim tTimerReady As Bit ' True if the millisecond variable has been incremented Dim dMilliSeconds As Dword ' Holds the incrementing 32-bit value Dim wCCPR2 As CCPR2L.Word ' Combine CCP2L\H into a 16-bit register '-------------------------------------------------------------------------------------------------- GoTo Main ' Jump over any subroutines '-------------------------------------------------------------------------------------------------- ' Special Event on CCP2 Interrupt Handler ' Increment the 32-bit variable dMilliSeconds ' Sets the flag tTimerReady when the counter has incremented. ' This must be reset in the main program ' The interrupt is configured to trigger every 1 millisecond ' ISR_CCP2(Start) ' Indicate the start of the CCP2 interrupt handler Inc dMilliSeconds ' Increment the counting variable tTimerReady = True ' Indicate that the millisecond timer has been updated ISR_CCP2(Exit) ' Exit the CCP2 interrupt handler '-------------------------------------------------------------------------------------------------- ' The main program loop starts here ' Main: CCP2CON = %00001011 ' Compare mode: trigger special event, reset timer, start A/D conversion on CCP2 match (CCP2IF bit is set) $define cPrescalerValue 8 ' Set the timer's prescaler value $if cPrescalerValue = 8 $define T3_Prescaler T3_PS_1_8 $elseif cPrescalerValue = 4 $define T3_Prescaler T3_PS_1_4 $elseif cPrescalerValue = 2 $define T3_Prescaler T3_PS_1_2 $elseif cPrescalerValue = 1 $define T3_Prescaler T3_PS_1_1 $endif ' ' Setup Timer3: ' Clear TMPR3L\H ' Interrupt disabled ' 16-bit read-write mode ' Internal clock source ' 1:n prescaler ' Don't sync external clock input ' Make Timer3 the source for a compare interrupt ' OpenTimer3(TIMER_INT_OFF & T3_16BIT_RW & T3_Prescaler & T3_SYNC_EXT_OFF & T3_SOURCE_INT & T3_SOURCE_CCP) $define cMicroSeconds 1000 ' Interrupt rate (in MicroSeconds) ' ' Calculate the value to place into the CCPR2L\H registers in order to achieve a certain interrupt rate (in us) ' $define cCCPR_Value $eval (cMicroSeconds / cPrescalerValue) * (_xtal / 4) $if cCCPR_Value > 65535 $error "Value too large for interrupt duration" $elseif cCCPR_Value = 0 $error "Value too small for interrupt duration" $endif dMilliSeconds = 0 ' Reset the millisecond counter wCCPR2 = cCCPR_Value ' Load CCPR2L\H with the value to trigger an interrupt at a certain duration Int_CCP2(Enable) ' Enable the CCP2 interrupt Int_Global(Enable) ' Enable Global Interrupts ' ' Display the millisecond counter running in the background ' While 1 = 1 ' Create an endless loop If tTimerReady = True Then ' Has the millisecond timer incremented? tTimerReady = False ' Yes. So reset the flag HRSOut Dec dMilliSeconds, 13 ' Transmit the ASCII value of the counter EndIf Wend ' Do it forever