11. ADC
So the point of the ADC is that there are a lot of IC’s out there that give out an analog output. Why would you use such an IC in today’s digital age? Well, doing so will save you pins at the expense of noise immunity. Many thermal-couple IC’s, for example, measure temperature as a voltage output. Many gyroscopes and accelerometers also work with similar outputs. When designing with analog signals, it always better to be cautious because crosstalk and interference can, and will, make your readings inaccurate (giving you big headaches). Measuring the voltage of a device over a 5 meter cable for example, will probably give you a voltage drop depending on the resistance of the cable. These are factors that need to be taken into account, depending on the accuracy demands of the application.
There are several pins that must be configured correctly before the ADC will work. If you look at the PIC24 datasheet there are pins with the ANxx prefix. These are the analog input pins. In addition, the Vref+ and Vref- pins also need to be connected accordingly if you opt to use them as your reference voltages (you can also use AVdd and AVss). The reference voltages are used as the high and low voltage, against which the analog inputs will be compared.
There are several registers that you need to be familiar with. One of the more important register is AD1PCFGH. This register switches the ANxx pin into analog mode (otherwise those pins are controlled by the TRISx, LATx, and PORTx registers).
One feature that needs to be understood is that although there are many analog pins, there is only one or two ADC module (depending on the PIC model) on the IC itself. The sampling of different input pins is done through a series of multiplexors that engages/disengages the proper pins to and from the internal ADC. The ADC itself can sample 4 inputs at the same time at 10 bit sampling on the PIC24H series (only one input at a time on the PIC24F). This means that sampling 4 individual inputs on the PIC24H is usually pretty straight forward, but anything above 4, a bit more programming and ingenious timing is required. You’ll need to manual turn these multiplexors on and off to get your data. If further accuracy is needed the ADC can be configured as a 12 bit converter but can only sample one input at a time. The 12 bit ADC is only found on the PIC24H series.
A lot of the features on the ADC are useful for specific applications. This said, the ADC is so versatile that many of the features are rarely used for low frequency sampling. For this tutorial’s purpose, I will set up the ADC in its simplest form. For this reason, the code was written for the PIC24F.

The AD1CON1 register is the main control register and has several bits that turn the module on and off. It also has bits that tell whether the sampling of a particular input is completed. Needless to say, loops will probably be used to sample this “DONE” bit in your sampling functions. The other two control registers AD1CON2 and AD1CON3 controls the interrupt settings, the multiplexors involved in the sampling as well as the sampling clock used for the conversion process. Lastly the AD1CHS0 register controls how the inputs are set up, and against which voltage levels it will be compared. On the PIC24H series, there are 4 ADC channels in one ADC module, which can be configured with the various AD1CHSxxx registers.
The following program samples the voltage input from AN4. It was configured on a PIC24FJ64GA002. It uses the ADC in the simplest form possible. It uses the following settings just for reference:
AD1CON1
- Turn ADC module on
- Turn on during idle
- Data in integer form
- Auto convert
- Auto sample start
AD1CON2
- Use vref+ and vref-
- do not scan inputs
- ignore interrupts
- 16 word buffer
- use MUX A
AD1CON3
- sample using internal RC clock
- auto sample set to the most amount of time
- clock division for sampling clock
(main.c)
/*
Engscope Tutorial
JL
May 6, 2009
ADC module PIC24F
*/
#include “../h/system.h”
#include “timer.h”
//config
_CONFIG2(0xFBFD);
_CONFIG1(0x377F);
long value;
//main loop
int main(void)
{
//OSCCON = 0x77C0; //select Internal Clock
OSCCON = 0x33C0; //select Primary Oscillator, External XL, PPL
CLKDIV = 0x0000; //do not divide
//Initiate
ADCInit();
while(1)
{
//process events
DelaymSec(10);
ADCProcessEvents();
value = (long) voltage;
value = (value * 3300) / 1024;
}
}
The high and low level header files look like the following:
(adc.h)
/*
Engscope Tutorial
JL
May 6, 2009
ADC module PIC24F
*/
//some variables
extern unsigned int voltage;
//init function
extern void ADCInit();
//main process function
extern void ADCProcessEvents();
(adc.c)
/*
Engscope Tutorial
JL
May 6, 2009
ADC module PIC24F
*/
#include “../h/system.h”
#define ADC_VOLTAGE 4 //select input
//declare variables
unsigned int voltage;
//init function
void ADCInit()
{
AD1CON1 = 0x80E4; //Turn on, auto sample start, auto-convert
AD1CON2 = 0x6000; //Vref+, Vref-, int every conversion, MUXA only
AD1CON3 = 0x1F05; //31 Tad auto-sample, Tad = 5*Tcy
AD1CHS = ADC_VOLTAGE;
AD1PCFGbits.PCFG4 = 0; //Disable digital input on AN5
AD1CSSL = 0; //No scanned inputs
}
//main process function
void ADCProcessEvents()
{
while (!AD1CON1bits.DONE);
voltage = ADC1BUF0;
}
In the high level header, I configured the module with the ADCInit() function. Next, sampling is simply a matter of reading out the values.
Regardless of how the module is configured, ADC in general is a tricky endeavor. Many factors can affect the accuracy of the sampling, as well as the results desired. Frequency of sampling, for example, can be a huge factor is how successful the conversion is implemented. The PIC24H will allow for higher performance in the frequency domain, and allows for up to 4 channels of sampling. However, for simple low frequency sampling, the PIC24F might suffice. In addition, the length of the trace or wire, and the susceptibility to noise of that trace will affect the conversion results. All these factors must be considered when designing a circuit.
Table of Contents
Previous – I2C – Part 3 – Advanced Functions

Entries (RSS)
Is the comment for the ADC code on the ADCInit() function correct for the following line. Isn’t PCFG4 really AN4 instead of AN5?
AD1PCFGbits.PCFG4 = 0; //Disable digital input on AN5