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

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.

Related Posts

• Shital survase

Can anyone give me program to interface LM-35 to PIC 16f877a in embedded C with comment.

• #include
#include
sbit rs=P1^2; //Register select (RS)
sbit rw=P1^1;
sbit en=P1^0; //Enable (EN) pin

sbit cs=P3^2;
sbit wr= P3^1;
sbit rd= P3^0;
sbit intr= P3^3;
#define lcddata P0

void delay(unsigned int time) //Time delay function
{
unsigned int i,j;
for(i=0;i<time;i++)
for(j=0;j<5;j++);
}

void lcdcmd(unsigned char value) //Function for sending values to the command register of LCD
{
lcddata=value;
rs=0;
rw=0;
en=1;
delay(50);
en=0;
}

void display(unsigned char value) //Function for sending values to the data register of LCD
{
lcddata=value;
rs=1;
rw=0;
en=1;
delay(50);
en=0;

}

void lcdint(void) //function to initialize the registers and pins of LCD
{
P2=0x00;
P2=0xFF; //always use with every lcd of hitachi

lcdcmd(0x38);
lcdcmd(0x0F);
lcdcmd(0x01);
lcdcmd(0x06);
lcdcmd(0x80);
display('a');
delay(50);
lcdcmd(0x81);
display('d');
delay(650);
lcdcmd(0x82);
display('c');
delay(50);
}

void main()
{
char digit[3]={0,0,0}; // ch1,ch2,ch3;
P2=0x00;
P2=0xFF; //Input port
lcdint();
while (1)
{
delay(1002);
cs=0; //chipselect is on now –It is active low pin
wr=0; // write is enabled –It is also active low
delay(102);
wr=1; //start conversion analog to digital
rd=1;
while(intr==1); //The looop runs until intr==1 and when intr==0 it jumps to next iteration
rd=0; //read the digital output from adc

lcdcmd(0xC0);
display(digit[0]+0x30);
display(digit[1]+0x30);
display(digit[2]+0x30);

}}

• Sir I am going to interface the temperature sensor ds18s20 to the renesas microcontroller through ADC . So plz provide code for interface

• Sir actually I want to i

• good

• 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.

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() {
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?

• Ahmed Mostfa

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

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.
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.

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 ???

• Christopher

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.

• sumathi

that’s pic18f452

• sumathi

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)
{

while(GO/DONE==1);
}

}

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.

• Ahmed Mostfa

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!
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.

• Do you mean you want to display 10-bit ADC output to 10 LEDs in binary format?

• 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 ??

• gayan kumara

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.

tell me what should i do now, and i also need some ADC test codes for pic 16F877A MCU.
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.

• Anum Shahid

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

• Thanks, Anum!

• Anum Shahid

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

• The libraries are built-in mikroC compiler. You won’t be able to see the actual library routines in 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

• Sorry, I don’t have one.

• 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
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

• It should work. Did you compile the program yourself or use the Hex file?

• 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.

• Vaxtrezz

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

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

• 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:

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…

• I am sorry I don’t have any example for PIC18F4580.

• ```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 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();

while(1)
{
delay(20); //for time acquasition to sample
result=result1=0;
ADCON0bits.GO=1; //start of conversion
result1=result1<<8;

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;

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);

}
}

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 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