Lab 5: Analog-to-digital conversion (ADC)

Output1

Description

Analog-to-digital conversion (ADC) is necessary because, while embedded systems deal with digital values, their surroundings typically involve many analog signals such as, temperature, speed, pressure, the output of a microphone, etc. They all need to be converted into digital data before being processed by the microcontroller. Today, we will see how to read an external analog signal using a PIC16F688 microcontroller, and display the conversion output (a digital number) on a LCD. The input analog signal will be a varying voltage between 0-5V derived using a potentiometer.

Required Theory

The PIC16F688 microcontroller has a built-in 10-bit ADC with eight input channels. The eight channels are available at RA0, RA1, RA2, RA4, RC0, RC1, RC2, and RC3. They have alternate labels, AN0-AN7, for this function, and are multiplexed into a single Sample and Hold circuit. The output of the Sample and Hold is connected to the input of the A/D converter. The 10-bit conversion result is stored  into the ADC result registers ADRESH (A/D Result Higher byte) and ADRESL (A/D Result Lower byte). Each of these registers is 8-bit. The functionality of the A/D module is controlled by three registers: ANSEL, ADCON0, and ADCON1. The details of these control registers are discussed in ADC channels in PIC16F688.

Circuit Diagram

The test circuit to demonstrate the 10-bit A/D conversion using PIC16F688 is shown below. The test input voltage for ADC is derived from a 5K potentiometer connected across the +5V power supply, and is connected to RA2/AN2 pin of PIC16F688. The supply voltage (+5V) is chosen as the reference voltage for A/D conversion. Therefore, the 10-bit ADC will convert any analog voltage between 0-5V to a digital number ranging from 0-1023. The number will be displayed on the LCD.

This circuit diagram is not much different from the one for our previous lab (Lab 4: Interfacing a character LCD), except it has a 5K potentiometer for generating test analog voltage for ADC operation.

Circuit diagram

Circuit setup on breadboard

Circuit setup using the PIC16F688 breadboard module

Software

The programming involves configuring ANSEL, ADCON0, and ADCON1 registers. Since RA2/AN2 is used as analog input, the corresponding ANSEL bit must be set. In ADCON0 register, set CHS1 and clear CHS0 and CHS2, so that the AN2 channel will be connected to the internal Sample and Hold circuit. Clearing the VCFG bit in ADCON0 will select the supply voltage (+5V) as the reference voltage for A/D conversion. The ADCON1 register is used to select the clock source for A/D conversion. However, the MikroC Pro for PIC has got a built-in library function named ADC_Read() that, by default, uses the internal RC clock for ADC operation. So you don’t need to initialize the ADCON1 register. The complete program is given below.

/*
 Lab 5: Analog-to-digital converter
 Internal Oscillator @ 4MHz, MCLR Enabled, PWRT Enabled, WDT OFF
 Copyright @ Rajendra Bhatt
 October 10, 2010
*/
// LCD module connections
 sbit LCD_RS at RC4_bit;
 sbit LCD_EN at RC5_bit;
 sbit LCD_D4 at RC0_bit;
 sbit LCD_D5 at RC1_bit;
 sbit LCD_D6 at RC2_bit;
 sbit LCD_D7 at RC3_bit;
 sbit LCD_RS_Direction at TRISC4_bit;
 sbit LCD_EN_Direction at TRISC5_bit;
 sbit LCD_D4_Direction at TRISC0_bit;
 sbit LCD_D5_Direction at TRISC1_bit;
 sbit LCD_D6_Direction at TRISC2_bit;
 sbit LCD_D7_Direction at TRISC3_bit;
// End LCD module connections
// Define Messages
 char message1[] = "ADC Value= ";
 char *temp = "0000";
 unsigned int ADC_Value;
 void main() {
  ANSEL = 0b00000100; // RA2/AN2 is analog input
  ADCON0 = 0b00001000; // Analog channel select @ AN2
  CMCON0 = 0x07 ; // Disbale comparators
  TRISC = 0b00000000; // PORTC All Outputs
  TRISA = 0b00001100; // PORTA All Outputs, Except RA3 and RA2
  Lcd_Init();                      // Initialize LCD

  Lcd_Cmd(_LCD_CLEAR);             // CLEAR display
  Lcd_Cmd(_LCD_CURSOR_OFF);        // Cursor off
  Lcd_Out(1,1,message1);           // Write message1 in 1st row
  do {
   adc_value = ADC_Read(2);
   temp[0] = adc_value/1000 + 48; // Add 48 to get the ASCII character value
   temp[1] = (adc_value/100)%10 + 48;
   temp[2] = (adc_value/10)%10 + 48;
   temp[3] = adc_value%10 + 48;
   Lcd_Out(1,11,temp);
   Delay_ms(2000);
  } while(1);
 }

Loading the HEX file with my handy iCP01 PIC programmer.

Output

Vary the potentiometer and you will see the 10-bit conversion result also vary proportionally from 0- 1023.

We recommend EasyEDA for circuit design and PCB prototype
From Schematic to PCB Production, Just Need One Tool - EasyEDA
Cheapest PCB: 10pcs 2-layers 10cm×10cm only $8.21, 3-day build time, 100% E-test

Related Posts

72 comments

  • hi,
    i have project. this project controlled rectifier with dc motor speed control. i am using 16f877a mcu. i want to detect zero crossing and then product triggering pulse for thyristor.this thyristor for delay using potentiometer with adc.
    please help me.

    unsigned int temp_res;
    unsigned char FlagReg;
    sbit ZC at FlagReg.B0;
    void interrupt()
    {
    if (INTCON.INTF){ //kesme oluşması için INTCON.INTF bayrağı kaldırdı
    ZC = 1;
    INTCON.INTF = 0;
    }

    }
    unsigned int sayi1, sayi2;
    void main() {
    ADCON0 =0b00000001;
    ADCON1 =0x0C;
    TRISA= 0b00000001;
    TRISD = 0b11111100; // RD0 is output
    TRISB = 0x01; //RB0 giriş yapıldı
    OPTION_REG.INTEDG = 1; //yükselen kenarda tetikleme
    INTCON.INTF = 0; // INT bayrağını sil
    INTCON.INTE = 1; //Rb0 KESMESİNE MÜSADE ET
    INTCON.GIE = 1; //BÜTÜN KESMELERE MÜSADE ET
    do
    {
    temp_res = ADC_Read(0); // Get 10-bit results of AD conversion
    PORTD = temp_res;
    PORTE = temp_res >> 8; // Send 2 most significant bits to RC1, RC0
    sayi1 =temp_res; // okunan değer
    sayi2=sayi1*3.92;
    sayi2 = sayi1/10;
    if(sayi2>42){
    sayi2=sayi2-1;
    }
    sayi2=sayi2/10;
    if (ZC){ //zero crossing oluştu
    Vdelay_ms(sayi2); // tetikleme pulsenin gecikme süresi

    PORTD.f0 = 1;
    delay_us(1000); //1000us pulse göndersin POZİTİF ALTERNANSDAKİ SCR Yİ TETİKLEMEK İÇİN
    PORTD.f0 = 0;
    delay_ms(9);
    PORTD.f1 = 1;
    delay_us(1000); //1000us pulse göndersin NEGATİF ALTERNANSDAKİ SCR Yİ TETİKLEMEK İÇİN
    PORTD.f1 = 0;
    ZC = 0;
    }
    }
    while(1);
    }

  • Hi!

    I have an analog voltage from a sensor whose output values vary from 0 to 3.3V. But I wanna use the internal fixed reference voltage of 2.048V since my circuit is powered by a battery. Is it possible to use that reference voltage even the sensor output exceeds 2.048V?

  • So what is the sampling rate in here, how can I find it, I have same program but for pressure sensor.

  • sir how to calculate the analog value equivalent of the digital value displayed in the lcd

  • How can i put 2 potentiometer input A/D into PIC16F688?

  • good works I am a begginer pls do you have it on assembly language I only first start with it
    best regard

    • Luis Miguel Schiebelbein

      Hello Abbaadam,
      I believe you can use microschip program to convert te c++ code to assemble. Otherwise you also can donwload any c++/Assembly converter directly from google.com search.
      Hope I could help you.
      Best Regards

  • any one tell me how can i convert numeric values in to alphabet like if the value ranges from 920 to 940 it shows B plz help

  • hello sir, i have need to display the values of pressure transducer on lcd screen. so my doubt is that will this program help me? as in can i replace the potentiometer used here with the pressure transducer ? will it work ? please help sir.. as soon as possible..

  • Sir I have completed all labs till Lab 5 (both on proteus & hardware) I has 2 questions.

    1-pic16f688 has 1 ADC (8 channels)… what does it mean? it has ADC available on 8 pins? if yes then which 8 pins? port A pins? but port A has total 6 pins.

    2- “adc_value = ADC_Read(2);”
    Here we are passing “2” to functional ADC_Read (). How does the function come to know whether it has to read A2 or C2?

    • 8 adc’s which can be switched
      ADC1 – AN0 – Pin 13
      ADC2 – AN1 – Pin 12
      ADC3 – AN2 – Pin 11
      ADC4 – AN3 – Pin 3
      ADC5 – AN4 – Pin 10
      ADC6 – AN5 – Pin 9
      ADC7 – AN6 – Pin 8
      ADC8 – AN7 – Pin 7
      set the TRISA value to configure the pin as an input.
      connect the input voltage to the desired pin and apply the vref to the high side of the potentiometer.
      everything else is programming

  • thank’u for this work. may u please email me some code on how to convert adc result in microntrollers to ascii and display with lcd
    both 8bit and 10bit conversions using assembly language.

  • i read in mikroc ADC library that “This routine initializes PIC’s internal ADC module to work with RC clock. Clock determines the time period necessary for performing AD conversion (min 12TAD).”
    this is mean that ADC works only with RC clock ???

  • Hi all,

    I just get started with PIC32MX795F512L (USB STARTER KIT II) and I have no idea of how I can use my PIC microcontroller to control my 10K potentiometer and see the value of the converted analog to decimal with a variable under NEW WATCH. Any solution method is wellcomed. Thanks very much.

  • that’s pic18f452

  • sir, i’m using pic18f52,i’hav tried to display ADC value to voltage but i dont know where could i made mistakes..can u plz help me..

    #include
    #include
    #include

    void data1(unsigned char *);

    void cmd(unsigned char);

    void delay1(unsigned int);

    void voltage(unsigned char );

    #define lcddata LATC
    #define rs RD0
    #define rw RD1
    #define en RD2

    void main()
    {

    TRISA=0X0FF;
    TRISD=0;
    TRISC=0;
    LATD=0;
    LATC=0;
    LATA=0;

    cmd(0x38);
    cmd(0x0E);
    cmd(0x01);
    cmd(0x06);
    cmd(0x81);
    data1(“volt”);

    while(1)
    {

    ADCON0=0X81;
    ADCON1=0X0E;
    while(GO/DONE==1);
    voltage(ADRESL);
    voltage(ADRESH);
    }

    }

    void voltage(unsigned char a)
    {

    char buff[5];
    float b=a*0.0048;
    sprintf(buff,”%.2f”,b);
    data1(buff);

    }
    void cmd(unsigned char s)
    {

    lcddata=s;
    rs=0;
    rw=0;
    en=1;
    delay1(1);
    en=0;

    }

    void data1(unsigned char *a)
    {
    cmd(0x06);
    while(*a!=”)
    {

    lcddata=*a;
    rs=1;
    rw=0;
    en=1;
    delay1(1);
    en=0;
    a++;
    }

    }

    void delay1(unsigned int k)
    {

    int i,j;
    for(i=0;i<k;i++)
    for(j=0;j<1257;j++);

    }

  • sir ur work is very useful to me such as beginner like me, when i copy the coding for adc value reading to my pic micro c it will be compiled successfully and generate the hex file to me it works on the real pic simulator but no to real circuit. pic simulator and also protiues to program will work fine i am using pic micro c 8. pls help me any programming setting need to do

  • how can i use adc0804 with 16f627a??

  • Hi Sir,

    I’m really interested to understand one part of the codes.
    can u plz explain this part.

    temp[0] = adc_value/1000 + 48;

    temp[1] = (adc_value/100)%10 + 48;

    temp[2] = (adc_value/10)%10 + 48;

    temp[3] = adc_value%10 + 48;

    Why you only used temp up to 4, why 0-3 exactly.??
    And also what is the benifits of doing it by this way.?!!
    Why you divided by 1000,100,10 & 1 exactly.?!!
    Why we are interested to know the reminder.?!!

    I’m really sorry for bothering you with this amount of questions one shot.
    Thank You in Advance.

    • He used each equation for each row from the four bytes that he have. For example to get 0001 as a digital value, you would calculate it by using these equations. each column represents an equation.

  • Let’s say i connect a LDR to pin 11 of the pic… and the other side of the LDR i connect to + the adc value is 1023 and connect it to the – the adc value is 0.
    What is the right way to connect a LDR to a pic microcontroller?

    thank you!
    marC:)

  • ANSEL = 0b00000100; // RA2/AN2 is analog input

    ADCON0 = 0b00001000; // Analog channel select @ AN2

    ansel : bit 0 = 0, bit 1 = 0 bit 2 = 1 (so RA2 is input)
    adcon0 = 0b00001000; = bit 0 = 0, bit 1 = 0, bit 2 = 0, bit 3 = 1;
    so RA3 is input??? no?

    i don’t quite understand

    thank you for helping me out!
    thanks!
    marC:)

  • Hi Raj!
    ADCON0 = 0b00001000;
    the last digit is RA0, so : RA3 is as ADCON??

    isnt’t RA2??

    thank you!
    marC:)

  • I just want the leds to light up sequentially as i vary the pot (like the output of lm3914). Thanks alot.

  • HIi R-B. I’m still have a lot to learn about c-programming. I would like to use this project to display leds instead (in a bargraph style) to display the sampled values. Kindly help with the adjustments. Thanks a lot.

  • If ANSEL = 0b00000100; // AN2
    ADCON0 should be equal to :
    0b00001000

    ANSEL = 0b00000100; // RA2/AN2 is analog input
    ADCON0 = 0b00001000; // Analog channel select @ AN2

    what’s the difference between those two?

    thank you!
    marC:)

  • What is ADCON0 = 0b01001000; ?? sir?

    thank you!
    marC:)

  • What’s the difference between ADC_Read(2) or ADC_Read(5), what makes you think that channel 2 is the right one? i cannot figure it out?
    thanks RAj!

  • can u plz tell me how 2 store ADC value in EEPROM using PIC12F675

  • what is the use of a potentiometer with ADC ??

  • adc_value = ADC_Read(2);

    dear sir i was trying to compile the codes that you have given, but in mikroc compiler gave me an error after the compile the above.

    “undeclared identifier,ADC_Read”in expression”

    tell me what should i do now, and i also need some ADC test codes for pic 16F877A MCU.
    please help me
    thank you.

  • hi R-B,your tutorials are wonderful.I really learned pic from your tutorials.I have some problems.I made a power supply and ? wanna add voltmeter and ammeter to it.I have already added voltmeter succesfully but ? dont know how ? can add ammetter with pic16f688.Code part is quite difficult for me and and ? am newbie in electronics.How can ? use a LCD and a pic16f688 for both voltmeter and ammeter.In fact ? wanna use 4×16 LCD display since ? have + and – polarity power supply and thats why ? wanna show both polarity voltmeter and ammeter in a single LCD display but unfortunately ? just know to use 2×16 LCD display.

  • Its very helpful. The description is very proper and very easy. Good job

  • Very Help..

  • Hie Mr R-B, Iam doing my temperature measurement project based on pic16f688, I want to know the library routines that I can use in compiling using mikroC

  • i have gas sensor to display 300-10000ppm.i have connect it with PIC16F887 at channel RE0-AN5 and i use Vcc as +5v. i want to follow the same code you posted but i just made change on LCD connection with PIC. now, what changes i have to make to let ADC convert the ppm anlaog value to digital and display it on LCD? can you modify

  • iwant 10 bit adc 7 segment display asm code thanks

  • i want asm code 10 bit adc 4 seven segment display with pic 16f 877 thanks

  • great project. I use adc pin to read datA FROM motor to make my version of flight/car simulator

  • Found the Problem R-B

    I notice you saying within the Forum number “15” response ,
    I quote:
    “It is required because the LCD is character based.”
    unquote

    I changed the LCD , I have several, changed it to a 4 line character one , and there it was on the top line

    I also noticed with your Program
    adc_value = ADC_Read(2);
    Since this LCD has 8 display areas,(across and paired 1 & 2, then 3 & 4 and so on)
    like ADC Valu in area 1 and = 0000 in area 2 (adjacent)
    the single line LCD fell down

    ADC value top left display area
    = 0000 top right display area

    Fabulous lil project

    Regards

    Robert

  • Used the Hex.file supplied
    ive since used the same physical pic as the voltmeter and same circuit with the prescaler and it works fine, taking off the prescaler and reprogging the 688 for the ADC, still the same, …..” ADC Valu ” no input

    Thanks R-B

  • Well I have built the Voltmeter and works perfectly
    I have then programmed the 688 for your ADC, and i get “ADC Valu” but
    no readings into the pin, WHY, if the voltmeter works, with the same circuit, i program the 688 for ADC and it dont, … me thinks Software prob

    Robert

  • I was wondering if I could do multiple a/d conversions using the library in MikroC. I says that the function only works with the internal voltage reference. So does that mean that I have to connect 5 volts to the voltage reference as an input in my code?

    • Mark,
      I didn’t get exactly what you are asking, but yes you can do A/D conversions through multiple I/P channels. However, at a time you can connect only one channel to the internal A/D converter. If you want to use the power supply voltage as the reference for A/D conversion, you don’t need to connect the Vref pin to Vcc, you just need to clear the VCFG bit in ADCON0 register.

  • biswajit behera

    excellent tutorials BOSS, keep it up.
    i want a ICP01 PIC PROGRAMMER, kindly reply me if i can get it from you. thanking you and waiting for your reply.

  • Your tutorials are quite helpful. Thanks

  • Would you please explain the following code in detail.

    temp[0] = adc_value/1000 + 48; // Add 48 to get the ASCII character value

    temp[1] = (adc_value/100)%10 + 48;

    temp[2] = (adc_value/10)%10 + 48;

    temp[3] = adc_value%10 + 48;

    • Hey Binoy,
      adc_value stores the 10-bit digital number. In order to display it on a character LCD, you need to convert each digit into ASCII value. adc/1000 gives the thousands digit. Suppose the adc_value = 1020. Then adc/1000 = 1. The ascii value of character ‘1’ is 49, ‘2’ is 50, and so on. So you need to add 48 to decimal digit to get its equivalent ASCII code. It is required because the LCD is character based. Again, adc_value/100 = 10 (for 1020). Then 10%10 = 0 (remainder) is the hundred’s digit. This goes on until you get the unit’s digit.

  • Hi sir. Can you do lab tutorial of ADC & PWM?
    How to correlate the ADC with PWM.
    Thanks

  • Muhammad Sohail

    Hi Sir i am computer engineer & doing my MS Sir i need a little help i have done a student project which is security surveillance system using Wireless sensor network The embedded system of my this project include a sensor , a micro controller , and a transceiver i have used pic18f micro controller now i want to use pic 16f688. Sir I need your help in this regard.
    Regards
    Muhammad Sohail

  • what is the fastest sampling rate this pic can provide? can it reach 512 ksps?

  • Under normal condition, with Vref = +5 V, the ADC output is 0-1023 for analog input 0-5 V. In program, you can change it to be 10-1023 using a simple math. Use this equation:

    New ADC = 10 + 1013*ADC/1023

    Now, when ADC = 0 (corresponds to 0V), the new value of ADC = 10. And when, ADC = 1023 (for 5 V input), the new ADC = 1023. All other voltages between 0 and 5 V will have values between 10-1023.

  • hamza mohamed ansari

    How can I do if I want the value 10-1023 to represent the voltage 0-5V, and the LCD to display values from 0V to 5V when I vary the potentiometer??
    Thank you.

  • How can I do if I want the value 10-1023 to represent the voltage 0-5V, and the LCD to display values from 0V to 5V when I vary the potentiometer??
    Thank you.

  • I have to make a project to measure three AD value, with three potentiometers and display it on a LCD display. I use a PIC16F690. Can you give me, your mail adress so can give you a mail with details, maybe you can healp me with C source code. Thanks.

    • I am sorry I can’t help anybody personally on his or her project; you can use the resources that I have provided on my website, and it is free. That’s all I can do.
      Thank you.

  • do u have example program using PIC18f4580…

  • Pingback: Electronics-Lab.com Blog » Blog Archive » Tutorial on Analog to Digital conversion using PIC

  • Output
    Vary the potentiometer and and you will......

    You have the “and” twice

  • Nice document! Well documented. I will feature it in pcbheaven.com right away 😉

    • What is this ADC_Read(2), and how we combine ADRESH and ADRESL into 10 bit for further calculation

      // Program analog to digital 10 bit conversion show the o/p to LCD

      #include
      #define RS PORTEbits.RE0
      #define RW PORTEbits.RE1
      #define EN PORTEbits.RE2
      #define DATA1 PORTD
      void lcdcmd(unsigned char);
      void lcddata(unsigned char);
      void delay(unsigned char);
      void lcdinit(void);
      void adcinit(void);
      void delay();

      void main(void)
      {
      unsigned long result1;
      unsigned int result=0;
      unsigned char first,second,third,fourth;
      TRISAbits.TRISA0=1; // AN0 channel is used
      TRISEbits.TRISE0=0; //LCD DISPLAY
      TRISEbits.TRISE1=0;
      TRISEbits.TRISE2=0;
      TRISD=0;
      lcdinit();
      adcinit();

      while(1)
      {
      delay(20); //for time acquasition to sample
      ADCON1=0xC0;
      result=result1=0;
      ADCON0bits.GO=1; //start of conversion
      while((ADCON0bits.GO)!=0);
      result1=ADRESH;
      result1=result1<<8;
      result1+=ADRESL;
      ADCON0bits.ADON=0;

      result1=result1*5000/1023;
      result=result1;
      first=(result/1000)|0x30;
      second=((result%1000)/100)|0x30;
      third=(((result%1000)%100)/10)|0x30;
      fourth=(((result%1000)%100)%10)|0x30;

      ADCON1=0x0A;
      delay(1);
      lcdcmd(0x80);
      delay(15);
      lcddata(first);
      delay(15);
      lcddata('.');
      delay(15);
      lcddata(second);
      delay(15);
      lcddata(third);
      delay(15);
      lcddata(fourth);
      delay(15);
      ADCON0bits.ADON=1;

      }
      }

      void lcdinit()
      {
      ADCON1=0X0A; //to make AN5,AN6,AN7 as i/p port
      EN=0;
      delay(0x172);//(0x992)
      lcdcmd(0x38);
      delay(0x172); //for 20ms (250);//(0x992);
      lcdcmd(0x0C);
      delay(15);
      lcdcmd(0x01);
      delay(15);
      lcdcmd(0x06);
      delay(15);
      lcdcmd(0x80);
      delay(50);
      }

      void adcinit()
      {
      ADCON1 = 0xC0; //Fosc/16,CHAN0,ADON=1
      ADCON0=0X41; //right JUSTIFIED,Fosc/16,AN0
      }

      void lcdcmd(unsigned char x)
      {
      TRISD=0;
      DATA1 = x;
      RS = 0;
      RW = 0;
      EN = 1 ;
      delay(1);
      EN = 0;
      }

      void lcddata(unsigned char value)
      {
      TRISD=0;
      DATA1 = value;
      RS = 1;
      RW = 0;
      EN = 1;
      delay(1);
      EN = 0;
      //lcd_wait();
      }

      void delay(unsigned char y)
      {
      unsigned char z,a;
      for(z=0;z<y;z++)
      for(a=0;a<135;a++);

      }

      practically it is not showing the result please guide

Leave a Reply

Your email address will not be published. Required fields are marked *