Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

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

Status
Not open for further replies.

Picit

Newbie level 5
Joined
Dec 16, 2012
Messages
10
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Activity points
1,372
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.
 

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:

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
}


unsigned char read_a2d(unsigned char channel){
	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;
	return(ADRESH);	// return ADRESH
}

/***** MAIN PROGRAM *****/
void main()
{
    //*** Initialisation  
	unsigned char x,y;
	unsigned char lcd_msg[20], adc_val[15];
	init_a2d();	// initialise the A2D module
	lcd_init(); // initialise the LCD
	while (1) {
	x = read_a2d(4); // ignore x for the moment
	itoa(adc_val,ADRESH,2); // read register directly
	strcpy(lcd_msg,adc_val);
	lcd_goto(0);	// select first line
	lcd_puts(lcd_msg);
	lcd_goto(0x40);	// Select second line
	strcpy(lcd_msg,"AC = ");
	itoa(adc_val,ADRESL,2); // read register directly
	strcat(lcd_msg,adc_val);
	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:

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.
 

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
}


unsigned char read_a2d(unsigned char channel){
	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;
	return(ADRESH);	// return ADRESH
}

/***** MAIN PROGRAM *****/
void main()
{
    //*** Initialisation  
	unsigned char x,y;
	unsigned char lcd_msg[20], adc_val[15];
	init_a2d();	// initialise the A2D module
	lcd_init(); // initialise the LCD
	while (1) {
	x = read_a2d(4); // ignore x for the moment
	itoa(adc_val,ADRESH,2); // read register directly
	strcpy(lcd_msg,adc_val);
	lcd_goto(0);	// select first line
	lcd_puts(lcd_msg);
	lcd_goto(0x40);	// Select second line
	strcpy(lcd_msg,"AC = ");
	itoa(adc_val,ADRESL,2); // read register directly
	strcat(lcd_msg,adc_val);
	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
 

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.

The data sheet for ADCON bits 7-6 (ADCS) says :

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?
 

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.
3. change 'return(ADRESH);' to 'return(ADRESH << 8) + ADREsL;' so the full ADC measurment is returned frm the function.

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
unsigned int ReadADC(int Channel)
{
	ADCON0 &= 0xC7; // clears existing channel selection bits
	ADCON0 |= (Channel & 0x03) << 3 ; // set the new channel selection  bits
	ShortDelay(20); 
	ADCON0 |= (1 <<	GO);  // start the ADC measurement
	do
	{
		#asmline clrwdt
	}while(ADCON0 & (1 << GO));  // poll until ADC has finished

	// return the result as a 16-bit number	
	return ((unsigned int)ADRESH << 8) + ADRESL;
}

and it works fine.

Brian.
 
  • Like
Reactions: Picit

    Picit

    Points: 2
    Helpful Answer Positive Rating
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.

The data sheet for ADCON bits 7-6 (ADCS) says :

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 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.
3. change 'return(ADRESH);' to 'return(ADRESH << 8) + ADREsL;' so the full ADC measurment is returned frm the function.

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
unsigned int ReadADC(int Channel)
{
	ADCON0 &= 0xC7; // clears existing channel selection bits
	ADCON0 |= (Channel & 0x03) << 3 ; // set the new channel selection  bits
	ShortDelay(20); 
	ADCON0 |= (1 <<	GO);  // start the ADC measurement
	do
	{
		#asmline clrwdt
	}while(ADCON0 & (1 << GO));  // poll until ADC has finished

	// return the result as a 16-bit number	
	return ((unsigned int)ADRESH << 8) + ADRESL;
}

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
 

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.
 

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.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top