# PIC 16f877A C program for ADC averaging [duplicate]

This question already has an answer here:

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.

-

## marked as duplicate by Olin Lathrop, Joe Hass, placeholder, Dave Tweed♦May 8 at 14:16

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

## 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
}

-
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. –  Scott Seidman May 8 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.

-