# [SOLVED]PIC16F88 ADC returns half the values expected-expecting 1024 and getting much less?)

Status
Not open for further replies.

#### Picit

##### Newbie level 5
PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much less?

Hi, I've been using a PIC16F88 to control 7 segment displays, LCD controllers and a BCD all without problem, but I'm struggling to understand the ADC.

The setup:
I have setup the PIC to read a value from a 10k trim pot linked to the 5v supply that feeds the PIC.
The PIC is using the internal clock and setup to run at 4Mhz.
The ADC is setup to use the internal voltage reference e.g. Avdd and Avss.
The ADCON register is setup for 10bit right justified.
The binary output from the registers ADRESH and ADRESL are fed to the LCD

The problem:
At startup, with the trim pot set to minimum the LCD reads 0 as expected.
As I wind up the Trim pot the values increase as expected.
With the trim pot at maximum the registers read 00000001:11001101 (461)
Even taking a direct wire from +5V I get the same values
I'm expecting something like 00000011:11001101 (972) e.g. close to the max ADC value 1024.

I'm not sure if i'm doing something wrong or I have misunderstood how the ADC works. Please help. Thank you. Any ideas welcome.

##### Banned
Re: PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much le

Zip and post your code and circuit.

#### rohitkhanna

##### Banned
Re: PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much le

As you wind the trimpot up, at what point/ voltage does the reading stop increasing ? Noting this might give some insight into whats happening.

With a properly setup ADC, the reading should be able to go to 1023. Why are you expecting anything less ?

Also - after a quick look at the PIC manual, I notice that the ADC clock prescalar must be chosen carefully. For the 4MHz case, it can only be Fosc/8 or Fosc/16. Selecting anything else (or NOT programming this at all) might result in strange results like yours.

cheers!

Last edited:

#### Picit

##### Newbie level 5
Re: PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much le

Code Extracts:
Code:
TRISA  = 0b00010000;          // configure RA4 (only) as an input
ANSEL = 0b00010000;	   // disable analog on port a except RA4

void init_a2d(void){
CMCON = 7;		// disable comparators
ADCON0=0xA0;	       // select Fosc/32 channel 4
ADCON1=0x80;	       // select right justify result. A/D port configuration use internal vdd
ADON=1;		       // turn on the A2D conversion module
}

channel&=0x07;	// truncate channel to 3 bits
ADCON0&=0xC5;	// clear current channel select
ADCON0|=(channel<<3);	// apply the new channel select
GO=1;	// initiate conversion on the selected channel
while(GO)continue;
}

/***** MAIN PROGRAM *****/
void main()
{
//*** Initialisation
unsigned char x,y;
init_a2d();	// initialise the A2D module
lcd_init(); // initialise the LCD
while (1) {
x = read_a2d(4); // ignore x for the moment
lcd_goto(0);	// select first line
lcd_puts(lcd_msg);
lcd_goto(0x40);	// Select second line
strcpy(lcd_msg,"AC = ");
lcd_puts(lcd_msg);
delay_ms(1000);
}
}

Thanks for the response. Here's an extract of the code. As you can see I did set up the pre-scalar and have tried a couple of options but get the same result. As for expecting anything less than 1023 I agree with you - that's what I am trying to achieve.

As for your question. The reading increases inline with the winding of the trim pot. It doesn't just stop. The highest reading is when the trim pot hits the max point. Thanks for you help.

Last edited:

#### betwixt

##### Super Moderator
Staff member
It would be better to return (ADRESH << 8) + ADRESL in your ADC read routine so the whole value is returned and to wait for conversion to finish before returning. Try changing "continue" to "{ }" or better still, if you are using the watchdog timer, reset it between the { and }.

Brian.

#### rohitkhanna

##### Banned
Re: PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much le

Code Extracts:
Code:
TRISA  = 0b00010000;          // configure RA4 (only) as an input
ANSEL = 0b00010000;	   // disable analog on port a except RA4

void init_a2d(void){
CMCON = 7;		// disable comparators
ADCON0=0xA0;	       // select Fosc/32 channel 4
[B][COLOR="#FF0000"]	ADCON1=0x80;	       // select right justify result. A/D port configuration use internal vdd[/COLOR][/B]	ADON=1;		       // turn on the A2D conversion module
}

channel&=0x07;	// truncate channel to 3 bits
ADCON0&=0xC5;	// clear current channel select
ADCON0|=(channel<<3);	// apply the new channel select
GO=1;	// initiate conversion on the selected channel
while(GO)continue;
}

/***** MAIN PROGRAM *****/
void main()
{
//*** Initialisation
unsigned char x,y;
init_a2d();	// initialise the A2D module
lcd_init(); // initialise the LCD
while (1) {
x = read_a2d(4); // ignore x for the moment
lcd_goto(0);	// select first line
lcd_puts(lcd_msg);
lcd_goto(0x40);	// Select second line
strcpy(lcd_msg,"AC = ");
lcd_puts(lcd_msg);
delay_ms(1000);
}
}

Thanks for the response. Here's an extract of the code. As you can see I did set up the pre-scalar and have tried a couple of options but get the same result. As for expecting anything less than 1023 I agree with you - that's what I am trying to achieve.

As for your question. The reading increases inline with the winding of the trim pot. It doesn't just stop. The highest reading is when the trim pot hits the max point. Thanks for you help.

All i can see is that you have selected ADCS<2:0> as 000, which implies Fosc/2 or 500nS which is flagged as "2: These values violate the minimum required TAD time."

Fix this first before trying anything fancy like left-shift instead of right-shift blah blah

#### Picit

##### Newbie level 5
Re: PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much le

All i can see is that you have selected ADCS<2:0> as 000, which implies Fosc/2 or 500nS which is flagged as "2: These values violate the minimum required TAD time."

Fix this first before trying anything fancy like left-shift instead of right-shift blah blah

I'm not sure I follow. The only bit set in ADCON1 is bit 7 ( right justify ). Bit 6 (ADCS2) is set to 0.

If ADCS2 = 0 (which it is):
00 = FOSC/2
01 = FOSC/8
10 = FOSC/32
11 = FRC (clock derived from the internal A/D module RC oscillator)*

Therefore having ADCON set to 0xA0 gives 10100000 which gives FOSC/32 (bits 5-3 set the channel).

Please explain how you get Fosc/2? What have I missed?

#### betwixt

##### Super Moderator
Staff member
Picit is right about the ADCON bits. I think the underlying problem is as stated in post #5, the 'continue' is being executed when the ADC is still taking the measurement and because of the rather dangerous program construction, the ADC is then being read and it's result used.

It would be far safer to return the ADC reading from the 'read_a2d()' routine and deal with result later, it also lets you see the real ADC value being read in one go rather than as two register values at different times. I suggest doing this:

1. change 'unsigned char read_a2d(unsigned char channel)' to 'unsigned int read_a2d(unsigned char channel). Changing to int will let the real ADC reading be returned.
2. change 'while(GO)continue;' to 'while(GO) {};' If necessary, put something between the brackets so the compiler doesn't optimize it out.

You can now safely use the value returned from the routine as the ADC measurment without risk of the values changing until you take the next measurment.

This is simiar code I use in one of my 16F88 projects:
Code:
//*******************************************************************************
//  Takes a voltage measurment from the selected channel
{
ADCON0 &= 0xC7; // clears existing channel selection bits
ADCON0 |= (Channel & 0x03) << 3 ; // set the new channel selection  bits
ShortDelay(20);
do
{
#asmline clrwdt

// return the result as a 16-bit number
}

and it works fine.

Brian.

Picit

### Picit

Points: 2

#### rohitkhanna

##### Banned
Re: PIC16F88 ADC returns half the values expected. Expecting 1024 and getting much le

I'm not sure I follow. The only bit set in ADCON1 is bit 7 ( right justify ). Bit 6 (ADCS2) is set to 0.

If ADCS2 = 0 (which it is):
00 = FOSC/2
01 = FOSC/8
10 = FOSC/32
11 = FRC (clock derived from the internal A/D module RC oscillator)*

Therefore having ADCON set to 0xA0 gives 10100000 which gives FOSC/32 (bits 5-3 set the channel).

Please explain how you get Fosc/2? What have I missed?

my bad. I referred to pic16F688, and not pic16F88, which you are using.
simple but silly error. Dayumn... but these pic's are pesky !!

:grin:

#### Picit

##### Newbie level 5
Picit is right about the ADCON bits. I think the underlying problem is as stated in post #5, the 'continue' is being executed when the ADC is still taking the measurement and because of the rather dangerous program construction, the ADC is then being read and it's result used.

It would be far safer to return the ADC reading from the 'read_a2d()' routine and deal with result later, it also lets you see the real ADC value being read in one go rather than as two register values at different times. I suggest doing this:

1. change 'unsigned char read_a2d(unsigned char channel)' to 'unsigned int read_a2d(unsigned char channel). Changing to int will let the real ADC reading be returned.
2. change 'while(GO)continue;' to 'while(GO) {};' If necessary, put something between the brackets so the compiler doesn't optimize it out.

You can now safely use the value returned from the routine as the ADC measurment without risk of the values changing until you take the next measurment.

This is simiar code I use in one of my 16F88 projects:
Code:
//*******************************************************************************
//  Takes a voltage measurment from the selected channel
{
ADCON0 &= 0xC7; // clears existing channel selection bits
ADCON0 |= (Channel & 0x03) << 3 ; // set the new channel selection  bits
ShortDelay(20);
do
{
#asmline clrwdt

// return the result as a 16-bit number
}

and it works fine.

Brian.

Hi Brian

Thanks for your response. I incorporated your recommendations into my existing code but unfortunatley I still get the wrong result.

I then replaced my function with yours, but the function never returns. It gets stuck in a loop - nothing ever appears on the LCD. I tried adding a delay in the "do" loop but it still never returns. Any ideas why this might be?

Regards
Picit

#### betwixt

##### Super Moderator
Staff member
If it never returns it would suggest it is still waiting for the ADC to finish it's conversion. Can you post your new code so I can see please.

Brian.

#### Picit

##### Newbie level 5
Brian, I've solved it with your help.
I noticed in your code snippet that you had a "ShortDelay" between changing channel and starting the ADC.
In the sample code supplied with the Hitech C compiler, which mine was based on, this is missing.
Looking at the data sheet again whenever the channel is changed and before the ADC is started it seems that delay is needed for the charging capacitor. I now have the correct reading of 1023 when the trim pot is at max.
Thanks for taking the time to help me.
Kind regards
Picit

Status
Not open for further replies.