an example, the voltage at tap 2 is 2.5V. The converted
value (using the formula) is 2. 5 128 = 320.
The 10-bit binary value of 320 is 01 0100 0000. As
you can see, the upper three bits (bits 9-7), 010, are the
value 2. Any value between 01 0000 0000 (256) and 01
0111 1111 (383) will also work. This yields a window of
127 or — with a 5V power supply — about 620 mV. By
selecting the divider as I have, you can see that the
voltage tap is centered in the window and yields the best
solution, taking into account noise and circuit tolerances.
Since the ADC is ratiometric, the actual value of the
positive reference is not relevant (keeping within the
specifications of the PIC) as long as the input to the ADC
does not exceed this value. The voltage divider resistors
can stay the same as shown — also independent of the
For a specific application, you may want to change
the divider to only two or three resistors. For example, if
you want range 3 and mode 1 (or range 1 and mode 3),
the divider could be what you see in Figure 3. Notice that
the total resistance is still 8K even though the positive
reference is 5V. One parameter you need to take into
consideration when selecting the resistor values is that the
specifications for the ADC state that the maximum
resistance of the source should be less than 10K ohms.
It is possible to extend this method beyond using only
the upper three bits. Using the upper four bits will yield
up to 16 possible “states.” This will cut the noise/tolerance
range in half to 63 counts of the ADC, or about 310 mV.
Theoretically, this method could be extended to the
full number of ADC bits, but I would hesitate using it
beyond six bits (a 68 mV window) mainly due to noise
and other circuit tolerances. Keep in mind that six bits is
about 1.5%, so using 1% resistors in the divider would be
absolutely necessary. Even then, you would want to verify
the voltage values at each tap.
Some Program Specifics
In order to get the best resolution for the ranges, the
program changes the CPU clock speed depending on the
range selected, as follows:
Ranges 0-4: 32 MHz clock = 8 MHz instruction rate =
125 ns period
Range 5: 8 MHz clock = 2 MHz instruction rate =
500 ns period
Ranges 6 and 7: 2 MHz clock = 0.5 MHz instruction
rate = 2 µs period
The initialization of the processor starts the CPU with
the 32 MHz CPU clock. Running at 32 MHz requires the
use of the internal PLL which takes about 1 ms to stabilize.
If range 5 is selected, the program will decrease the CPU
clock to 8 MHz. If range 6 or 7 is selected, the CPU
frequency will be lowered to 2 MHz.
These frequencies were selected in order to meet my
goal of eight decades of resolution. Also, keep in mind
that the PIC processors have four cycle instructions; for
instance, a 32 MHz clock results in an instruction time of
Range 0 uses a timing loop with eight instructions so
that the time to execute the loop is 1 µs. Ranges 1
through 7 use timer 1 with a preset value read from a
table in program space. For all ranges — except 0 — the
timer 1 clock is derived from FCPU/4 with the prescaler
value set to 8, which makes the timer 1 clock frequency =
The ADC reading for the pulse width and delay/off-time is used in software timing loops which — for ranges 1-
7 — determine the number of times timer 1 times out.
There are several configuration macros which have
been implemented to help a user who may want to
customize the program:
DEBUG: Defining this value to 1 reassigns several of
the I/O pins so that you can use the PIC debugger.
FAST: Define this value to 1 to speed up some of the
times by reading the ADC only once during start-up. This
means that not only the mode and ranges but the pulse
width and delay time will also be fixed once the start-up
code has completed.
AD_NO_BITS_0_1: Defining this to 1 causes the ADC
read function to clear bits 0 and 1 of the reading. This will
make the pulse width and delay/off-time values less
susceptible to noise, at the expense of reducing the
resolution by a factor of four while maintaining the same
full scale capability.
AD_USE_ 8_BITS: Defining this to 1 causes the ADC
read function to use only the upper eight bits of the
conversion. This is different from AD_NO_BITS_0_1
in that the resolution is unchanged, but the full scale
capability of each range is reduced by a factor of
EDGE_REG: Define as IOCAP to trigger on
positive edges; define as IOCAN to trigger on
One of the ADC control registers is modified —
based on the selected range — to make the sample
36 December 2016