Professional Documents
Culture Documents
Osmeoisis 2022-09-06 15-33-19PIC - Enh - A - 10
Osmeoisis 2022-09-06 15-33-19PIC - Enh - A - 10
au
We’ve seen how to respond to simple on/off digital signals, but we live in an “analog” world; many of the
sensors you will want your PIC-based projects to respond to, such as temperature or light, have smoothly
varying outputs whose magnitude represents the value being measured; they are analog outputs.
In many cases, you will want to treat the output of an analog sensor as though it was digital (i.e. on or off,
high or low), without worrying about the exact value. It may be that a pulse, that does not meet the
requirements for a “digital” input, has to be detected: for example, the output of a Hall-effect sensor attached
to a rotating part. Or we may simply wish to respond to a threshold (perhaps temperature) being crossed.
In such cases, where we only need to respond to an input voltage being higher or lower than a threshold, it is
usually appropriate to use an analog comparator. In fact, analog comparators are so useful that most PICs
include them, as built-in peripherals.
This lesson explains how to use comparators, on enhanced mid-range PIC devices, to respond to analog
inputs.
In summary, this lesson covers:
Using comparators to compare voltage levels
Adding comparator hysteresis
Comparator-driven interrupts
Wake-up on comparator change
Comparators
A comparator (technically, an analog comparator) is a device which compares the voltages present on its
positive and negative inputs. If the voltage on the positive input is greater than that on the negative input, the
output is set “high”; otherwise the output is “low”.
An example is shown in the diagram on the right.
The two 10 kΩ resistors act as a voltage divider, presenting 2.5 V at the
comparator’s negative input. This sets the on/off threshold voltage.
The potentiometer can be adjusted to set the positive input to any
voltage between 0 V and 5 V.
When the potentiometer is set to a voltage under 2.5 V, the
comparator’s output will be low and the LED will not be lit. But when
the potentiometer is turned up past halfway, the comparator’s output
will go high, lighting the LED.
Comparators are typically used when a circuit needs to react to a sensor’s analog output being above or
below some threshold, triggering some event (e.g. time to fill the tank, turn off a heater, or start an alarm).
They are also useful for level conversion. Suppose a sensor is outputting pulses which are logically “on/off”
(i.e. the output is essentially digital), but do not match the voltage levels needed by the digital devices they
are driving. For example, with VDD = 5 V, TTL-compatible digital inputs require at least 2 V (or 4 V for
Schmitt trigger inputs) to register as “high”. That’s a problem if a sensor is delivering a stream of 0 - 1 V
pulses. By passing this signal through a comparator with an input threshold of 0.5 V, the 1 V pulses would
be recognised as being “high”.
The comparator input channels are selected via the CxPCH and CxNCH bits in the CMxCON1 register:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
CMxCON1 CxINTP CxINTN CxPCH<1:0> – – CxNCH<1:0>
It is also possible to use a fixed or variable voltage reference as the positive input on either comparator.
We’ll look at the voltage references in detail in the next lesson, but briefly they allow an internally-generated
voltage to be used as the comparator threshold, avoiding the need to use external circuitry to create the
threshold (as we will do in the examples in this lesson), simplifying your design and freeing an I/O pin.
Note that VSS (ground) can be selected as the positive input. This can be used to detect when an input
crosses 0 V, but note that the voltage on a PIC pin must never be allowed to go below -0.3 V.
CxNCH<1:0> Negative input Shared with pin The two comparators share a set of four
input pins: C12IN0-, C12IN1-, C12IN2-
00 C12IN0- RA1 and C12IN3-, which on the 16F1824 share
01 C12IN1- RC1 their physical pin with RA1, RC1, RC2 and
RC3 respectively.
10 C12IN2- RC2
Each of these pins can be selected as the
11 C12IN3- RC3 negative input on either comparator.
The CxON bit turns the comparator on or off: ‘1’ to turn it on, ‘0’ to turn it off.
Turning off the comparator saves power, which is especially important in sleep mode. Of course, if you’re
using the comparator to wake the device from sleep (see later in this lesson), you’ll need to leave it on.
However, comparator power consumption can be reduced by clearing the CxSP bit, which, for a 16F1824
with a 5 V supply in sleep mode, reduces the comparator current from 42 µA to 10 µA (typical). The trade-
off (yes, there is always at least one…) is that the comparator is slower in low-power mode. For example,
the time taken to respond to a rising edge increases from 400 ns to 1200 ns (typical).
The comparator’s output appears as the CxOUT bit: it is normally set to ‘1’ if and only if the positive input
voltage is higher than the negative input voltage.
That’s the normal situation, but the operation of the comparator can be inverted by setting the CxPOL output
polarity bit:
CxPOL = 0 selects normal operation
CxPOL = 1 selects inverted operation, where CxOUT = 1 only if positive input < negative input.
The comparator output can also be made available externally, on the CxOUT pin, by setting the CxOE bit.
The C1OUT and C2OUT pins are shared with (and take priority over) RA2 and RC4 respectively.
Note that, to use a pin as an external comparator output, it must first be configured as an output by clearing
the corresponding TRIS bit.
It is often desirable to add some hysteresis to the comparator’s operation, to avoid its output randomly
changing states due to noise on its inputs, while the input levels are very close. A small amount of hysteresis
(typcially 45 mV) can be added to the comparator inputs by setting the CxHYS bit.
The CxSYNC bit is used to synchronise the comparator output with Timer1; we’ll see how it’s used when
we look at Timer1, in a later lesson.
Finally, it may be important, in some applications, to be able to sample the output of both comparators at the
same time. With the C1OUT bit being in the CM1CON0 register, and C2OUT being in CM2CON0, it is
not possible to read both bits at once – you can only read one register, and then the other.
To address this problem, a copy (or mirror) of each comparator output bit is also made available in the
CMOUT register:
The advantage of using the CMOUT register is that both comparator output bits can be read in a single
operation. Otherwise, whether you use these bits or the CxOUT bits in the CMxCON0 registers makes no
difference – they are mirror images of each other: MC1OUT = C1OUT, and MC2OUT = C2OUT.
The circuit can alternatively be built using the Microchip Low Pin Count Demo Board.
The 10 kΩ potentiometer on that board is already connected to C1IN+ (labelled ‘RA0’ on the board), via a 1
kΩ resistor (not shown in this diagram). But you must ensure that jumper JP5 is closed; it will be, if you
haven’t modified your demo board. You will need to connect an LED to RA2, as was shown in lesson 1.
You can connect 10 kΩ resistors via the 14-pin header on the demo board. C12IN0- (labelled ‘RA1’) is
available on pin 8, and +V and GND are pins 13 and 14 respectively.
It is straightforward to configure the PIC as a simple comparator, turning on the LED if the potentiometer is
turned more than halfway toward the +5V supply (right) side.
First, we need to configure RA2 as an output and RA0 (C1IN+) and RA1 (C12IN0-) as inputs:
; configure ports
movlw ~(1<<nLED) ; configure LED pin (only) as an output
banksel TRISA ; (RA0/C1IN+ and RA1/C12IN0- are inputs)
movwf TRISA
Although analog is the default mode for all inputs with an associated analog input function, it is good
practice to explicitly select analog input mode for RA0 and RA1, to make it clear that these pins are being
used as analog inputs:
movlw b'000011' ; select analog mode for RA0 and RA1
banksel ANSELA ; -> RA0/C1IN+ and RA1/C12IN0- are analog
movwf ANSELA
This turns comparator 1 on, and configures it to test for C1IN+ > C12IN0-, with the external output disabled
and no additional “features” such as low-power, hysteresis or output synchronisation.
This is the default setting, so in theory it was only necessary to enable comparator 1, with:
banksel CM1CON0
bsf CM1CON0,C1ON ; enable comparator 1
But relying implicitly on default settings is obscure and error-prone; it is much clearer and safer to explicitly
initialise all of the register bits that affect the functions you are using.
To turn on the LED when the comparator output is high, repeatedly test C1OUT:
main_loop
; display comparator output
banksel CM1CON0
btfsc CM1CON0,C1OUT ; if comparator output high
bsf LED ; turn on LED
btfss CM1CON0,C1OUT ; if comparator output low
bcf LED ; turn off LED
; repeat forever
goto main_loop
Note that there is no need to add ‘banksel LATA’ directives before accessing the LED output pin, because,
in enhanced mid-range PICs, the comparator and output data latch registers are all located in the same bank.
This makes it possible to avoid having to use the more complex type of “copy a bit” routine, involving
‘goto’s and labels, that we’ve had to use before.
You should find that, as you turn the potentiometer past halfway, the LED turns on and off.
The RA2 pin, that we’ve used to drive the LED, can instead be used as the comparator 1 external output pin,
C1OUT. So, if we enable the external output, the comparator can drive the LED directly, independently of
our program code.
The comparator configuration becomes:
; configure comparator 1
movlw b'10100100' ; configure comparator 1:
; 1------- comparator enabled (C1ON = 1)
; --1----- external output enabled (C1OE = 1)
; ---0---- output not inverted (C1POL = 0)
; -----1-- normal power mode (C1SP = 1)
; ------0- hysteresis disabled (C1HYS = 0)
; -------0 asynchronous output (C1SYNC = 0)
banksel CM1CON0
movwf CM1CON0
movlw b'00000000' ; select comparator 1 inputs:
; --00---- + in = C1IN+ pin (C1PCH = 00)
; ------00 - in = C12IN0- pin (C1NCH = 00)
movwf CM1CON1 ; -> C1OUT pin = 1 if C1IN+ > C12IN0-
Once configured, the comparator output will appear on the C1OUT pin automatically, so the main loop is
left with nothing to do:
main_loop
; (do nothing)
; repeat forever
goto main_loop
(‘LDR1’), connecting the photocell (PH1) and 22 kΩ resistor in the lower left of the board to C1IN+.
If you are using the Microchip Low Pin Count Demo Board, you can’t take the 10 kΩ potentiometer out of
the circuit – it, and the 1 kΩ resistor in series between it and C1IN+, must be used as the “fixed” resistance,
forming the lower arm of the potential divider. You must also remove jumper JP5 (you may need to cut the
PCB trace – ideally you’d install a jumper, so that you can reconnect it again later), to disconnect the pot
from the +5 V supply.
If you turn the pot all the way to the right, you’ll have a total resistance of 11 kΩ between C1IN+ and
ground. That means that ideally you’d use a photocell with a resistance of around 10 kΩ with normal indoor
lighting. The photocell can then be connected between pin 7 on the 14-pin header and +5 V.
If you don’t have a photocell, there is no problem with continuing to use the potentiometer-only circuit for
these lessons; but it’s certainly more fun to build a circuit that responds to light!
When you build this circuit and use the above code, you will find that the LED turns on when the LDR is
illuminated.
If you would prefer it to work the other way, so that the LED is lit when the light level falls (simulating e.g.
the way that streetlamps turn on automatically when it gets dark), there’s no need to change the circuit
connections or program logic. Simply change the comparator initialisation instructions to invert the output,
by setting the C1POL bit:
; configure comparator 1
movlw b'10110100' ; configure comparator 1:
; 1------- comparator enabled (C1ON = 1)
; --1----- external output enabled (C1OE = 1)
; ---1---- output inverted (C1POL = 1)
; -----1-- normal power mode (C1SP = 1)
; ------0- hysteresis disabled (C1HYS = 0)
; -------0 asynchronous output (C1SYNC = 0)
banksel CM1CON0
movwf CM1CON0
movlw b'00000000' ; select comparator 1 inputs:
; --00---- + in = C1IN+ pin (C1PCH = 00)
; ------00 - in = C12IN0- pin (C1NCH = 00)
movwf CM1CON1 ; -> C1OUT pin = 1 if C1IN+ < C12IN0-
Adding hysteresis
You will notice, as the light level changes slowly past the threshold where the LED turns on and off, that the
LED appears to fade in and out in brightness. This is caused by noise in the circuit and fluctuations in the
light source (particularly at 50 or 60 Hz, from mains-powered fluorescent lamps): when the input is very
close to the threshold voltage, small input voltage variations due to noise and/or fluctuating light levels cause
the comparator output to rapidly, and randomly, switch between high and low. This rapid switching, similar
to switch bounce, can be a problem if the microcontroller is supposed to count input transitions, or to
perform an action on each change.
To reduce this phenomenon, we can set the C1HYS bit to enable comparator hysteresis. In this mode, a
small separation voltage (typically 45 mV for the PIC16F1824) is added at the comparator inputs.
However, you will find that the effect is quite small – in some situations you may need more hysteresis than
this input separation voltage can provide.
Additional hysteresis can be created by adding positive feedback – feeding some of the comparator’s output
back to its positive input.
Vth Veq
Req
Vdd Veq (thus Vth > Veq)
Rh Req
As we’ve seen, each comparator’s output can be made available externally; the external outputs can be used
to add additional comparator hysteresis, as appropriate.
If you are using the Gooligum training board you can build this circuit by placing a shunt in position 3
(labelled ‘C1IN-’) of JP25, connecting a photocell (PH2) and 22 kΩ resister to C12IN0-, and in JP17 to
enable the LED on RC1. You can use the solderless breadboard to add the supplied 4.7 kΩ, 10 kΩ and 22
kΩ resistors; C2IN+ (labelled ‘RC0’) and C2OUT (‘RC4’) are available via pins 10 and 6, respectively, on
the 16-pin header.
Using these voltage and resistance values in the formulas above gives, for this circuit:
Veq = 1.6 V Req = 3.2 kΩ Rh = 22 kΩ
Vtl = 1.40 V Vth = 2.03 V Vhb = 0.63 V
Thus, the upper and lower voltage thresholds are either side of 1.6 V.
The hysteresis band of 0.63 V represents around 13% of the input voltage range of 0 – 5 V, which is quite
large but makes it easy to observe the effect.
The code is essentially the same as before, but modified to work with comparator 2 and an LED on RC1.
Our port initialisation becomes:
; configure ports
movlw ~(1<<nLED|1<<4) ; configure LED pin and RC4/C2OUT as outputs
banksel TRISC ; (RA1/C12IN0- and RC0/C2IN+ are inputs)
movwf TRISC
banksel ANSELA ; select analog mode for RA1 and RC0
bsf ANSELA,1 ; -> RA1/C12IN0- and
bsf ANSELC,0 ; RC0/C2IN+ are analog inputs
Note again that, to allow C2OUT to drive the RC4 pin, RC4 must be configured as an output.
Note that it is very important to clear the C2POL bit. If it is not cleared, the comparator output, appearing
on C2OUT, will be inverted, and the feedback will be negative, instead of the positive feedback needed to
create hysteresis.
The LED is then lit in the main loop, whenever the C2OUT bit is high (indicating that the LDR is in
darkness), in the same was as in example 1.
Complete program
Here is how these pieces fit into the basic comparator example program:
;************************************************************************
; *
; Description: Lesson 10, example 2 *
; *
; Demonstrates basic use of Comparator 2 *
; with hysteresis added via feedback from external output *
; *
; Turns on LED when input on C12IN0- < threshold on C2IN+ *
; *
;************************************************************************
; *
; Pin assignments: *
; C12IN0- = voltage to be measured (e.g. pot output or LDR) *
; C2IN+ = threshold voltage (set by voltage divider resistors) *
; C2OUT = comparator output (fed back to threshold via resistor)*
; RC1 = indicator LED *
; *
;************************************************************************
#include "p16f1824.inc"
;***** CONFIGURATION
; ext reset, internal oscillator (no clock out), no watchdog,
; brownout resets on, no power-up timer, no code or data protect,
; no failsafe clock monitor, two-speed start-up disabled
; no write protection, 4xPLL off, stack resets on,
; low BOR threshold, high-voltage programming
__CONFIG _CONFIG1, _MCLRE_ON & _FOSC_INTOSC & _CLKOUTEN_OFF & _WDTE_OFF &
_BOREN_ON & _PWRTE_OFF & _CP_OFF & _CPD_OFF & _FCMEN_OFF & _IESO_OFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
; pin assignments
#define LED LATC,1 ; indicator LED on RC1
constant nLED=1 ; (port bit 1)
;***** Initialisation
start
; configure ports
movlw ~(1<<nLED|1<<4) ; configure LED pin and RC4/C2OUT as outputs
banksel TRISC ; (RA1/C12IN0- and RC0/C2IN+ are inputs)
movwf TRISC
banksel ANSELA ; select analog mode for RA1 and RC0
bsf ANSELA,1 ; -> RA1/C12IN0- and
bsf ANSELC,0 ; RC0/C2IN+ are analog inputs
; configure comparator 2
movlw b'10100100' ; configure comparator 2:
; 1------- comparator enabled (C2ON = 1)
; --1----- external output enabled (C2OE = 1)
; ---0---- output not inverted (C2POL = 0)
; -----1-- normal power mode (C2SP = 1)
; ------0- hysteresis disabled (C2HYS = 0)
; -------0 asynchronous output (C2SYNC = 0)
banksel CM2CON0
movwf CM2CON0
movlw b'00000000' ; select comparator 2 inputs:
; --00---- + in = C2IN+ pin (C2PCH = 00)
; ------00 - in = C12IN0- pin (C2NCH = 00)
movwf CM2CON1 ; -> C2OUT = 1 if C12IN0- < C2IN+
; repeat forever
goto main_loop
END
Comparator Interrupts
Each comparator can be selected, independently, as an interrupt source. This is similar to the interrupt-on-
change (IOC) facility described in lesson 8, except that, instead of an IOC interrupt being generated when a
defined edge is detected on a digital input, the comparator interrupt is triggered whenever the comparator’s
output changes in a defined way.
This means that, if you wish to respond to a change in the comparator output, there is no need to continually
poll the CxOUT flag, as we have been doing. Instead, you can choose to generate an interrupt whenever
CxOUT transitions from low to high and/or from high to low, and handle the “comparator change” event in
your interrupt service routine (ISR) – much as we did for pin change interrupts in lesson 8.
As with all other interrupts (see lesson 7), the comparator interrupt has an associated enable bit, which has to
be set to allow the comparator to trigger interrupts, and an interrupt flag bit, which is set whenever the
comparator’s output changes.
So far, all of the interrupt enable and flag bits we’ve looked at have been in the INTCON register. But the
comparator module is considered to be a peripheral (not part of the PIC core), and its interrupt bits are held
in peripheral interrupt registers.
Each comparator interrupt is enabled by setting the corresponding CxIE bit in the PIE2 (peripheral interrupt
enable 2) register:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
We saw in lesson 7 that, to enable any interrupts to occur, it is necessary to set the GIE (global interrupt
enable) bit in the INTCON register, as well as the enable bits (such as C1IE or C2IE) for whichever
interrupt sources you wish to use.
However, to enable any peripheral interrupt (where the enable bit is in one of the PIE registers), you must
also set the PEIE (peripheral interrupt enable) bit in INTCON:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
So, to enable comparator interrupts, we must set C1IE and/or C2IE, PEIE and GIE.
The comparator interrupt flags, CxIF, are located in PIR2 (peripheral interrupt register 2):
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Each of the CxIF flags are set whenever an enabled edge (positive or negative transition) is detected on the
corresponding comparator output (CxOUT).
As with other interrupt sources, the comparator interrupt flags are active, whether or not comparator
interrupts are enabled. This means that you could, in principle, poll C1IF to detect defined changes in
C1OUT – although it would normally make more sense to simply poll C1OUT directly.
The type(s) of change (or edge) that can trigger each comparator interrupt are selected via the CxINTP and
CxINTN interrupt enable bits in the CMxCON1 register:
Setting the CxINTP bit to ‘1’ enables the detection of positive edges: the CxIF flag will be set whenever
CxOUT transitions from low to high.
Similarly, setting the CxINTN bit to ‘1’ enables the detection of positive edges: CxIF will be set whenever
CxOUT transitions from high to low.
Thus, it is possible to selectively detect positive and/or negative transitions on either comparator. And if
these interrupt enable bits are both clear, the comparator interrupt flag will not be affected by any transition.
In the interrupt service routine, you should clear whichever CxIF flag has been set. And to avoid false
triggering (an interrupt occurring because of a previous change), you would usually also clear the CxIF flags
immediately before enabling comparator interrupts – but see the example below.
We can keep the circuit and comparator configuration the same as before. It’s a good idea to leave the
hysteresis in place, as this minimises the number of comparator transitions – limiting the number of times the
interrupt will be triggered.
The comparator configuration is the same as in the previous example, except that we must also enable the
positive (rising) and negative (falling) edge detectors:
; configure comparator 2
movlw b'10100100' ; configure comparator 2:
; 1------- comparator enabled (C2ON = 1)
; --1----- external output enabled (C2OE = 1)
; ---0---- output not inverted (C2POL = 0)
; -----1-- normal power mode (C2SP = 1)
; ------0- hysteresis disabled (C2HYS = 0)
; -------0 asynchronous output (C2SYNC = 0)
banksel CM2CON0
movwf CM2CON0
movlw b'11000000' ; select comparator 2 interrupts and inputs:
; 1------- enable rising edge detection (C2INTP = 1)
; -1------ enable falling edge detection (C2INTN = 1)
; --00---- + in = C2IN+ pin (C2PCH = 00)
; ------00 - in = C12IN0- pin (C2NCH = 00)
movwf CM2CON1 ; -> C2OUT = 1 if C12IN0- < C2IN+,
; C2IF set on all transitions
Then we can enable the comparator interrupt, by setting the C2IE bit.
As mentioned earlier, it is common practice to clear the associated interrupt flag immediately before
enabling an interrupt source, to avoid a previous event, which had set the interrupt flag, triggering the
interrupt prematurely.
However, in this example – how is our light/dark indicator LED to be initialised? We often simply clear the
LED pin (RC1 in this case), so that the LED is initially not lit. But what if the photocell is initially in
darkness? We should start with the LED lit in that case. And if we simply wait for comparator changes to
be detected, the LED will remain unlit until the photocell cycles from dark to light then back to dark.
We could solve this problem by including initialisation code which checks the comparator output on start-up
and then initialises the LED appropriately. But a simpler solution is to force the ISR (which checks the
comparator and updates the LED) to run immediately after interrupts are enabled. And we can do that by
setting the C2IF flag before enabling the compactor interrupt:
banksel PIR2 ; enable comparator 2 interrupt:
bsf PIR2,C2IF ; set interrupt flag (to force immediate
banksel PIE2 ; interrupt to set initial LED state)
bsf PIE2,C2IE ; set enable bit
In the interrupt handler, we must (as always) clear the interrupt flag:
banksel PIR2
bcf PIR2,C2IF ; clear interrupt flag
Since the comparator interrupt is triggered by any comparator output change, we can’t know whether the
interrupt was caused by the comparator input going high or low.
So, we check the value of C2OUT, and update the LED output accordingly:
; display current comparator 2 output
banksel CM2CON0
btfsc CM2CON0,C2OUT ; if comparator output high
bsf LED ; turn on LED
btfss CM2CON0,C2OUT ; if comparator output low
bcf LED ; turn off LED
; repeat forever
goto main_loop
This may seem to be a complicated way of achieving the same result as in the previous example. The
advantage (which we haven’t exploited here) of this approach is that the processor only has to test the
comparator output and update the LED when an input transition occurs – leaving the processor largely idle
and able to do perform other tasks in the main loop and/or service other interrupt sources.
Complete program
Here is how it all fits together:
;************************************************************************
; *
; Description: Lesson 10, example 3 *
; *
; Demonstrates use of comparator interrupt *
; (assumes hysteresis is used to reduce triggering) *
; *
; Turns on LED when input on C12IN0- < threshold on C2IN+ *
; *
;************************************************************************
; *
; Pin assignments: *
; C12IN0- = voltage to be measured (e.g. pot output or LDR) *
; C2IN+ = threshold voltage (set by voltage divider resistors) *
; C2OUT = comparator output (fed back to threshold via resistor)*
; RC1 = indicator LED *
; *
;************************************************************************
#include "p16f1824.inc"
;***** CONFIGURATION
; ext reset, internal oscillator (no clock out), no watchdog,
; brownout resets on, no power-up timer, no code or data protect,
; no failsafe clock monitor, two-speed start-up disabled
; no write protection, 4xPLL off, stack resets on,
; low BOR threshold, high-voltage programming
__CONFIG _CONFIG1, _MCLRE_ON & _FOSC_INTOSC & _CLKOUTEN_OFF & _WDTE_OFF &
_BOREN_ON & _PWRTE_OFF & _CP_OFF & _CPD_OFF & _FCMEN_OFF & _IESO_OFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
; pin assignments
#define LED LATC,1 ; indicator LED on RC1
constant nLED=1 ; (port bit 1)
;***** Initialisation
start
; configure ports
movlw ~(1<<nLED|1<<4) ; configure LED pin and RC4/C2OUT as outputs
banksel TRISC ; (RA1/C12IN0- and RC0/C2IN+ are inputs)
movwf TRISC
banksel ANSELA ; select analog mode for RA1 and RC0
bsf ANSELA,1 ; -> RA1/C12IN0- and
bsf ANSELC,0 ; RC0/C2IN+ are analog inputs
; configure comparator 2
movlw b'10100100' ; configure comparator 2:
; 1------- comparator enabled (C2ON = 1)
; --1----- external output enabled (C2OE = 1)
; ---0---- output not inverted (C2POL = 0)
; -----1-- normal power mode (C2SP = 1)
; ------0- hysteresis disabled (C2HYS = 0)
; -------0 asynchronous output (C2SYNC = 0)
banksel CM2CON0
movwf CM2CON0
movlw b'11000000' ; select comparator 2 interrupts and inputs:
; 1------- enable rising edge detection (C2INTP = 1)
; -1------ enable falling edge detection (C2INTN = 1)
; --00---- + in = C2IN+ pin (C2PCH = 00)
; ------00 - in = C12IN0- pin (C2NCH = 00)
movwf CM2CON1 ; -> C2OUT = 1 if C12IN0- < C2IN+,
; C2IF set on all transitions
; enable interrupts
movlw 1<<PEIE|1<<GIE ; enable peripheral and global interrupts
movwf INTCON
banksel PIR2 ; enable comparator 2 interrupt:
bsf PIR2,C2IF ; set interrupt flag (to force immediate
banksel PIE2 ; interrupt to set initial LED state)
bsf PIE2,C2IE ; set enable bit
; repeat forever
goto main_loop
END
Recall that, if an enabled interrupt source is triggered while the PIC is in sleep mode, the device will wake
from sleep, set the corresponding interrupt flag to indicate which event occurred, execute the instruction
following ‘sleep’ and, if global interrupts are enabled (GIE = 1), then enter the interrupt service routine.
If you only want to use an external event, such as a comparator change, to wake the device from sleep,
without actually triggering an interrupt, simply disable interrupts (clear GIE) before entering sleep mode.
The comparator can be configured as we did in example 3, except that we now want to detect only rising
comparator output transitions (corresponding to a falling light level):
; configure comparator 2
movlw b'10100000' ; configure comparator 2:
; 1------- comparator enabled (C2ON = 1)
; --1----- external output enabled (C2OE = 1)
; ---0---- output not inverted (C2POL = 0)
; -----0-- low-power mode (C2SP = 0)
; ------0- hysteresis disabled (C2HYS = 0)
; -------0 asynchronous output (C2SYNC = 0)
banksel CM2CON0
movwf CM2CON0
movlw b'10000000' ; select comparator 2 interrupts and inputs:
; 1------- enable rising edge detection (C2INTP = 1)
; -0------ disable falling edge detection (C2INTN = 0)
; --00---- + in = C2IN+ pin (C2PCH = 00)
; ------00 - in = C12IN0- pin (C2NCH = 00)
movwf CM2CON1 ; -> C2OUT = 1 if C12IN0- < C2IN+,
; C2IF set only on rising output transitions
Note that low-power mode has been selected, by clearing the C2SP bit, which is usually more appropriate
for operation in sleep mode, where we presumably want to reduce power consumption.
A short delay (10 ms or so) should be added to allow the comparator to settle before entering standby. This
is recommended because signal levels can take a while to settle after power-on, and if the comparator output
was transitioning from low to high while going into sleep mode, the PIC would immediately wake and the
LED would flash – a false trigger.
To enable wake on comparator change, we need to enable the comparator interrupt, but not global interrupts
(since we’re not actually using interrupts here; we’re only using an interrupt source to wake the device):
; enable comparator 2 interrupt (for wake on change)
bsf INTCON,PEIE ; enable peripheral interrupts
banksel PIE2
bsf PIE2,C2IE ; and comparator 2 interrupt
Within the main loop, we can start by turning off the LED, because we don’t want it lit on start-up:
; turn off LED
banksel LATC
bcf LED
Before entering sleep mode, it is very important to clear the interrupt flag, to ensure that the PIC won’t wake
immediately (due to an earlier comparator change):
; go into standby (low power) mode
banksel PIR2
bcf PIR2,C2IF ; clear comparator 2 interrupt flag
sleep ; enter sleep mode
The PIC will now sleep until it is woken by a comparator change, which we wish to show by lighting the
LED for one second:
; turn on LED for 1 second
banksel LATC ; turn on LED
bsf LED
DelayMS 1000 ; delay 1 sec
Note that the delay is generated by the DelayMS macro, introduced in lesson 6.
Complete program
Here is the complete comparator wake-up example program:
;************************************************************************
; *
; Description: Lesson 10, example 4 *
; *
; Demonstrates wake-up on comparator change *
; *
; Turns on LED for 1 sec when comparator output goes high *
; then sleeps until the next rising output transition *
; (assumes hysteresis is used to reduce false triggering) *
; *
;************************************************************************
; *
; Pin assignments: *
; C12IN0- = voltage to be measured (e.g. pot output or LDR) *
; C2IN+ = threshold voltage (set by voltage divider resistors) *
; C2OUT = comparator output (fed back to threshold via resistor)*
; RC1 = indicator LED *
; *
;************************************************************************
#include "p16f1824.inc"
radix dec
;***** CONFIGURATION
; ext reset, internal oscillator (no clock out), no watchdog,
; brownout resets on, no power-up timer, no code or data protect,
; no failsafe clock monitor, two-speed start-up disabled
; no write protection, 4xPLL off, stack resets on,
; low BOR threshold, high-voltage programming
__CONFIG _CONFIG1, _MCLRE_ON & _FOSC_INTOSC & _CLKOUTEN_OFF & _WDTE_OFF &
_BOREN_ON & _PWRTE_OFF & _CP_OFF & _CPD_OFF & _FCMEN_OFF & _IESO_OFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
; pin assignments
#define LED LATC,1 ; indicator LED on RC1
constant nLED=1 ; (port bit 1)
;***** Initialisation
start
; configure ports
movlw ~(1<<nLED|1<<4) ; configure LED pin and RC4/C2OUT as outputs
banksel TRISC ; (RA1/C12IN0- and RC0/C2IN+ are inputs)
movwf TRISC
banksel ANSELA ; select analog mode for RA1 and RC0
bsf ANSELA,1 ; -> RA1/C12IN0- and
bsf ANSELC,0 ; RC0/C2IN+ are analog inputs
; configure oscillator
movlw b'00111010' ; configure internal oscillator:
; -0111--- 500 kHz (IRCF = 0111)
; ------1- select internal clock (SCS = 1x)
banksel OSCCON ; -> 8 us / instruction cycle
movwf OSCCON
; configure comparator 2
movlw b'10100000' ; configure comparator 2:
; 1------- comparator enabled (C2ON = 1)
; --1----- external output enabled (C2OE = 1)
; ---0---- output not inverted (C2POL = 0)
; -----0-- low-power mode (C2SP = 0)
; ------0- hysteresis disabled (C2HYS = 0)
; -------0 asynchronous output (C2SYNC = 0)
banksel CM2CON0
movwf CM2CON0
movlw b'10000000' ; select comparator 2 interrupts and inputs:
; 1------- enable rising edge detection (C2INTP = 1)
; -0------ disable falling edge detection (C2INTN = 0)
; repeat forever
goto main_loop
END
Conclusion
We saw in this lesson that the comparators on enhanced mid-range PICs can be used to compare the voltages
on various input pins. We also saw how to use the external comparator output to generate hysteresis. And
we saw that the comparators can be used as interrupt sources, and to wake the device from sleep.
This lesson also mentioned that that the comparators can be configured with flexible combinations of inputs,
including internal fixed and variable voltage references – but it didn’t provide any detail on how those
features are used.
So in the next lesson we’ll look at the fixed voltage reference and digital-to-analog converter (DAC) and see
how they can be used with the comparators, avoiding the need to use external voltage dividers to generate
threshold voltages, as we did in this lesson.