Many PIC's have the ability to perform analogue to digital conversions, and in this example the 16F877 is used. It doesn't take any extra components, although a pull down resistor is always good to keep the conversions as accurate as possible, though not needed in some cases. Here's an example:
This example has 2 inputs that are going to be used for ADC's, and an LCD to relay information to the user. ADC needs some settings defined before its use, the compiler will assign default settings if you don't specify them in your program, but its a handy thing to know.
ADIN_RES specifies the resolution of the ADC result, 8 bit allows for values from 0-255 and 10 bit allows 0-1023. 10 bit is usally a good option, as it provides much better accuracy and each increment would be the equivalent of 0.0048V, compared to 8 bit reolution whose increments would be the equivalent of 0.009V.
ADIN_TAD defines the clock source that the ADC uses. FRC indicates that the internal RC Osc is used for the operation, and is guaranteed to work every time. Other options are available, but can degrade the ADC performance . So if accuracy is not important you could use the external Osc and achieve much faster results. See the programmer help file for more detail.
ADIN_STIME Allows the internal capacitors to fully charge before a sample is taken. This may be a value from 0 to 65535 microseconds (us). A value too small may result in a reduction of resolution. While too large a value will result in poor conversion speeds without any extra resolution being attained.
ADCON1 isthe register used to set up which pins are used as anologue / digital, and what pin to compare the voltage with (Vref). Check the datasheet for a more detailed explanation on the ADCON1 register for the PIC your using.
There are many different options, but to keep it easy, the following setting is used in this example;
The above makes all of PORTA analogue and the voltage reference is Vdd. Bit 7 (set as a 1 in the example) is to tell the MCU how to save the data, i.e. left / right justify. We have chosen 1 - right justify, so now the data will always be 0000 00xx xxxx xxxx in 10 bit mode - much easier to work with.
Now for the program itself. The following example performs an ADC on PORTA.0 and then PORTA.1 and displays the value in volts on the LCD. Note the small delay between samples, this is allowing the internal capacitors to charge between samples.
The output on the LCD looks something like this;
I have used floats in this example, and it's not the most efficient way of programming, but once the float algorithm has been loaded into the program, it doesn't consume to much more code whenever you use floats, however, this example compiled to 824 lines of code. You can however use words (16bit registers) to reduce the program size and increase its efficiency, but I always find myself choosing accuracy over speed..
Voltages over 5V
For voltages that exceed 5V that you wish to sample, simply use a voltage divider network like the following;
For best ADC performance of the PIC micro, the input impedance should not exceed 2.5K as the PIC's internal capacitors will take too long too charge/discharge. This isn't really an issue for most projects though, as you can just change the sample time with a couple of settings - see the help file of your compiler for more information. I generally use 10K resistors without any issues.