Watch the Video Tutorial

Most of PIC microcontrollers today have built-in analog to digital converters (ADC) with the number of channels depending on the number of pins a particular microcontroller have. 
Analog signals: Directly measurable quantities in terms of some other quantity
                       Some examples: 
                       Thermometer: The mercury liquid inside the thermometer rises as temperature rises 
                       Car Speedometer: Needle of a car speedometer moves farther right as you accelerate 
                       Audio Amplifier: The volume of an audio amplifier increases as you turn the knob. 
Digital Signals: Have only two states. or 1on or off. 
                       Example: A switch can be either on or off.

The PIC18F4620 (40 pins) has 13 built in ADC channels, the PIC18F2620 (28 pins) has 10 built in ADC channels, the PIC18F1220 (18 pins) has 7 built in ADC channels etc.

Figure 1: ADC Channels

These analog to digital converters allow analog continuous voltages to be converted into a discreet digital numbers inside the PIC as the PIC can only process digital numbers. This can enable a PIC to be connected to analog sensors such as temperature sensors, pressure sensors, humidity sensors, optical sensors, and power sensors. 

Any sensor which can generate a voltage between 0V and a maximum 5V can be used. If the output voltage is higher than 5V, a method to step it down should be used such as a voltage divider with resistors. 


To convert an analog signal to digital format, we have two main steps to process: 
1. Quantizing which is basically to break down analog value in a set set of finite states
                 Example: Let say you have 0-5V signals. 
                               The number of possible states that the converter can output is: N=2n 
                               where n is the number of bits in the AD converter.
                               For a 3 bit A/D converter, N =23=8. 
                               Analog quantization size: Q=(Vmax-Vmin)/N = (5V – 0V)/8 = 0.625V.
We will have a set of voltages from 0 to 5V in a set of discrete states with 0.625V increments:

  • 0V to 0.625V
  • 0.625V to 1.25V
  • 1.25V to 1.875V
  • 1.875V to 2.5V
  • 2.5V to 3.125V
  • 3.125V to 3.75V
  • 3.75V to 4.375V
  • 4.375V to 5V

The resolution of the converter indicates the number of discrete values it can produce over the range of analogue values. The resolution is usually expressed in bits and it is equals to the quantization size (Q).
(Q) = Vrange / 2^n, where Vrange is the range of analog voltages which can be represented. 
The higher the resolution, the accurate the converted value. Most of PIC18F microcontrollers have analog channels with a 10-bit resolution.            
2. Encoding which is now to assign a digital word or number to each state and matching it to the input signal.
If 0V is detected by a 10-bit ADC channel, then once the conversion is complete, 00 0000 0000 should be stored in memory. On the other extreme, if 5V is detected by the ADC channel, 11 1111 1111 should be stored in memory. Any value in between will produce a corresponding binary number.

ADC Configuration with MPLAB Code Configurator

MPLAB is phasing out the PIC18F Peripheral Library which is no longer included in XC8 from v1.35. In this version, you have to download and install them separately into your compiler and they are now called Legacy Peripheral Libraries.

The MPLAB® Code Configurator (MCC) is a user friendly Graphical User Interface (GUI) plug-in tool for MPLAB® X IDE which generates easy to understand C code that is inserted into an MPLAB® X project, based on the settings peripherals configurations and selections made in the Graphical User Interface (GUI).  The advantage of  MCC , it can generate codes not only for PIC18F but for a wide range of PICs including PIC16F and PIC24 series.

To learn more, read the article: MPLAB® Code Configurator

Below is the ADC configuration with MCC. In this example we are using the PIC18F26K20. These are our configurations:

  • Clock Source: FOSC/2 : A/D Clock source definitions (select clock frequency used for internal synchronization of A/D converter.
    It also affects duration of conversion) The clock is critical to produce the fastest but also accurate Analog to Digital Conversion
  • Acquisition Time: 2 TAD: The time to complete one bit conversion is defined as TAD. One full 10-bit conversion requires 11 TAD periods. For correct conversion, the appropriate TAD specification must be met. Check your device datasheet to get the ADC conversion requirements, for PIC18F26K20 the min TAD is 0.7 uS and max 25 us
  • Result Alignment: Right. You can Right justify or left justify the result
  • Positive Reference: we are using the VDD (+5V) as the positive reference.
  • Negative Reference: We are using the VSS (0V) as our negative reference
  • Channel ANo: In the Pin Manager, we enabled pin RA0 which is Analog Channel 0 as our analog pin.

Figure 2: ADC Configuration with MCC

Then all we need to do is click on generate to generate our source and header files. Figure 3 below shows the generated files. The Header files adc.h, mcc.h, pin_manager.h and Source files adc.c, mcc.c and pin_manager.c.

Figure 3: MCC generated files

The functions we can use with the ADC module are declared in the adc.h file:

  • ADC_Initialize(): This routine initializes the Initializes the ADC. This routine must be called before any other ADC routine is called. This routine should only be called once during system initialization.
  • ADC_StartConversion(adc_channel_t channel): This routine is used to select desired channel for conversion. The ADC_Initialize() function should have been called before calling this function.
  • ADC_IsConversionDone(): This routine is used to determine if conversion is completed. When conversion is complete routine returns true. It returns false otherwise.
  • ADC_GetConversionResult(): This routine is used to get the analog to digital converted value. This
    routine gets converted values from the channel specified.


uint16_t convertedValue;

ADC_Initialize();                 //Initialize ADC
ADC_StartConversion(AN1_Channel); // Start conversion on channel AN1
while(ADC_IsConversionDone());    // Check if conversion is done
convertedValue = ADC_GetConversionResult(); // Get the results
  • ADC_GetConversion(adc_channel_t channel): This routine is used to select desired channel for conversion and to get the analog to digital converted value.

Figure 4: Displaying analog voltage from AN0

Below is the main.c file: The voltage across the potentiometer connected to AN0 is read and display on the lcd as shown on figure 4 above.

#include "mcc_generated_files/mcc.h"
#include "lcd.h"
#include "stdio.h"

                         Main application

// This function creates seconds delay. 
// The argument specifies the delay time in seconds
void Delay_Seconds(unsigned char z)
    unsigned char x,y;
    for (y = 0; y < z; y++)
        for (x = 0; x < 100; x++)__delay_ms(10);

//**************Declare Global Variables**************************************
uint16_t convertedValue = 0;
float  voltage ;
char Buffer[20];

void main(void)
    // Initialize the device
    // initialize the LCD

     LCDPutCmd(LCD_CURSOR_OFF);  //LCD Cursor off
     LCDPutStr("ADC with MCC");  //Display String "ADC with MCC"
     LCDGoto(0,1);               //Go to second line
     LCDPutStr("Turn POT on AN0");  //Display String "Turn Potentiometer on AN0"
     Delay_Seconds(2);    // 2 seconds delay
     DisplayClr();        // Clear the display
    while (1)
        convertedValue = 0;
        convertedValue = ADC_GetConversionResult(); 
       voltage = (convertedValue * 5.0)/1023; // convert ADC count into voltage
       LCDPutStr("Voltage = "); //Display "Voltage" on the screen
       sprintf(Buffer, " %.3g", voltage ); // Convert voltage to string, rounded to 3 decimal points number
       LCDPutStr(Buffer); //Display the Voltage on the screen
       LCDPutStr("     "); // Clear after comma 
       LCDGoto(0,0);               //Go to first line
 End of File

ADC Configuration with PIC18F Peripheral Libraries

Microchip XC8 PIC18F Peripheral libraries provide Analog to Digital library functions as shown in table below. 
You can access the PIC18F Peripheral Library Help Document found inside your compiler installation directory in: ..Program Files (x86) Microchip xc8 v1.34 docs MPLAB_XC8_Peripheral_Libraries.pdf (assuming you installed your compiler in the Program Files (x86) directory. v1.34 is the version of your compiler, it might be different if you are using a different compiler).
Search for the PIC you are going to use, click on: “CLICK HERE for the Peripheral Library Support Details for this Device” For a 18F2620 family, the ADC functions are from page 904 in the pdf ).

Watch the Video Tutorial:

A/D Functions Descriptions
BusyADC Is A/D converter currently performing a conversion?
CloseADC Disables the A/D converter
ConvertADC Starts an A/D conversion
OpenADC Configures the A/D converter
ReadADC Reads the conversion result
SetChanADC Selects A/D channel to be used

BusyADC: If the value returned is ‘1’, it indicates that the ADC is busy in conversion. If the value is ‘0’, then it indicates that the ADC has completed conversion.
CloseADC: This function disables the A/D converter module.
ConvertADC: This function starts an A/D conversion. The function BusyADC( ) should be used to find out when the conversion is complete.
OpenADC: This function is used to configure the A/D converter module. The number of arguments that this
function can take depend on the type of microcontroller used.
The PIC18F2620 takes three arguments: config, config2 and portConfig.
The values defined can be bitwise AND ed. For the PIC18F2620 the important definitions are:
A/D Clock source definitions (select clock frequency used for internal synchronization of A/D converter.
It also affects duration of conversion) The clock is critical to produce the fastest but also accurate Analog to Digital Conversion:
ADC_FOSC_16 = FOSC / 16
ADC_FOSC_32 = FOSC / 32
ADC_FOSC_64 = FOSC / 64
ADC_FOSC_RC = Internal RC oscillator
ADC_FOSC_MASK = Mask ADC Conversion Clock Selection bits
A/D Result justification: ADC_RIGHT_JUST = Right justify the result
ADC_LEFT_JUST = Left justify the result
ADC_RESULT_MASK = Mask ADC Result adjust bit

A/D acquisition time select (In order to enable the ADC to meet its specified accuracy, it is necessary to provide a certain time delay between selecting specific analog input and measurement itself. This time is called ‘acquisition time’ and mainly depends on the source impedance).

The time to complete one bit conversion is defined as TAD. One full 10-bit conversion requires 11 TAD periods. For correct conversion, the appropriate TAD specification must be met. Check your device datasheet to get the ADC conversion requirements, for PIC18F26K20 the min TAD is 0.7 uS and max 25 us
ADC_0_TAD = A/D Acquisition Time is 0 TAD
ADC_2_TAD = A/D Acquisition Time is 2 TAD
ADC_4_TAD = A/D Acquisition Time is 4 TAD
ADC_6_TAD = A/D Acquisition Time is 6 TAD
ADC_8_TAD = A/D Acquisition Time is 8 TAD
ADC_12_TAD = A/D Acquisition Time is 12 TAD
ADC_16_TAD = A/D Acquisition Time is 16 TAD
ADC_20_TAD = A/D Acquisition Time is 20 TAD
ADC_TAD_MASK = Mask ADC Acquisition Time Selection bits

Channel: ADC_CH0 = Select Channel 0
ADC_CH1 Select Channel 1
ADC_CH2 Select Channel 2
… up to the last channel…
A/D Interrupt: ADC_INT_ON = Interrupts enabled
ADC_INT_OFF = Interrupts disabled
ADC_INT_MASK = Mask ADC Interrupt Enable/Disable
A/D Vref configuration: ADC_REF_VDD_VREFMINUS

A/D port configuration: ADC_0ANA = All channels are digital
ADC_1ANA = analog: AN0 and remaining channels are digital
ADC_2ANA = analog: AN0-AN1 and remaining channels are digital
ADC_3ANA = analog: AN0-AN2 and remaining channels are digital
… up to the last channel…

An example for the use of OpenADC function is given below:

 //FOSC/2 as conversion clock, Result is right justified,  Aquisition time of 2 AD, Channel 1 for sampling, ADC interrupt off and ADC reference voltage from VDD & VSS
ReadADC: This function reads the ADC Buffer register which contains the conversion value. 

To use more than one ADC channels, you need to select the appropriate number of channels in PortConfig and convert each channel individually.

 Example to convert 3 ADC channels

unsigned int ADCResult[3]= {0};
float voltage;
unsigned char ResultStr[10];

void main(void)
init_ADC();                        // Call initialize ADC
//---sample and convert---- 
for(unsigned char i=0;i<=2;i++) //Loop 3 times to sample and read each channel individually.
ADCON0bits.CHS=i;  //Select a channel to sample (index i = 0 (first channel) when the loop
//runs for the first time, i will be 1 then 2 for each successive runs)
ConvertADC();         // Convert Analog to Digital
ADCResult[i] = (unsigned int) ReadADC();   //Read each channel
voltage = (ADCResult[i]*5.0)/1023;            //convert 10-bit ADC data into voltage 

void init_ADC(void)        //Configure ADC with 3 analog channels


Display the voltage read from analog channel 0 to the LCD display as shown on the circuit diagram above. 

The MCLR is not enabled and the circuit uses internal oscillator at 8MHz so an external crystal device and a resisitor to MCLR pin are not needed. The contrast resistance connected to VSS, VDD and VEE of the LCD is not shown here.

 * File: ADC.c
 * Author: Student Companion SA:
 * Analog to Digital converter with PIC microcontroller
#include "ADC.h"
#include <stdio.h>
#include <stdlib.h>
#include <delays.h>
void init_ADC1(void); //Initialize ADC
void init_XLCD(void); //Initialize LCD display
void DelayFor18TCY( void ); //18 cycles delay
void DelayPORXLCD (void); // Delay of 15ms
void DelayXLCD (void); // Delay of 5ms
//**************Declare Global Variables**************************************
unsigned int ADCResult=0;
float voltage;
unsigned char Buffer[20];
void main(void)
 OSCCON=0x76; //Configure to use 8MHz internal oscillator.
 init_XLCD(); //Call the Initialize LCD display function
 init_ADC1(); //Call the Initialize ADC function
 putrsXLCD("ADC"); //Display "ADC"
 SetDDRamAddr(0x40); //shift cursor to beginning of second line
 putrsXLCD("Display Voltage"); //Display "Display Voltage"
for (int x=0; x<=20;x++) __delay_ms(50); // 1 second delay
 WriteCmdXLCD(0x01); //Clear Screen
ADCResult =0;
 //---sample and convert----
 ConvertADC(); //Start conversion
 while(BusyADC()); //Wait here until conversion is finished
 ADCResult = ReadADC(); //Read the converted data
 voltage = (ADCResult*5.0)/1024; // convert the converted data into voltage
 //we divide by 1024 because it’s a 10-bit converted data
 putrsXLCD("Voltage= "); //Display "Voltage" on the screen
 sprintf(Buffer, "%.3g", voltage ); // Convert voltage to string
 putsXLCD(Buffer); //Display the Voltage on the screen
 putrsXLCD(" "); // Clear after comma  WriteCmdXLCD(0x02); //Home position on LCD
void init_XLCD(void) //Initialize LCD display
 OpenXLCD(FOUR_BIT&LINES_5X7); //configure LCD in 4-bit Data Interface mode
 //and 5x7 characters, multiple line display
 while(BusyXLCD()); //Check if the LCD controller is not busy
 //before writing some commands?
 WriteCmdXLCD(0x06); // move cursor right, don’t shift display
 WriteCmdXLCD(0x0C); //turn display on without cursor
void init_ADC1(void) //Initialize ADC
 /**** ADC configured for:
 * FOSC/2 as conversion clock
 * Result is right justified
 * Aquisition time of 2 AD
 * Channel 1 for sampling
 * ADC interrupt off
 * ADC reference voltage from VDD & VSS
void DelayFor18TCY( void ) //18 cycles delay
void DelayPORXLCD (void) // Delay of 15ms
void DelayXLCD (void) // Delay of 5ms

You can download the full project files (MPLAB XC8 source code and Proteus Schematic design) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files).

Download: ADC_with_MCC_MPLAB Project

Download: ADC_with_MCC_Proteus

Download ADC Header

Download ADC HEX

Download ADC Main C

Download ADC Proteus