PORTB interrupt on change Bug in 18Fxxxx
Perhaps this has been discussed before, but if it has been, I missed it.
I've spent a few hours figuring it out and just in case somebody else runs into the same problem, I'm passing on my hard-earned lesson.
I'm using the 18F2431, but I suspect this bug is in other chips also.
Microchip suggests in the datasheets
The user, in the
Interrupt Service Routine, can clear the interrupt in the
a) Any read or write of PORTB (except with the
MOVFF (ANY), PORTB instruction).
b) Clear flag bit, RBIF.
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.
If using the "PORTB interrupt on change", you must be careful to only read PORTB in the ISR in response to the interrupt, and use LATB for any output ports also on B. But that is not enough, because...
There is a bug in the internal implementation of the PortB Interrupt on Change logic in the PIC.
Now, we all know that...
Each instruction is executed in 4 phases Q1, Q2, Q3, Q4 with one phase at each processor clock.
Now looking closely at the block diagrams on pages 116, 117, 118 of DS39616C (datasheet for this part), (I'll wait while you turn to that page, LOL) you can see that there are 4 latches: TRISB, LATB, PORTB, and what I call the "PORTB change detection latch". From this we learn that...
- The value from the pins is latched into PortB in Q1 of each instruction cycle.
- If an instruction is reading PORTB, the instruction reads the value from this latch in Q2.
- If an instruction has read PORTB, the value from the pin is latched into the change detection latch in Q3.
The trouble is, that the value latched into the "PORTB change detection latch" IS LATCHED FROM THE PIN RATHER THAN FROM THE PORTB latch.
SO... if the value on the pin changes between Q1 and Q3
, the value latched into the change detection latch will be different than the value in the PORTB latch, and the PORTB latch will latch the new value in Q1 of the next instruction, making it equal to the value latched into the change detection latch on the previous Q3, thus enabling RBIF to be cleared - even though the most recently read value of PORTB is not what is currently on the pins. (The preceeding sentence may be unclear without a timing diagram.)
To work around this, RBIF must be cleared BEFORE reading PORTB in order to detect any change on the pin between Q1 and Q3. But, if do this, RBIF will immediately be set again, since the value in the change detection latch still differs from the value in the PORTB latch.
So, we must read PORTB first, to clear the difference which got us here in the first place, and then clear RBIF, then read PORTB again (without clearing RBIF) to insure that any change on the pins between Q1 and Q3 that instruction is detected.
In numbered sequence ...
- Read PORTB first to latch the current value from the pins into the PORTB latch and possibly into the change detection latch. In most cases, both values will be the same.
- Clear RBIF. If both latches have the same value at this point, RBIF will stay clear.
- Read PORTB again after clearing RBIF. If the value on the PIN changes between Q1 and Q3, RBIF will be set again automaticly and this is good because the value most recently read from PORTB is not the value currently on the pins. Another interrupt will occur as soon as we return from this one
In something like C code...
unsigned char LastKnownPORTB;
#pragma interrupt ISR
void ISR ()
LastKnownPORTB = PORTB; // clear the mismatch to enable RBIF to be cleared.
INTCONbits.RBIF = 0;
LastKnownPORTB = PORTB; // read and save the new state of PORTB.
#pragma code Vector = 0x008
void InterruptVector (void)
_asm goto ISR _endasm
I hope this helps somebody.
post edited by ocelot - 2007/11/10 20:51:20