Clearing interrupt-on-change

Coding and general discussion relating to the compiler

Moderators: David Barker, Jerry Messina

Post Reply
SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Clearing interrupt-on-change

Post by SHughes_Fusion » Wed Jun 17, 2015 9:29 am

I'm looking at ways to optimise some code to see if I can reduce the clock frequency I'm running at to save power. (Battery powered unit)

To do this I simply use a 1Hz 'tick' I already have in the code with a counter which increments each time the main loop runs, basically seeing how many times the loop runs per second.

I'm seeing something very unusual - when I enable the interrupt on change module this value drops by a factor of 6 or 7.

Delving deeper, the datasheet (PIC18LF26K22) states that to clear the interrupt I need to do the following:
Any read or write of PORTB to clear the mismatch condition (except when PORTB is the source or destination of a MOVFF instruction).
Execute at least one instruction after reading or writing PORTB, then clear the flag bit, RBIF.
My code is:

Code: Select all

     EncoderPos = EncoderPort
     ProcessEncoder = true
     RBIF = false
Which compiles to:

Code: Select all

?I001241_F000_000253_P000261 ; L#MK ENCODERPOS = ENCODERPORT
    MOVFF PORTB,M2023_S08
?I001242_F000_000254_P000261 ; L#MK PROCESSENCODER = TRUE
    MOVLB 8
    BSF M2009_U01,2
?I001243_F000_000256_P000261 ; L#MK RBIF = FALSE
    BCF INTCON,0,0
Now, this uses MOVFF which the datasheet says not to use. Is there any way I can 'persuade' the compiler to choose a different instruction in this case?

Also, I note that the IoC example in the Wiki doesn't follow the second point in the datasheet, clearing the flag immediately after reading the port. Is this one instruction wait something that only applies to certain processors? Is it worth updating the wiki to reflect this?

I should say that I don't know for sure that this is the cause of the massive increase in processor load, however what the loop is doing is not massively different between 'idle' and 'active' modes - it is basically just testing a series of flags - so I can't see any other reason for such a large change.

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Clearing interrupt-on-change

Post by Jerry Messina » Wed Jun 17, 2015 10:22 am

Is there any way I can 'persuade' the compiler to choose a different instruction in this case?
This should work...

Code: Select all

dim EncoderPort as PORTB
dim RBIF as INTCON.booleans(0)

dim EncoderPos as byte

inline sub nop()
    asm
        NOP
    end asm
end sub
            
macro READ_IOC(x)
    WREG = EncoderPort
    x = WREG
    nop()
    RBIF = false
end macro

READ_IOC(EncoderPos)
Translates to:

Code: Select all

?I000000_F000_000045_M000000 ; L#MK READ_IOC(ENCODERPOS)
    MOVF PORTB,0,0
?I000001_F000_000045_M000000 ; L#MK READ_IOC(ENCODERPOS)
    MOVWF M0_U08,0
?I000002_F000_000045_M000000 ; L#MK READ_IOC(ENCODERPOS)
        NOP
?I000003_F000_000045_M000000 ; L#MK READ_IOC(ENCODERPOS)
    BCF INTCON,0,0
It'll also take care of the banksel for you if 'x' is in a different bank.

I don't know for sure that this is the cause of the massive increase in processor load
Quite likely. If the IOC mismatch isn't getting cleared then you'll get repeated interrupts until it is...
A mismatch condition will continue to set the RBIF flag bit.
Reading or writing PORTB will end the mismatch
condition and allow the RBIF bit to be cleared. The latch
holding the last read value is not affected by a MCLR nor
Brown-out Reset. After either one of these Resets, the
RBIF flag will continue to be set if a mismatch is present.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: Clearing interrupt-on-change

Post by SHughes_Fusion » Wed Jun 17, 2015 11:17 am

Thanks, Jerry, going through WREG appears to do the trick - it has halved the processor load anyway!

Just to check, I've done this slightly different from you - can you see any reason why this wouldn't work?

Code: Select all

     WREG = EncoderPort
     EncoderPos = WREG
     ProcessEncoder = true
Also, I'm wondering if there might be a bit of a flaw in my code / design. Could *any* read of Port B potentially mess up interrupt detection? I use all 8 pins as inputs for various things but only use IoC on two. Will polling the other pins potentially cause an IoC to be missed or is that very unlikely?

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Clearing interrupt-on-change

Post by Jerry Messina » Wed Jun 17, 2015 11:42 am

can you see any reason why this wouldn't work?
Off the top of my head that should be fine. I just like to put stuff like this together into a macro/inline sub to make sure I don't forget and do something stupid in the future.

Could *any* read of Port B potentially mess up interrupt detection? I use all 8 pins as inputs for various things but only use IoC on two. Will polling the other pins potentially cause an IoC to be missed or is that very unlikely?
Yeah, that could be a problem... any read or write to PORTB updates the mismatch latches
The interrupt-on-change feature is recommended for
wake-up on key depression operation and operations
where PORTB is only used for the interrupt-on-change
feature. Polling of PORTB is not recommended while
using the interrupt-on-change feature.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: Clearing interrupt-on-change

Post by SHughes_Fusion » Wed Jun 17, 2015 11:46 am

Hmmm... That basically means you can't use the rest of port B if you use IoC so have at least 4 wasted pins...

I wonder if use of MOVFF could be a work-around for this? I don't know if the reason not to use it to clear the interrupt is because it doesn't update the mismatches or something else, but if it is that then I just need to change my code to copy the port contents to a shadow variable before testing it.

I must say I haven't noticed missing any interrupts - I use it to monitor a rotary encoder and the pulse count is pretty much what we expect. Also, as we are using one with 512 counts per revolution missing the odd one wouldn't matter massively.

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Clearing interrupt-on-change

Post by Jerry Messina » Wed Jun 17, 2015 12:55 pm

The PIC18 ref manual has a little more to say on this...
This interrupt can wake the device from SLEEP. The user, in the interrupt service routine, can
clear the interrupt in the following manner:
a) Any read or write of PORTB will end the mismatch condition, except a write using the
MOVFF instruction
.
b) Clear flag bit RBIF.
The MOVFF instruction will not end the mismatch condition if PORTB is used only as the destina-
tion register. The contents of the destination register are not automatically read by this instruction
in the second cycle. All other reads, writes, and bit operations will read the port during execution.
A mismatch condition will continue to set flag bit RBIF. Reading PORTB will end the mismatch
condition, and allow flag bit RBIF to be cleared.
Things might have changed (and I always use the individual datasheet as the final word), but it seems that a MOVFF read will clear the mismatch, just not a write.

If you don't care about losing an interrupt or two then I suppose it doesn't really matter.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: Clearing interrupt-on-change

Post by SHughes_Fusion » Wed Jun 17, 2015 1:03 pm

Hard to tell how accurate the datasheet is on this topic but it says:
[quote]except when PORTB is the source or destination of a MOVFF instruction[/quote]
The datasheet (18F26K22) also status there must be at least one instruction execution between reading/writing the port and clearing the flag which is something that many other places where IoC is mentioned don't say.

Hard to be sure but anecdotal evidence would suggest that the MOVFF read I was performing wasn't enough to allow the interrupt flag to be cleared. The fact I changed the ISR code alone and got a step change in free processor time would suggest the routine was being called more than once. It was probably the other port reads (which don't happen very often) were what was allowing the flag to be cleared.

In these circumstances I'm more worried about false interrupts wasting processor cycles than the odd missed interrupts.

Post Reply