![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
nizar445
Joined: 09 Jun 2009 Posts: 23
|
Reading ADC values (Little Help needed) |
Posted: Thu Nov 25, 2010 8:48 am |
|
|
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
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);
}
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Thu Nov 25, 2010 9:54 am |
|
|
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);
}
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19363
|
|
Posted: Thu Nov 25, 2010 10:04 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
nizar445
Joined: 09 Jun 2009 Posts: 23
|
|
Posted: Tue Nov 30, 2010 5:39 am |
|
|
Hi all,
Thanks for your help, but I am still having the same problem!!!? More help please if possible..... .
@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. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19363
|
|
Posted: Tue Nov 30, 2010 6:13 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
nizar445
Joined: 09 Jun 2009 Posts: 23
|
|
Posted: Wed Dec 01, 2010 10:56 am |
|
|
@Ttelmah
Thanks a lot for helping me man, you were right, the problem was the noise coming from my supply
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
Thanks in advanced for your help. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19363
|
|
Posted: Wed Dec 01, 2010 2:58 pm |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Wed Dec 01, 2010 3:28 pm |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|