IOC Interrupts
Interrupt On Change interrupts induce a Flag whenever a PORT Pin changes state from low-to-high, and/or from high-to-low. Some PICs have generous IOCs while many limit them to PORTB/C.
Older PICs use a generic Read Only IOC Interrupt Flag (IOCIF). Individual PORT bits in the IOCB/C Registers, and IOCIE in the INTCONx Register must be Enabled (Set, = 1). The PIC18FxxK22 is a popular example.
More recent PIC versions allow virtually any Input pin to be configured as an IOC, with polarity control. In fact, the PIC18FxxK40 will allow an IOC interrupt, with Rising Edge AND/OR Falling Edge trigger on EVERY PORT PIN (including MCLR when configured as an Input)! This means you can generate an IOCIF when the PORT goes Positive, and generate an IF when it goes Negative selectively; and you can assign BOTH Rising AND Falling.
IOC is similar to INT0/1/2 in that it is triggered by a change in the logic state of the PORT Pin. The PIC18FxxK50 allows control over Rising/Falling edge trigger over the INTx Interrupts, but any change in logic state on the IOC pins will trigger an IOC interrupt.
IOC may be any of the following:
- Interrupt on any logic state change (High-to-Low, Low-to-High; doesn’t matter)
- Interrupt on a Positive transition (Low-to-High)
- Interrupt on a Negative transition (High-to-Low)
- Configurable as either Positive, or Negative, or both
Configuring SFRs for IOC
Without exception, the Global Interrupt Enable (GIE) bit of the INTCON Register must be set (= 1), and usually the Peripheral Interrupt Enable (PIE) bit must also be set (= 1). The Interrupt On Change Interrupt Enable (IOCIE) bit must be set, along with the individual PORT Pin Interrupt Enable (IOCB, IOCC).
For the PIC18FxxK40, you must set the associated IOCxN bit (IOCAN, IOCBN, IOCCN…) for a Negative Edge trigger and/or the IOCxP bit for a Positive Edge trigger.
Registers associated with IOC:
- Global Interrupt Enable (GIE, often INTCON.7)
- Peripheral Interrupt Enable (PIE, often INTCON.6)
- IOCA, IOCB, IOCC, IOCD… (Individual bits correspond with individual PORT pins)
- IOCIP Interrupt Priority bit, INTCONx Register (1 = High Priority)
- IOCAP, IOCBP, IOCCP… Positive Edge Trigger bits
- IOCAN, IOCBN, IOCCN… Negative Edge Trigger bits
- IOCAF, IOCBF, IOCCF… Individual IOC Flags for individual PORT Pin changes
Handling an IOC Interrupt
For older PICs, the IOCIF merely indicates one of the Enabled IOC PORT Pins have changed logic state.
Within the Interrupt Handler you must read the PORTs to determine which of the Enabled pins triggered the Interrupt. Even if you only have one IOC pin Enabled, the method of clearing the IF is often by reading the PORT.
When a global IOCIF is implemented along with individual PORT IOC flags, the global IF is often Read Only. The method of clearing this Flag is to clear the individual IOC[PORT] Flag(s). The global IF indicates one of the individual IOCIFs are set. Clearing all individual IOCIFs clear the global IF.
With newer PICs, you will only get one single IOCAF (IOCBF, IOCCF…) type Flag, which is specific to the PORT Pin that changed logic state. Clearing that Flag is all that’s needed.
Whether using “ON INTERRUPT GOTO [Label]”, ASM Interrupts, or DT_INTs, older PICs require special attention when handling IOC Interrupts. (Newer PICs with IOCAF type Flags can be treated like any other Interrupt.)
Here are several examples ,based on the PIC18FxxK22, of how to implement an IOC Interrupt Handler:
Example 1: “ON INTERRUPT GOTO…”
ON INTERRUPT GOTO Alarm ;Establish the Interrupt Handler ;Special Function Registers: INTCON = %11001000 ;Enable GIE, PIE, & RBIE TRISB.4 = 1 ;Configure PORTB.4 as Input ANSELB.4 = 0 ;Configure PORTB.4 as Digital IOCB.4 = 1 ;PORTB.4 will trigger an IOCIF INTCON2.0 = 0 ;RBIP = 0, Low Priority Interrupt DISABLE Alarm: IF PORTB.4 = 1 THEN ;IOCIF Set HIGH LED ;Do Something GOSUB Squelch ;Do More Somethings INTCON.0 = 0 ;Clear the RBIF ELSEIF INTCON.2 = 1 THEN ;Filter out other Interrupts (TMR0IF) TOGGLE LATA.6 ;Do Something INTCON.2 = 0 ;Clear TMR0IF ENDIF RESUME ENABLE
The "IF PORTB.4 = 1" statement initiates a PORT Read, which is one of the requirements for clearing the RBIF (PORTB IOCIF). According to the Data Sheet, this alone should clear the RBIF. Some application Interrupt Handlers may require the RBIF to be cleared manually, INTCON0.0 = 0. Everything else is covered in the PBP3 Reference Manual and Data Sheet.
Example 2: DT_INTS
Notice we did not have to clear the RBIF in the Interrupt Handler (denoted as INTCON.0 = 0 in the “ON INTERRUPT” example). In the Interrupt Processor there is an option to automatically reset the flag, which this example uses yes.
Example 3: ASM Interrupt
DEFINE INTHAND Alarm ;Covered in 6.2.2 (page 249 of PBP3 Reference Manual) ;Special Function Registers: INTCON = %11001000 ;Enable GIE, PIE, & RBIE TRISB.4 = 1 ;Configure PORTB.4 as Input ANSELB.4 = 0 ;Configure PORTB.4 as Digital IOCB.4 = 1 ;PORTB.4 will trigger an IOCIF INTCON2.0 = 0 ;RBIP = 0, Low Priority Interrupt ;Variables: BSR_TEMP VAR BYTE ;Temporary storage Variable for BSR STATUS_TEMP VAR BYTE ;Temporary storage Variable for STATUS W_TEMP VAR BYTE ;Temporary storage Variable for W GOTO Main ;Alarm must occupy Address 0x08, see 6.2.3 (Page 249) of the PBP3 Reference Manual ASM ;Forces the Assembler to treat the following as ASSEMBLY Alarm ;Use of a colon (:) optional MOVWF W_TEMP ;Save W in W_TEMP MOVFF STATUS, STATUS_TEMP ;Save STATUS in STATUS_TEMP MOVFF BSR, BSR_TEMP ;Save BSR in BSR_TEMP BANKSEL LATA ;Select BANK with LATA BSF LATA,6 ;Turn LED On BANKSEL INTCON ;Select BANK with INTCON BCF INTCON, 0 ;Clear RBIF (INTCON.0) MOVFF BSR_TEMP, BSR ;Restore BSR MOVF W_TEMP, W ;Restore Working Register MOVFF STATUS_TEMP, STATUS ;Restore STATUS RETFIE ;Return from Handler ENDASM ;Denotes end of ASSEMBLY Main: ;Normal Routine Label DO Some Code LOOP
Assembly Language Interrupts are covered in Section 6.2 (page 248) of the PBP3 Reference Manual. The first 3 lines of the Interrupt Handler “Alarm” save important Registers to temporary variables, declared with all your other Variables. They are Restored in 3 of the last 4 lines. RETFIE is the ASM version of RETURN (from Interrupt Handler)
Newer PIC MCUs incorporate automatic context saving. This would be covered in the Interrupt section of the Data Sheet. When Context Restore is available, the MOVx commands storing and restoring context to the W, STATUS, and BSR registers would not be required; but RETFIE FAST would be used instead of RETFIE.
Example 4: DT_INTS on PIC16F1619
;Includes: include "DT_INTS-14a.bas" include "ReEnterPBP.bas" ;Interrupt Processor: ASM INT_LIST macro ;IntSource, Label, Type, ResetFlag? INT_Handler IOC_INT, _Alarm, PBP, yes ;IOCB.4 endm INT_CREATE ;Creates the interrupt processor ENDASM ;Special Function Registers: INTCON = %11001000 ;.0 = IOCIF, .3 = IOCIE TRISB.4 = 1 ;RB4 = Input IOCBP.4 = 1 ;IOC Interrupt when RB4 goes from low-to-high ;Variables: wsave VAR BYTE $20 SYSTEM ;location for W if in bank0 ;wsave VAR BYTE $70 SYSTEM ;alternate save location for W ;if using $70, comment wsave1-3 ;Labels: Main: @ INT_ENABLE IOC_INT Do PAUSE 100 LOOP Alarm: HIGH LED @ INT_RETURN
Using DT_INTS-14 is almost identical to -18 but with the addition of the variable wsave. If you open DT_INTS-14.bas in MicroCode Studio, lines 22 & 23 have 2 variables commented out with instructions to “Place a copy of these variables in your Main program; The compiler will tell you which lines to un-comment; Do Not un-comment these lines.” By pasting both lines into your code, the compiler will give you an error for the wrong one. Since both versions use the same Variable name, you must test them one at a time by commenting out one and trying to compile. In this case, the PIC16F1619 needs the $20 version.
While in DT_INTS-14.bas, lines 30 - 32 list wsave1_2_3 with instructions, “IF any of these three lines cause an error ?? Comment them out to fix the problem; Which variables are needed, depends on the Chip you are using”. When you leave all 3 uncommented and try to compile, you will get an error for wsave3. Simply comment that line out and save this version of DT_INTS-14.bas as something different, like DT_INTS-14a.bas. With these few exceptions, the -14 version will perform the same as the -18 version.
This section deals specifically with Interrupt On Change peculiarities. The appropriate Data Sheet will give details on specific PIC implementation.
Furthermore, “ON INTERRUPT”, DT_INTS, and ASM Interrupts are covered in much greater detail elsewhere (PBP3 Reference Manual is a good source for non-DT_INTS Interrupt handling).
Recommended reading would include associated sections of the PBP3 Reference Manual, as well as Interrupt, PORT 1/0, and IOC sections of your Data Sheet. If using ASSEMBLY Interrupts, read that section in the PBP3 Reference Manual (Section 6.2, starting on page 248) along with the Instruction Set Summary section of the appropriate Data Sheet. The Instruction Set Summary is like the Commands section (Section 5) of the PBP3 Reference Manual, except it deals specifically with the Assembly Language Commands applicable to your specific PIC.
Another good resource covering Assembly Language programming for PIC18 MCUs:
"Applying PIC18 Microcontrollers", by Barry B. Brey (Copyright 2008 Pearson Education, Inc., ISBN 0-13-088546-0).