### ADC Voltage Measurement

This schematic shows the addition of a 10k resistor (R2) and a 1uF capacitor (C12). This is just a very crude integrator that will suffice for testing our ADC programming capabilities. The input to the integrator is our newly created PWM output (CCP2) on pin 12. The junction of R2/C12 is fed to pin 2 which is analog-to-digital converter channel 0 (AN0).

#### ADC Software Configuration

The configuration registers we are concerned with when setting up the A/D converter are *ADCON0*, *ADCON1*, *TRISA*.

*ADCON0[7:6]* selects the conversion clock frequency. According to the “Mid-Range MCU Family” datasheet, since we are running at 16MHz, a value of *AN0* as the active analog input. THe *ADON* can be set to

*ADCON1[7]* (*ADFM*) controls the justification of our 10-bit A/D result in the *ADRESH* and *ADRESL* registers. This code requires “left justification”, so this bit is set to *PCFG[3:0]* selects the analog/digital configuration of PORT A. We only require *AN0* to be an analog input. Also, we want to use the internal 5V as the reference to the ADC. We can easily get away with that because the input is being derived from the PIC itself. There is no chance of exceeding the 5V/GND limits with the R/C integrator we’re using. So that gives us a value of

*TRISA* is set to 0x3f since there are no outputs to drive on this port.

#### ADC Acquisition

A conversion is started by setting the *ADCON0*. We are just going to poll the *AN0* which can be a source of error. But for our purposes, this will be “good enough”.

#### ADC Data Display

(This description uses the DL1414 as the display device. See the relevant code in the download package for specific LCD code.) In the main loop at *ADCON0*. When it is clear, we fall into the display routine at *ADRESL/H*. I disable interrupts while I change to bank 1. This is probably not required, but I’m paranoid. The values are placed into 3 registers so that they may be processed. The ADC data is 10 bits long: 8 MSBs are in *ARHimg* (also a copy in Meter) and 2 bits are in *ARLimg[7:6]*. Now, how to display this value?

We will make the assumption that these 10 bits represent a range of 0 to 5 volts. This will be in error depending on your power supply voltage and offsets internal to the PIC. But for our purposes here, we will assume this 5V range is accurate. We can divide the 1024 possible values by 5 giving a result (divisor) of 204. 204 represents 1 volt. Since we are limited to 8-bit math on the PIC, it would be more convenient to scale our operations to 8 bits. This can be done if we reduce both the result and the divisor to 8 bits. Dividing each by 4 we get 1024/4 = 256 and 204/4 = 51. So to calculate whole volts, we can divide *ARHing* (ignoring the 2 LSB in *ARLimg* is identical to a 2-bit right shift, or a divide by 4) by 51.

Because the PIC has no divide instruction, we will use the technique of subtracting the divisor, 51, from from *ARHimg*, and counting (in *Cdigit*) each subtract until a borrow occurs. This all happens on lines 454 throught line 460. Note that on the 16F876, when the subtract instruction causes a ‘borrow’, the C (carry) bit is *Cdigit* holds the correct voltage value for either of these two paths taken.

Now we make *Cdigit* an ASCII value on line 470. We know that its value can only be 0 through 5, so it’s safe to OR it with the value of 0x30. Then it is written into the display buffer at *Char0*. The next display character is always the decimal point so we store that at *Char1*.

So now we have the remainder in *Meter*, and the two LSBs from the ADC are sitll in *ARLimg*. We want to bring all of these bits into a single register. After our ‘division’, the largest value that can possibly be in *Meter* is decimal 50, or 0x32. So we see that bits 7 and 6 of *Meter* will be ’00’. This gives us room to shift-in the 2 LSBs. This happens on lines 476 through 483.

To calculate tenths of a volt, use the same successive subtraction with the value of 21 as ‘divisor’. The tenths result is stored in *Char2*.

The hundredths value should be the reaminder. But because the value of 21 we used does not evenly divide all possible values, we may be left with a value greater than 9. I’m taking the easy way out and am just ‘clipping’ the hundredth value to a maximum of 9. This results in some error but it’s close enough for our purposes right now. This value is then stored in *Char3*, we call the display routine, and start another ADC conversion by setting the *ADCON0*.