For the need of more accuracy in my adc value, I have to average the adc value for particular time, what are the types of techniques handled in the averaging Can anyone help me with the example C programs for different types of averaging techniques PIC 16f877A.
2 Answers
First of all, if your input is noisy, you can simply take a number of readings inside a tight loop, say 10, and use the average value from that as your reading.
To average over a period of time, you want to use a rolling average, such as implemented by the following code. Readings are added to an array, and then the average of all the readings is computed and returned. I did not include the initialization of the ADC module, and the ADC channel is hard-coded as 0.
Because of the limited register size of the PIC16, I am using #if statements to select the smallest variable sizes possible (i.e. unsigned char index if array is less than 256).
#define MAX_ADC_VALUES 20
#define ADC_CHANNEL 0
unsigned short adcValues [MAX_ADC_VALUES]; // array of ADC readings
#if MAX_ADC_VALUES < 256
unsigned char adcValueIndex = 0; // current index into array
#else
unsigned short adcValueIndex = 0;
#endif
unsigned char fullArray = 0; // set to 1 after we have filled array
// call this on a periodic basis
unsigned short getAveragedAdcReading()
{
#if MAX_ADC_VALUES < 256
unsigned char i;
#else
unsigned short i;
#endif
unsigned short adcReading; // latest reading from ADC
#if MAX_ADC_VALUES < 66
unsigned short adcAverage; // 10-bit ADC; sum of 65 readings just fit in an unsigned short
#else
unsigned long adcAverage;
#endif
// if your input is noisy, then add a loop here to take several readings in a row and average
ADCON0bits.CHS = ADC_CHANNEL; // channel set here
ADCON0bits.GO = 1; // start ADC conversion
while (ADCON0bits.nDONE); // wait till ADC conversion is over
adcReading = (ADRESH<<8) + ADRESL ; //merge the MSB and LSB of the result
adcValues[adcValueIndex++] = adcReading; // overwrite oldest reading with newest one
adcAverage = 0;
for (i=0; i < MAX_ADC_VALUES; i++)
{
adcAverage += adcValues[i];
}
if (fullArray)
{
adcAverage /= MAX_ADC_VALUES; // get average of MAX_ADC_VALUES readings
}
else
{
adcAverage /= adcValueIndex; // use this if array not filled yet
}
if (adcValueIndex >= MAX_ADC_VALUES)
{
adcValueIndex= 0; // if at end of array, loop around
fullArray = 1; // we have now filled array, can divide by MAX_ADC_VALUES from now on
}
return (unsigned short)adcAverage; // cast only needed if adcAverage is unsigned long
}
-
\$\begingroup\$ You could handle this as a running sum by subtracting the value that falls out of the array and adding the new value. Also, this becomes much more sleek if the array size is a power of two, and the divides would be replaced by shifts. Neither of these tweaks, of course, are as good for teaching the basic concept. \$\endgroup\$ Commented May 8, 2014 at 11:06
Have a look at this book: The Scientist and Engineer's Guide to Digital Signal Processing
It is available completely free of charge online on: http://www.dspguide.com/pdfbook.htm
You should take a look at Chapter 15: Moving Average Filters. It is a very simple, yet powerful technique to increase the accuracy of your ADC.