• AVR Freaks

PORTB interrupt on change Bug in 18Fxxxx

Author
ocelot
Super Member
  • Total Posts : 192
  • Reward points : 0
  • Joined: 2006/04/11 09:35:34
  • Location: Bama
  • Status: offline
2007/11/10 20:42:25 (permalink)
1 (1)

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
following manner:
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...

  1. The value from the pins is latched into PortB in Q1 of each instruction cycle.
  2. If an instruction is reading PORTB, the instruction reads the value from this latch in Q2.
  3. 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.

In prose...

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 ...
  1. 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.
  2. Clear RBIF.  If both latches have the same value at this point, RBIF will stay clear.
  3. 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 ()
{
   if (INTCONbits.RBIF)
   {
       LastKnownPORTB = PORTB; // clear the mismatch to enable RBIF to be cleared.
       INTCONbits.RBIF = 0;
       LastKnownPORTB = PORTB; // read and save the new state of PORTB.
   }
}// ISR

#pragma code Vector = 0x008
void InterruptVector (void)
{
_asm goto ISR _endasm
}
#pragma code

//// end

I hope this helps somebody.

-ocelot
post edited by ocelot - 2007/11/10 20:51:20
#1

10 Replies Related Threads

    DarioG
    Allmächtig.
    • Total Posts : 54081
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: Oesterreich
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2007/11/11 03:25:03 (permalink)
    0
    Thanks for sharing this.
    I used that interrupt seldom, on 16F and 18F, and never noticed anything bad, but it may depend on PIC used.

    Actually, I'm wondering: what if the PORT is not read twice? Won't you just get 2 consecutive interrupts? (not a big pain in this case). Or will a bad value be stored into LastKnownPortB ?

    GENOVA :D :D ! GODO
    #2
    ocelot
    Super Member
    • Total Posts : 192
    • Reward points : 0
    • Joined: 2006/04/11 09:35:34
    • Location: Bama
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2007/11/11 16:16:35 (permalink)
    0
    DarioG,

    If, in the ISR, you clear RBIF after reading PORTB, you will miss any transition that occurs between Q1 and Q3 of the instruction which reads the port.  In that case, you won't get another interrupt to make things right.

    My particular case revealed this because multiple signals connected to PORTB 4-7 often transition within a few microseconds of each other.   In designs where this rarely happens, the chances are very low that you will happen to read PORTB in the critical moment when a transition occurs on one of these 4 pins.

    -ocelot
    post edited by ocelot - 2007/11/11 16:19:51
    #3
    DarioG
    Allmächtig.
    • Total Posts : 54081
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: Oesterreich
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2007/11/11 16:35:25 (permalink)
    0
    Yep, all right then.
    More or less, on a 40MHz part, the Q1 and Q3 moments would be 50nSec apart. The interrupt would have happened at maximum 100nSec (I assume) after the change in PORTB happened. Plus some (fast) context saving... yes, this all means that only pulses shorter than some 1-2uSec will trigger what you state. But, IMO, if one is handling such pulses, missing one of them (and getting an interrupt at the very next time)... won't hurt a lot. Only an isolated pulse like that would suffer from this bug.

    Hope someone does confirm this, anyway.

    GENOVA :D :D ! GODO
    #4
    ocelot
    Super Member
    • Total Posts : 192
    • Reward points : 0
    • Joined: 2006/04/11 09:35:34
    • Location: Bama
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2007/11/12 10:06:44 (permalink)
    0
    DarioG,
     
    Actually, it does not have to be a short pulse.  A transition just has to happen at a critical moment; as you say, in about a 50ns window.   In my case, the pulses are something like 250us between transitions, plenty slow enough for a PIC to monitor.  But all four upper bits of PORTB are connected to inputs which tend to change within a couple of microseconds of each other.  The first pin that changes, say RB7, causes the interrupt, and the probability is high that (every 10 minutes or so) one of the other pins; RB4, RB5 or RB6, will change during that 50ns window when my ISR is reading PORTB.   A change on the pin at that moment will not generate an interrupt.
     
    -Ocelot
    #5
    DarioG
    Allmächtig.
    • Total Posts : 54081
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: Oesterreich
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2007/11/12 11:15:20 (permalink)
    0
    I see.
    Yes, inside that window... but the window Starts soon after the IRQ has been serviced, am I correct?

    GENOVA :D :D ! GODO
    #6
    ocelot
    Super Member
    • Total Posts : 192
    • Reward points : 0
    • Joined: 2006/04/11 09:35:34
    • Location: Bama
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2007/11/12 17:56:12 (permalink)
    0
    Right.  If, for example, PORTB is read 20 instructions into the ISR, then, the window of bad opportunity begins 2 microseconds (at 40MHz clock speed) after the change on one of the PORTB<7:4> pins.   If one of the other pins changes during the 50ns interval during which the ISR is reading PORTB, that second change will not be noticed and will not result in a 2nd interrupt.
     
    -Ocelot
    #7
    bromleyk60
    Starting Member
    • Total Posts : 62
    • Reward points : 0
    • Joined: 2003/11/07 12:39:50
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2009/01/30 03:19:18 (permalink)
    0
    Ocelot, thank you for posting this. I appreciate how long it must have taken you.
    The Bug you describe has caused me problems and I have spent considerable time searching.

    I must say that I am disappointed that Microchip have let this one out. The whole purpose of the Interrupt-On-Change feature is to guarantee an interrupt if the application's copy of PORTB changes. I wonder how many applications have the 'intermittent stuck button' fault that this bug would typically cause if there were an unlucky switch bounce.

    A couple of additional points:

    When I compiled code like yours on the C18 compiler it generated a MOVFF instruction to read PORTB. The datasheet says "Any read or write of PORTB (except with the MOVFF (ANY), PORTB instruction). This will end the mismatch condition.". So for PIC18 I used assembly for the actual PORTB reads.

    _asm
    MOVF PORTB,0,0
    _endasm
    INTCONbits.RBIF = 0;
    _asm
    MOVF PORTB,0,0
    _endasm
    LastKnownPORTB = WREG;

    I believe that a side effect of your work-around is that you can get interrupts when PORTB has not changed from LastKnownPORTB. For most applications I expect this does not matter.
    #8
    bromleyk60
    Starting Member
    • Total Posts : 62
    • Reward points : 0
    • Joined: 2003/11/07 12:39:50
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2009/01/30 06:05:09 (permalink)
    0
    Another quirk: I noticed with the code I posted above my ISR was being called twice per PORTB change. I discovered that BCF INTCON,RBIF,ACCESS does not clear RBIF if it immediately follows MOVF PORTB,W,ACCESS. I added a NOP and found this code worked correctly:

    _asm
    MOVF PORTB,0,0
    NOP
    _endasm
    INTCONbits.RBIF = 0;
    _asm
    MOVF PORTB,0,0
    _endasm
    LastKnownPORTB = WREG;

    I can't see why the NOP makes a difference - but it does!

    #9
    VStar650cl
    Junior Member
    • Total Posts : 102
    • Reward points : 0
    • Joined: 2009/01/25 09:44:24
    • Location: 0
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2009/01/30 08:15:54 (permalink)
    0
    The solution you proposed will mitigate the problem, but if the signal change occurs amid the second read, I suspect it would still latch an incorrect value.  The real problem is that IOC is not a panacea that can replace a real INT pin.  IOC was conceived for quickie keyboard monitoring, and for that it works well  In architectural terms, it should simply be avoided for any sort of signal requiring high reliability.
    #10
    VStar650cl
    Junior Member
    • Total Posts : 102
    • Reward points : 0
    • Joined: 2009/01/25 09:44:24
    • Location: 0
    • Status: offline
    RE: PORTB interrupt on change Bug in 18Fxxxx 2009/01/30 08:31:41 (permalink)
    0
    PS - If what anyone is really saying is that IOC should come with an "FDA Warning Label," you're right.  I have personally seen (and been involved in a few) many instances where inappropriate use of IOC got designers into big trouble.  I think this is mostly a result of Microchip painting the function with a rosy brush and not adequately framing the pitfalls.
    #11
    Jump to:
    © 2019 APG vNext Commercial Version 4.5