Ttelmah
Joined: 11 Mar 2010 Posts: 19412

ADC filtering 
Posted: Wed Apr 22, 2020 2:02 am 


There are lots of different ways of filtering the signal from ADC's.
The 'best' depends massively on what the actual error is coming 'from'.
However I've tried to put links here to the types that people have found
useful in the past.
First the simplest. Sum and average:
Code: 
int16 average(void)
{
//return an average of 8 readings
int32 sum;
int count;
//assuming ADC is already setup, and we are reading channel 1
set_adc_channel(1);
sum=0;
for (count=0;count<8;count++)
{
delay_us(10); //most PIC ADC's require about 10uSec to acquire
sum+=reac_adc(); //add the reading to the sum
}
sum/=8; //gives the average
return sum;
}

Comments here:
The most efficient integer divisions on the PIC are the 'binary' divisions.
So /2, /4, /8, /16 etc.. So if you average 4, 8, 16 etc., samples, the
maths is quicker.
This is a simple and effective average, but takes 8 loops before a
reading is returned. An average can also be done using an array of
values, so the result can update with just one new sample. An example
of this is lower down this thread in a post with a median filter as well.
Second, the 'rolling average':
Code: 
int16 rolling(void)
{
static int32 sum;
int16 val;
static int1 first=TRUE; //used to say this is the first loop
set_adc_channel(1);
delay_us(10);
val=read_adc();
if (first)
{
first=FALSE;
sum=val*8; //load the sum first time
}
else
{
sum=sum+val;
}
val=sum/8;
sum=val;
return val;
}

Here the 'sum' is loaded initially with 8* the ADC reading. Then at
the exit of the loop, this is divided by 8, and this becomes the result.
The sum then has this result subtracted.
The sum is maintained for each successive call.
Each time the sum maintained is 7* the last result, and then the new
value is added to this and the new average generated.
The effect is that the new result takes 1/8th from the new reading,
but is affected by much older results than the simple average. However
with less and less effect the older the reading. This can be a very
effective way of averaging, and gives you a new result with just one
sample each time it is called.
Then the 'olympic' average. This is a 'simple' average, but throwing
away the highest and lowest values first. Makes it a very good way of
rejecting individual 'outliers'.
Asmboy posted a implementation here:
http://www.ccsinfo.com/forum/viewtopic.php?t=50320&highlight=olympic
Then a more sophisticated filter the 'median' filter. This always returns
a value that is actually 'in' the values being read. It tries to return what
you can think of as the 'central' value. Implementation by PCM_programmer
here:
http://www.ccsinfo.com/forum/viewtopic.php?t=3462&highlight=medianfilter
This also has a basic averaging filter, but using a rolling array of values
allowing a new result with just a single new reading.
In all cases the actual amount of smoothing can be increased or decreased
by changing the number involved, but the more damping, the greater the
'lag' that will be experienced in the output.
Obviously all these filtering techniques can be applied to signals other
than the ADC as well, but the ADC is the one that most people will be
dealing with. 
