Comparator input / port output interaction issue

View: New views
1 Messages — Rating Filter:   Alert me  

Comparator input / port output interaction issue

by David Meiklejohn :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

(forgot to include the [PIC] tag)

Some of you may have seen my tutorials at
http://www.gooligum.com.au/tutorials.html

Anyway, in finishing off one last example in my latest lesson, I got
stumped for a couple of nights.

I'd done a lesson (not yet uploaded) on the dual comparator module in the
16F684, using assembler.  The final example demonstrates wake-up on
comparator change using both comparators, where a common input is compared
against low and high thresholds, and if either is crossed while the PIC is
in sleep mode, the device wakes and the offending condition (too high or
low) is indicated by an LED (high or low), until the condition clears and
the PIC goes back to sleep.

Very simple code, which worked perfectly.

Then I did the HI-TECH C version, for the equivalent "C" lesson.

The "indicate bad condition and wait until it clears" part of that code,
logically exactly equivalent to the (working) assembler version, is as
simple as:

    while (C1OUT)      // while input (C2IN+) < low threshold (C1IN-)
        RC3 = 1;       //   light "low" LED

    while (C2OUT)      // while input (C2IN+) > high threshold (C2IN-)
        RC2 = 1;       //   light "high" LED

After getting through here, unless the input is changing very very quickly
(low to high in a few us), at this point we can say that it's no longer
too high or low, so both LEDs are turned off, the comparator mismatch is
cleared by reading CMCON0, the interrupt flags are cleared, and off we go
to sleep - and if the input is bouncing around, the device will wake,
restart the main loop, and this snippet of code will light the appropriate
LED and wait until the signal is back between thresholds.

That's the theory, and the assembler version worked perfectly.  But in the
HI-TECH PICC-Lite version, only the "low" LED would light, after an input
change woke the PIC from sleep.  The example circuit uses an LDR/resistor
divider on C2IN+, with a high voltage corresponding to bright light.  So -
with the C version, shining a light on the LDR would not appear to do
anything, while putting the LDR in shadow would turn on the "low" LED, as
it should.  And yet, in the assembler version, both "high" and "low"
worked equally well.

I couldn't figure it out.  I looked at the disassembler output, and it all
seemed correct and very nice code (I used the 16F684 precisely because
it's supported by PICC-Lite, which generates fully optimised code).  In
fact, the code was shorter than my hand-written assembler version, which
included unnecessary 'banksel' directives - because the only registers
being accessed are CMCON0 (to read C1OUT and C2OUT) and PORTC, which are
in the same bank.  This turns out to be the key factor, but it took me
ages to figure out.

What do you do when your code doesn't work?  Debug, of course.  So I
whipped out my 16F684 ICD header, single stepped and set breakpoints (but
only simple ones are supported on the 16F684, with an ICD 2) - and it all
worked perfectly!

I watched the comparator outputs changing correctly.  Saw the interrupt
flags being set.  Set a breakpoint on "RC2 = 1", which is only run when
the device wakes after the high threshold is crossed - and lo and behold,
I'd shine a light on the LDR and the "high" LED would come on.  But let it
run without the breakpoint - nothing.  Not visible, anyway.

So, when my trusty ICD 2 fails me, how about simulation?  I dove into
MPLAB SIM, made a workbook to produce every condition I could think of,
and all worked perfectly in simulation.  Aargh!

Then it finally hit me.  The difference between single stepping, or
stopping on a breakpoint, and full speed running, is speed.  PICC-Lite had
generated more efficient, and faster, code than my assembler version.

Despite having written about read-modify-write in my tutorials, I didn't
see that anything like that applied here.  In theory, lighting an LED
attached to PORTC will not affect an analog input on PORTC (C2IN+ in this
case, on RC0).  And because in theory there is no such effect, the
simulator won't show it.

What was happening was that, when the input on C2IN+ went high, the PIC
would wake, the C2OUT bit would (correctly) be set, and the "while
(C2OUT)" statement correctly saw this condition and ran the following "RC2
= 1", turning on the "high" LED.  That's why, when I had a breakpoint set
on that line, execution stopped, as expected, with that LED lit.

But - turning on an LED means that PORTC has to suddenly supply more
current.  Now, in theory, even if the extra current drags down the Vdd
supply, both the C2IN+ input and the reference on C2IN- are generated by a
voltage divided from Vdd, and everything should remain proportional, any
affect applying to both C2IN+ (RC0) and C2IN- (RC1), so the comparator
output shouldn't change.  But theory isn't practice, because, immediately
after turning on the LED on RC3, C2OUT no longer read as "high".  So the
test failed the next time around the loop, and the LED was turned off
almost immediately after being turned on - too fast for me to see, but not
too fast for a breakpoint to pick up.

What was making the assembler version work was the "unnecessary" banksel
directives.  So to make the C version work, I had to add a small delay:

    while (C1OUT)      // while input (C2IN+) < low threshold (C1IN-)
    {
        RC3 = 1;       //   light "low" LED
        DelayUs(2);    //   2 us delay to settle port
    }

    while (C2OUT)      // while input (C2IN+) > high threshold (C2IN-)
    {
        RC2 = 1;       //   light "high" LED
        DelayUs(2);    //   2 us delay to settle port
    }


Live and learn, I guess.

I'm posting this in case anyone else can learn from my experience!


Regards,
David Meiklejohn






--
http://www.piclist.com PIC/SX FAQ & list archive
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist