2
\$\begingroup\$

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.

\$\endgroup\$
0

2 Answers 2

4
\$\begingroup\$

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
}
\$\endgroup\$
1
  • \$\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
2
\$\begingroup\$

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.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.