CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Reading ADC values (Little Help needed)

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
nizar445



Joined: 09 Jun 2009
Posts: 23

View user's profile Send private message

Reading ADC values (Little Help needed)
PostPosted: Thu Nov 25, 2010 8:48 am     Reply with quote

Hi all

I'm trying to write an ADC code for measuring values from an input source which can be changed. Everything seems to work apart from an accuracy issue which is something killing me!!! The numbers after the decimal point keeps changing. So any help in this area would be really appreciated.

BTW: I don’t know a lot about programming so please make it simple Embarassed
I also tried to put a 10uf capacitor across the ADC pin and zero but didn’t help.

My code is:-

Code:
#include <16F88.H>          
#device ADC=10      
#fuses NOWDT, NOPROTECT, NOLVP, XT, NOPUT, NOWRT, NODEBUG, NOMCLR, NOBROWNOUT, NOPROTECT,NOCPD, NOWRT, NODEBUG
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_B5, ERRORS)

void main()
{
   float Temp;      
   float r;
   setup_adc_ports(sAN0);    
   setup_adc(ADC_CLOCK_DIV_8);      
   set_adc_channel(0);   
   

   while(1)
   {
      r =read_adc();         
       Temp = (r*100)/1023.0;
      printf("Temp is  %f \r\n", Temp);
      delay_ms(1000);   
   }
}
pmuldoon



Joined: 26 Sep 2003
Posts: 218
Location: Northern Indiana

View user's profile Send private message

PostPosted: Thu Nov 25, 2010 9:54 am     Reply with quote

I would start with something like this, to see if the 10-bit AD value is stable. That should tell you if you have a hardware problem or if the problem is with the math or floating point conversion.

If it is external, make sure your source impedance is low. They used to recommend < 10K but I've seen some datasheets recommend lower, around 2.5K. Also remember the conversion is the ratio of the PIC's supply voltage and the input voltage (times 1023). If either of those is not steady, you will get erratic readings.

You can also just disconnect the AD input from your circuit and connect it to a solid source (i.e. the PIC VCC line, a 10K or less pot connected across your PIC power pins, etc).

The cap is a good idea, but 10uF is pretty high.

If everything makes sense, rewrite the code to display both 'r' and 'temp' to see if something is going wrong there.

Debugging is all about breaking the problem into smaller and smaller pieces until the problem area is so small that the problem becomes obvious.


Code:
#include <16F88.H>           
#device ADC=10       
#fuses NOWDT, NOPROTECT, NOLVP, XT, NOPUT, NOWRT, NODEBUG, NOMCLR, NOBROWNOUT, NOPROTECT,NOCPD, NOWRT, NODEBUG
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_B5, ERRORS)

void main()
{
   int16 r;
   setup_adc_ports(sAN0);     
   setup_adc(ADC_CLOCK_DIV_8);       
   set_adc_channel(0);   
   

   while(1)
   {
      r =read_adc();         
      printf("AD value is  %4LD \r\n", r);
      delay_ms(1000);   
   }
}
 
Ttelmah



Joined: 11 Mar 2010
Posts: 19278

View user's profile Send private message

PostPosted: Thu Nov 25, 2010 10:04 am     Reply with quote

Probably only part of it is 'programming'.

Your input, _will_ change. Unless the voltage is sitting exactly 'at' an ADC reading, even if everything is perfect, the ADC value will give the value above the real reading, and the value below, at intervals depending on where the voltage is actually sitting, and the noise sources present. Now you are outputting a 'float' value, which is potentially 6.5 digits long, while you are only reading a fractionally over 4 digit reading from the ADC. If the ADC changes by just one count, the output is going to jump by 0.1, and with with the 1023/100 factor, will (for example), at 500/501 counts, jump from 48.8758, to 48.9736, making the change look really bad.

Three things:
1) Smooth the ADC reading. Use a simple filter like:

Code:

#include <16F88.H>         
#device ADC=10     
#fuses NOWDT, NOPROTECT, NOLVP, XT, NOPUT, NOWRT, NODEBUG, NOMCLR, NOBROWNOUT, NOPROTECT,NOCPD, NOWRT, NODEBUG
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_B5, ERRORS)

void main()
{
   float Temp;     
   int32 reading_sum;
   int16 r;
   setup_adc_ports(sAN0);   
   setup_adc(ADC_CLOCK_DIV_8);     
   set_adc_channel(0); 
   delay_us(20); //you need this before the first reading
   reading_sum=read_adc()*7; 'preload' the sum.

   while(1)
   {
      reading_sum+=read_adc(); //Sum the ADC readings
      r=reading_sum/8;
      reading_sum-=r;
      Temp = r/1.023; //why do extra arithmetic....
      printf("Temp is  %3.5f \r\n", Temp);
      delay_ms(1000);   
   }
}

2) Limit the output accuracy displayed from the float - the above code also does this.

3) Look carefully at the electronics themselves - this is the 'non programming' part. While the value will show some fluctuation, it may well be that you have electrical noise problems. Remember that a fluctuation of just 5mV, in the supply, or your incoming signal _will_ be seen as a count change. For real accuracy, consider using a separate Vref, fed from a bandgap reference, and whatever reference is used, careful design of the hardware round the analog signals is vital....

Best Wishes
nizar445



Joined: 09 Jun 2009
Posts: 23

View user's profile Send private message

PostPosted: Tue Nov 30, 2010 5:39 am     Reply with quote

Hi all,

Thanks for your help, but I am still having the same problem!!!? More help please if possible..... Embarassed .

@pmuldoon
I’ve tried all of your suggestions but the results are just the same i.e. connecting the ADC pin to 0v using your code I got 002220121.........

@Ttelmah
Your code seems to give slightly better results! Could you explain it a bit more please?

(int32 reading_sum;) why int32 not float?

reading_sum=read_adc()*7; what does it mean?

(consider using a separate Vref, fed from a bandgap reference) how???

Thanks again for all of your help.
Ttelmah



Joined: 11 Mar 2010
Posts: 19278

View user's profile Send private message

PostPosted: Tue Nov 30, 2010 6:13 am     Reply with quote

Because floats are _slow_, and bulky in terms of code. You are adding a series of integers. The result can only ever be integer, so why use a float. Floats always throw away accuracy (there are only 24bits of useable accuracy in a 32bit float). If you can use integers, _do_.....

The sum, holds a 'rolling' total of eight readings, less one (the last average) Now, if you start with it holding zero, it is going to take quite a few loops to get filled up to this level. I am 'pre-loading' it with the value it would have had if there had been seven identical readings received. On the next loop another one is added (so it now holds the sum of eight readings), then the average generated and this is subtracted putting it back to holding seven readings.

Hardware.
Program the ADC to use the Vref pin as it's reference (look at the options), and attach a stable voltage to the Vref pin.
It really does sound as if you have appalling electrical noise problems in your circuit. Look at the design. Are you using a ground plane?. How is it routed relative to circuitry developing noise?. What decoupling are you using?. How is the analog signal routed?.

Best Wishes
nizar445



Joined: 09 Jun 2009
Posts: 23

View user's profile Send private message

PostPosted: Wed Dec 01, 2010 10:56 am     Reply with quote

@Ttelmah

Thanks a lot for helping me man, you were right, the problem was the noise coming from my supply Smile

But I am still very interested in knowing about your averaging method if possible.
Are you subtracting the average from the 8 reading? Why the average?
Will the output be different each time? (Will the 7 preloaded values change? How? They are outside the loop!!
Sorry for my poor understanding and as I said before I’m not a programmer Embarassed

Thanks in advanced for your help.
Ttelmah



Joined: 11 Mar 2010
Posts: 19278

View user's profile Send private message

PostPosted: Wed Dec 01, 2010 2:58 pm     Reply with quote

It is 'rolling average' system. Imagine a 'simple' average. You add eight values, and divide this by eight. Simple. Downside you need eight readings for each output reading. Worth also saying that division by 4, 8 or 16 should be used becasue these are the fastest to calculate (the 'binary' divisions). How can we make this quicker, and to return a value for each new reading?. This is the 'rolling' average. Imagine you held the sum of the last seven values. Then for each new one, just added one, and do the division. Great. But you'd need to have several sums, or to hold the value from eight samples ago. The way round this (which also increases the damping effect), is to treat the calculated average as the 'imaginary' value to subtract.
There are lots of different types of damping algorithm that can be used, and are better for different problems. This particular one is quite efficient, fast to calculate, easy to adjust for different damping levels (change the division and the preload).
Understand, this will run fine without the preload, but will take several seconds to approach the incoming value on the first pass. The preload just speeds this.
For a system with a generally smooth signal, but occasional spikes, a better system, is the 'olympic' filter. Here you store the maximum signal recorded over a number of samples, and the minimum, make the sum run for two more counts, and subtract these first, throwing away the most extreme values.

Best Wishes


Last edited by Ttelmah on Wed Dec 01, 2010 3:35 pm; edited 1 time in total
gpsmikey



Joined: 16 Nov 2010
Posts: 588
Location: Kirkland, WA

View user's profile Send private message

PostPosted: Wed Dec 01, 2010 3:28 pm     Reply with quote

Hey thanks Ttelmah - I was looking at a similar problem for my air temp readings and your explanation and code was just what I was looking for. Thanks for posting the example !

mikey
_________________
mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group