Jump to content
Sign in to follow this  
Shree

Weird Adc O/p From 16f877a

Recommended Posts

Hello Wizards

I was trying to use adc module and just wrote a simple code to read channel 0 (pinA0 aka pin no 2). The function of the code is to read adc values from pin 2 (AN0) and according to the value display on 7 segment display connected to port B. I have arranged the code such that, for each volt (0V-5V), the display show digits from 1 - 5. Ideally I must get a stable output for a particular setting, but instead what happens is the display keeps on moving from 0-5 at a high speed no matter what the input is. The code I wrote is given below;

/* TESTING ADC CHANNEL 0 OF 16F877A */
#include <system.h>
#pragma CLOCK_FREQ 4000000
#pragma DATA _CONFIG, _XT_OSC & _WDT_OFF & _LVP_OFF & _PWRTE_OFF & _CP_OFF
unsigned char LED[]={ 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f }; //Values for display from 0-9
void adc();
unsigned char adcvalue;
void main()
{
unsigned char AdcFV, t=0;
unsigned int average=0;
trisb=0x00;
portb=0xbf; //display '0' digit
trisa=0b00111111; //All the pins are i/p
clear_bit(adcon0, ADCS1); //The conversion rate 
set_bit(adcon0, ADCS0);// is fosc/8
clear_bit(adcon0, CHS2);//Channel 0 
clear_bit(adcon0, CHS1);// is
clear_bit(adcon0, CHS0);//Selected
set_bit(adcon0, ADON);//ADC module is turned on
clear_bit(adcon1, ADFM);//The result is right justified that is only 8bits of adresh are taken into account adresl is ignored
clear_bit(adcon1, ADCS2);//This gives extra options for conversion rate freq
set_bit(adcon1, PCFG3);//All the
set_bit(adcon1, PCFG2);//inputs
set_bit(adcon1, PCFG1);//except RA0 are digital
clear_bit(adcon1, PCFG0);//RA0 is analog with Vdd and Vss as reference
while(1)
{
	adc(); //Read ADC
	for(t=0; t<16; t++)
	{
	 average +=adcvalue; 
	}
	AdcFV=average/16; //Taking an average of 16 readings in order to get a stable reading of mains supply
	t=0;

	if(AdcFV==0)
	{
		portb=LED[0]|128;
	}
	if((AdcFV>0)&&(AdcFV<=51))
	{
		portb=LED[1]|128;
	}
	if((AdcFV>=52)&&(AdcFV<=102))
	{
		portb=LED[2]|128;
	}
		if((AdcFV>=103)&&(AdcFV<=153))
	{
		portb=LED[3]|128;
	}
	if((AdcFV>=154)&&(AdcFV<=204))
	{
		portb=LED[4]|128;
	}
	if((AdcFV>=205)&&(AdcFV<=255))
	{
		portb=LED[5]|128;
	}
}
}

void adc()
{
set_bit(adcon0, GO);
while(adcon0.GO==1){}
adcvalue=adresh;
}

 

Regards

Shree

 

P.S: The input to adc is pretty much stable. It is derived from the mains => step down transformer=> F/W Rectifier=> A potential divider using 22K CFR and 10K preset => A small capacitor 22uF/10V between ground and junction of 22k and 10K preset => 5V1 zener across the the signal and ground.

Share this post


Link to post
Share on other sites

One point, your ADC calls need to be INSIDE the 16x averaging loop, to read it 16 times, otherwise you've only read the ADC once per the while loop.

 

As a debugging aid, try adding a half second or a second time delay in the while loop so that you can see better what the behaviour is. You could also try putting a steady DC voltage on the ADC input to confirm its operation.

Share this post


Link to post
Share on other sites
Hello Wizards

I was trying to use adc module and just wrote a simple code to read channel 0 (pinA0 aka pin no 2). The function of the code is to read adc values from pin 2 (AN0) and according to the value display on 7 segment display connected to port B. I have arranged the code such that, for each volt (0V-5V), the display show digits from 1 - 5. Ideally I must get a stable output for a particular setting, but instead what happens is the display keeps on moving from 0-5 at a high speed no matter what the input is. The code I wrote is given below;

/* TESTING ADC CHANNEL 0 OF 16F877A */
#include <system.h>
#pragma CLOCK_FREQ 4000000
#pragma DATA _CONFIG, _XT_OSC & _WDT_OFF & _LVP_OFF & _PWRTE_OFF & _CP_OFF
unsigned char LED[]={ 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f }; //Values for display from 0-9
void adc();
unsigned char adcvalue;
void main()
{
unsigned char AdcFV, t=0;
unsigned int average=0;
trisb=0x00;
portb=0xbf; //display '0' digit
trisa=0b00111111; //All the pins are i/p
clear_bit(adcon0, ADCS1); //The conversion rate 
set_bit(adcon0, ADCS0);// is fosc/8
clear_bit(adcon0, CHS2);//Channel 0 
clear_bit(adcon0, CHS1);// is
clear_bit(adcon0, CHS0);//Selected
set_bit(adcon0, ADON);//ADC module is turned on
clear_bit(adcon1, ADFM);//The result is right justified that is only 8bits of adresh are taken into account adresl is ignored
clear_bit(adcon1, ADCS2);//This gives extra options for conversion rate freq
set_bit(adcon1, PCFG3);//All the
set_bit(adcon1, PCFG2);//inputs
set_bit(adcon1, PCFG1);//except RA0 are digital
clear_bit(adcon1, PCFG0);//RA0 is analog with Vdd and Vss as reference
while(1)
{
	adc(); //Read ADC
	for(t=0; t<16; t++)
	{
	 average +=adcvalue; 
	}
	AdcFV=average/16; //Taking an average of 16 readings in order to get a stable reading of mains supply
	t=0;

	if(AdcFV==0)
	{
		portb=LED[0]|128;
	}
	if((AdcFV>0)&&(AdcFV<=51))
	{
		portb=LED[1]|128;
	}
	if((AdcFV>=52)&&(AdcFV<=102))
	{
		portb=LED[2]|128;
	}
		if((AdcFV>=103)&&(AdcFV<=153))
	{
		portb=LED[3]|128;
	}
	if((AdcFV>=154)&&(AdcFV<=204))
	{
		portb=LED[4]|128;
	}
	if((AdcFV>=205)&&(AdcFV<=255))
	{
		portb=LED[5]|128;
	}
}
}

void adc()
{
set_bit(adcon0, GO);
while(adcon0.GO==1){}
adcvalue=adresh;
}

 

Regards

Shree

 

P.S: The input to adc is pretty much stable. It is derived from the mains => step down transformer=> F/W Rectifier=> A potential divider using 22K CFR and 10K preset => A small capacitor 22uF/10V between ground and junction of 22k and 10K preset => 5V1 zener across the the signal and ground.

Shree, your average is being constantly incremented by adcvalue!

 

Regards

 

davidb

Share this post


Link to post
Share on other sites
Shree, your average is being constantly incremented by adcvalue!

 

Regards

 

davidb

 

Hello David,

Ya exactly thats the problem. Do you think reading the adc value inside the for loop would sort this out? I think initialisation of average is also required at the start of fresh conversion. Could this be ok?

Thanks in Advance

Regards

Shree

Edited by Shree

Share this post


Link to post
Share on other sites
Ya exactly thats the problem. Do you think reading the adc value inside the for loop would sort this out? I think initialisation of average is also required at the start of fresh conversion. Could this be ok?

You do need to read the adc value within the loop as Kenn said. However this alone will not solve the problem.

 

At the moment you only read the adcvalue once before the loop and then repeatedly add that value 16 times followed by dividing it by 16 after the loop. The next time around you do exactly the same so that your average increments by the new adcvalue each time round you main loop. This is obviously not what you intended.

 

You could as you suggest initialise the average before the loop. It really depends on what you want to do but you need to consider the sampling rate, number of samples, how many samples you want to average over, moving averages etc.

 

I would suggest you look up averaging filters. As a start I would suggest you look at http://www.piclist.com/techref/microchip/math/filter.htm. The code is asm but should give you a few ideas.

 

Regards

 

davidb

Share this post


Link to post
Share on other sites
Hello Wizards

I was trying to use adc module and just wrote a simple code to read channel 0 (pinA0 aka pin no 2). The function of the code is to read adc values from pin 2 (AN0) and according to the value display on 7 segment display connected to port B. I have arranged the code such that, for each volt (0V-5V), the display show digits from 1 - 5. Ideally I must get a stable output for a particular setting, but instead what happens is the display keeps on moving from 0-5 at a high speed no matter what the input is. The code I wrote is given below;

/* TESTING ADC CHANNEL 0 OF 16F877A */
#include <system.h>
#pragma CLOCK_FREQ 4000000
#pragma DATA _CONFIG, _XT_OSC & _WDT_OFF & _LVP_OFF & _PWRTE_OFF & _CP_OFF
unsigned char LED[]={ 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f }; //Values for display from 0-9
void adc();
unsigned char adcvalue;
void main()
{
unsigned char AdcFV, t=0;
unsigned int average=0;
trisb=0x00;
portb=0xbf; //display '0' digit
trisa=0b00111111; //All the pins are i/p
clear_bit(adcon0, ADCS1); //The conversion rate 
set_bit(adcon0, ADCS0);// is fosc/8
clear_bit(adcon0, CHS2);//Channel 0 
clear_bit(adcon0, CHS1);// is
clear_bit(adcon0, CHS0);//Selected
set_bit(adcon0, ADON);//ADC module is turned on
clear_bit(adcon1, ADFM);//The result is right justified that is only 8bits of adresh are taken into account adresl is ignored
clear_bit(adcon1, ADCS2);//This gives extra options for conversion rate freq
set_bit(adcon1, PCFG3);//All the
set_bit(adcon1, PCFG2);//inputs
set_bit(adcon1, PCFG1);//except RA0 are digital
clear_bit(adcon1, PCFG0);//RA0 is analog with Vdd and Vss as reference
while(1)
{
	adc(); //Read ADC
	for(t=0; t<16; t++)
	{
	 average +=adcvalue; 
	}
	AdcFV=average/16; //Taking an average of 16 readings in order to get a stable reading of mains supply
	t=0;

	if(AdcFV==0)
	{
		portb=LED[0]|128;
	}
	if((AdcFV>0)&&(AdcFV<=51))
	{
		portb=LED[1]|128;
	}
	if((AdcFV>=52)&&(AdcFV<=102))
	{
		portb=LED[2]|128;
	}
		if((AdcFV>=103)&&(AdcFV<=153))
	{
		portb=LED[3]|128;
	}
	if((AdcFV>=154)&&(AdcFV<=204))
	{
		portb=LED[4]|128;
	}
	if((AdcFV>=205)&&(AdcFV<=255))
	{
		portb=LED[5]|128;
	}
}
}

void adc()
{
set_bit(adcon0, GO);
while(adcon0.GO==1){}
adcvalue=adresh;
}

 

Regards

Shree

 

P.S: The input to adc is pretty much stable. It is derived from the mains => step down transformer=> F/W Rectifier=> A potential divider using 22K CFR and 10K preset => A small capacitor 22uF/10V between ground and junction of 22k and 10K preset => 5V1 zener across the the signal and ground.

 

 

Shree,

 

I didn't check the logic - other's comments are probably good ones in this regard. Just for reference, here's some code that works for me using a temperature sensor and the built-in ADC. Be sure to check the use of the single byte value and make sure you are using the right byte (this code uses both bytes with the MAKESHORT function to combine them - I was interested in more precision).

 

 

//Returns the current temperature in Celsius or Farenheit as selected in Setup

 

#define go adcon0.2 //set A/D conversion start bit.

#define AVG_AMT 16 //sets # readings to average

unsigned int get_temp(void)

{

unsigned char x;

unsigned long temperature = 0;

unsigned int total = 0;

 

//Read Channel 4

adcon1 = 0b11000000; //extra divide by 2 for clock (bit 6). Right justified (bit 7)

adcon0 = 0b01100001; //Select Fosc/16 for 8mhz, and channel RA4/AN4, A/D on

 

 

for(x = 0 ; x < AVG_AMT ; x++)

{

delay_ms(1);

go = 1; //Convert RA4 to digital

while(go == 1); //wait for A/D conversion to complete. Resets "go" to 0 when done.

MAKESHORT(temperature, adresl, adresh); //joins the two bytes into one integer result

total += temperature; //Add on to the total

}

temperature = total / AVG_AMT; //these convert the reading from millivolts into degrees Celsius

temperature = temperature * 5000; //example:if ADC output is 75, then * 5000 (supply mV) = 375000

temperature /= 1024; //10 bits = 1024 steps. 375000 / 1024 = 366mV

if (temp_units == FALSE) //FALSE is for Fahrenheit units

{

temperature *=9; //this preserves the accuracy for 1 degree F changes

temperature /= 50; //convert to Fahrenheit

temperature +=32;

}

else temperature /= 10; //TRUE is for Celsius.

 

return temperature;

}

 

Best regards,

 

Kit

Share this post


Link to post
Share on other sites
Shree, your average is being constantly incremented by adcvalue!

Ya exactly thats the problem. Do you think reading the adc value inside the for loop would sort this out? I think initialisation of average is also required at the start of fresh conversion. Could this be ok?

 

For moving average algorithm take a look at this topic http://forum.sourceboost.com/index.php?s=&...ost&p=15723

 

Pavel

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...