• PicŪ Basic


  • Proton Interrupts made easy for the Amicus18 platform

    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 powerful 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 off a log.



    Note that the latest version of the Proton compiler must be used for the interrupt manager code to run successfully.

    With the PIC18F25K20 device that the Amicus18 board comes equipped 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:

    Code:
    ' Demonstrate the use of the Proton interrupt manager with the Amicus18 board
    '
         Include "Amicus18.inc"                      ' Configure the compiler to use the Amicus18 board (18F25K20, 64MHz)
    $define Handle_TMR0                             ' Handle a Timer0 overflow interrupt
      
        Include "Interrupt_Manager.inc"             ' Load the interrupt manager into the program
        Include "Amicus18_Timers.inc"               ' Load the Amicus18 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 informed 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 Name
    Handler description
    $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_RX Handle a USART1 byte received interrupt
    $define Handle_TX Handle a USART1 byte transmitted interrupt
    $define Handle_SSP 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 Name Handler description
    $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. However, there is no restiction to the amount of interrupt handlers that can be used in a single program, as long as it's only one of each type. By that I mean that you couldn't have two timer0 overflow interrupts being handled in a single program,.

    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:

    18F25K20 and 18F25K22 subroutine wrappers
    Interrupt wrapper name
    Interrupt wrapper description
    ISR_PORTB(StartOrExit) PORTB change interrupt handling subroutine wrapper
    ISR_INT0(StartOrExit) INT0 interrupt handling subroutine wrapper
    ISR_INT1(StartOrExit) INT1 interrupt handling subroutine wrapper
    ISR_INT2(StartOrExit) INT2 interrupt handling subroutine wrapper
    ISR_TMR0(StartOrExit) Timer0 overflow interrupt handling subroutine wrapper
    ISR_TMR1(StartOrExit) Timer1 overflow interrupt handling subroutine wrapper
    ISR_TMR2_Match(StartOrExit)
    Timer2 to PR2 match interrupt handling subroutine wrapper
    ISR_TMR3(StartOrExit) Timer3 overflow interrupt handling subroutine wrapper
    ISR_ADC(StartOrExit) ADC interrupt handling subroutine wrapper
    ISR_RX(StartOrExit) UART1 receive interrupt handling subroutine wrapper
    ISR_TX(StartOrExit) UART1 transmit interrupt handling subroutine wrapper
    ISR_SSP(StartOrExit) SSP1 interrupt handling subroutine wrapper
    ISR_CCP1(StartOrExit) CCP1 interrupt handling subroutine wrapper
    ISR_CCP2(StartOrExit) CCP2 interrupt handling subroutine wrapper
    ISR_OSC(StartOrExit) Oscillator Fail interrupt handling subroutine wrapper
    ISR_COMP1(StartOrExit) Comparitor 1 interrupt handling subroutine wrapper
    ISR_COMP2(StartOrExit) Comparitor 2 interrupt handling subroutine wrapper
    ISR_FLASH(StartOrExit) Eeprom or Flash memory write interrupt handling subroutine wrapper
    ISR_BUS(StartOrExit) MSSP1 bus collision interrupt handling subroutine wrapper
    ISR_VOLTS(StartOrExit) Low Voltage interrupt handling subroutine wrapper
    18F25K22 subroutine wrappers
    Interrupt wrapper name Interrupt wrapper description
    ISR_RX2(StartOrExit) UART2 receive interrupt handling subroutine wrapper
    ISR_TX2(StartOrExit) UART2 transmit interrupt handling subroutine wrapper
    ISR_SSP2(StartOrExit) SSP2 interrupt handling subroutine wrapper
    ISR_BUS2(StartOrExit) MSSP2 bus collision interrupt handling subroutine wrapper
    ISR_TMR1_Gate(StartOrExit) Timer1 gate interrupt handling subroutine wrapper
    ISR_TMR3_Gate(StartOrExit) Timer3 gate interrupt handling subroutine wrapper
    ISR_TMR5_Gate(StartOrExit) Timer5 gate interrupt handling subroutine wrapper
    ISR_CTMU(StartOrExit) CTMU (Charge Time Measurement Unit) interrupt handling subroutine wrapper
    ISR_CCP3(StartOrExit) CCP3 interrupt handling subroutine wrapper
    ISR_CCP4(StartOrExit) CCP4 interrupt handling subroutine wrapper
    ISR_CCP5(StartOrExit) CCP5 interrupt handling subroutine wrapper
    ISR_TMR4_Match(StartOrExit) Timer4 to PR4 match interrupt handling subroutine wrapper
    ISR_TMR5(StartOrExit) Timer5 overflow interrupt handling subroutine wrapper
    ISR_TMR6_Match(StartOrExit)
    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 Amicus18 timer macros into the program:
    Include "Amicus18_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:

    For the 18F25K20 and 18F25K22 devices
    Interrupt name
    Interrupt description
    Int_TMR0(Enable_or_Disable) Enable or Disable the Timer0 overflow interrupt
    Int_TMR1(Enable_or_Disable) Enable or Disable the Timer1 overflow interrupt
    Int_TMR2_Match(Enable_or_Disable) Enable or Disable the Timer2 to PR2 match interrupt
    Int_TMR3(Enable_or_Disable) Enable or Disable the Timer3 overflow interrupt
    Int_INT0(Enable_or_Disable) Enable or Disable the INT0 interrupt
    Int_INT1(Enable_or_Disable) Enable or Disable the INT1 interrupt
    Int_INT2(Enable_or_Disable) Enable or Disable the INT2 interrupt
    Int_CCP1(Enable_or_Disable) Enable or Disable the CCP1 interrupt
    Int_CCP2(Enable_or_Disable) Enable or Disable the CCP2 interrupt
    Int_PORTB(Enable_or_Disable) Enable or Disable the PORTB change interrupt
    Int_ADC(Enable_or_Disable) Enable or Disable the ADC interrupt
    Int_RX(Enable_or_Disable) Enable or Disable the UART1 receive interrupt
    Int_TX(Enable_or_Disable) Enable or Disable the UART1 transmit interrupt
    Int_SSP(Enable_or_Disable) Enable or Disable the SSP1 interrupt
    Int_OSC(Enable_or_Disable) Enable or Disable the Oscillator Fail interrupt
    Int_COMP1(Enable_or_Disable) Enable or Disable the Comparitor 1 interrupt
    Int_COMP2(Enable_or_Disable) Enable or Disable the Comparitor 2 interrupt
    Int_FLASH(Enable_or_Disable) Enable or Disable the Eeprom and Flash Write interrupt
    Int_BUS(Enable_or_Disable) Enable or Disable the MSSP1 Bus collision interrupt
    Int_VOLTS(Enable_or_Disable) Enable or Disable the Low Voltage interrupt
    For the 18F25K22 device
    Interrupt name
    Interrupt description
    Int_RX2(Enable_or_Disable) Enable or Disable the UART2 receive interrupt
    Int_TX2(Enable_or_Disable) Enable or Disable the UART2 transmit interrupt
    Int_SSP2(Enable_or_Disable) Enable or Disable the SSP2 interrupt
    Int_BUS2(Enable_or_Disable) Enable or Disable the MSSP2 Bus collision interrupt
    Int_TMR1_Gate(Enable_or_Disable) Enable or Disable the Timer1 Gate interrupt
    Int_TMR3_Gate(Enable_or_Disable) Enable or Disable the Timer3 Gate interrupt
    Int_TMR5_Gate(Enable_or_Disable)
    Enable or Disable the Timer5 Gate interrupt
    Int_CTMU(Enable_or_Disable) Enable or Disable the CTMU (Charge Time Measurement Unit) interrupt
    Int_CCP3(Enable_or_Disable) Enable or Disable the CCP3 interrupt
    Int_CCP4(Enable_or_Disable) Enable or Disable the CCP4 interrupt
    Int_CCP5(Enable_or_Disable) Enable or Disable the CCP5 interrupt
    Int_TMR6_Match(Enable_or_Disable) Enable or Disable the Timer6 to PR6 match interrupt
    Int_TMR5(Enable_or_Disable) Enable or Disable the Timer5 overflow interrupt
    Int_TMR4_Match(Enable_or_Disable) Enable or Disable the Timer4 to PR4 match interrupt
    General Interrupt enable or disable
    Int_Peripheral(Enable_or_Disable) Enable or Disable peripheral interrupts
    Int_Global(Enable_or_Disable) 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:
    Code:
        Include "Amicus18.inc"                      ' Configure the compiler to use the Amicus18 board (18F25K20, 64MHz)
    $define Handle_TMR0                             ' Handle a Timer0 overflow interrupt
      
        Include "Interrupt_Manager.inc"             ' Load the interrupt manager into the program
        Include "Amicus18_Timers.inc"               ' Load the Amicus18 timer macros into the program
      
        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
    Timer3 reaches the value held in registers CCP2L and CCP2H.

    Configure the Proton compiler to use the Amicus18 board which contains an 18F25K20 operating at 64MHz
    :
    Include "Amicus18.inc"


    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 Amicus18 timer macros into the program:
    Include "Amicus18_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 compare:
    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 = 001011
    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 compare 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:
    Code:
    '
    ' 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
    '
         Include "Amicus18.inc"                          ' Configure the compiler to use the Amicus18 board (18F25K20, 64MHz) 
    $define Handle_CCP2                                 ' Handle the CCP2 interrupt
        
        Include "Interrupt_Manager.inc"                 ' Load the interrupt manager routines into the program
        Include "Amicus18_Timers.inc"                    ' Load the Amicus18 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 = 001011                              ' 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 compare 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

    The interrupt manager include file along with three demo programs can be downloaded from here: Interrupt Manager.zip
    It can also be found in the compiler's "Samples\Amicus18_Board" folder,

    If the interrupt manager include file is to be used by other BASIC programs, place it in the compiler's "Sources" folder. Its default location is "C:\Program Files\ProtonIDE\Includes\Sources", or for Windows 7 64-bit "
    "C:\Program Files (x86)\ProtonIDE\Includes\Sources".
  • Recent Activity

    Maxi-15375

    Driving SK9822 or APA102 RGB LED's

    Thread Starter: rcurl

    I've been trying to figure out how to drive SK9822 or APA102 RGB LED's using the MSSP module in a PIC16F1503 but I'm getting nowhere. Part of the...

    Maxi Today, 17:07 Go to last post