An ADC sampling algorithm, median average filtering + recursive average filtering

Foreword

In actual AD acquisition scenarios, there will be effects of periodic changes and occasional pulse fluctuation interference on AD acquisition.

Here a combination of median average filtering + recursive average filtering is used

Refer to the code framework written by predecessors, and also refer to the blog below by blogger GuYH_, and make slight modifications on this basis to write this blog, which can be applied to actual projects. The most comprehensive summary of commonly used ADC sampling digital filtering algorithms! ! ! [Suggested collection]_adc filter algorithm_GuYH_’s blog-CSDN blog

If I have the opportunity in the future, I will set up a host computer to compare the advantages and disadvantages of several algorithms.

Please point out any errors and explain the reprint.

For learning and communication, please send an email to [email protected]

Algorithm explanation

Median filtering: sample 10 times, remove the maximum and minimum values, sum and divide by 8, which has a suppressive effect on pulse waves.

Recursive average filtering: Treat N consecutive sample values as a queue. The length of the queue is fixed at N. Each time a new data is sampled, it is put into the end of the queue, and the original data at the head of the queue is discarded (first in, first out principle) ), perform an arithmetic average operation on the N data in the queue to obtain a new filtering result. It has good suppression effect on periodic interference and has high smoothness; it is suitable for high-frequency oscillating systems.

Advantages: Suitable for AD acquisition with periodic changes and occasional pulse fluctuation interference.

Disadvantages: low sensitivity; poor suppression of occasional pulse interference, not suitable for occasions with severe pulse interference.

Code

Because the project uses a microcontroller that is lower than 51, AD sampling cannot be performed at too high a frequency, otherwise it will occupy a lot of CPU; the number of sampling cannot be set too high, otherwise it will occupy a lot of RAM space.

Median filtering every 10ms

Recursive averaging filtering is performed every 100ms

adc.h

#ifndef __ADC_H
#define __ADC_H

#include "sys.h"


#define AD_Channel_Num 4 //The number of ADC channels
#define AD_Sample_Num 4 //Number of samples

void AdcInit(void);
u16 ADC_Sample(u8 adch);
void AdcLoopTask(void);

typedef struct {
    u16 V_Chip_0V6;
    u16 V_Bat;
    u16 V_In;
    u16 I_Cur;
    u8 sampleCnt; //sampling count
    u8 bSampleInit; // Flag bit to remove the previous sample values, 0 means removal, 1 means start filtering
    u8 u8ArraryIndex; //The index of the current sample
u16 u16SampleValue[AD_Channel_Num]; // Target array for DMA transfer during rule conversion
u16 u16ValueSum[AD_Channel_Num]; //The sum of ADC sampling values
u16 u16Value[AD_Channel_Num]; // Average filtered AD value
} ADC_S;

extern ADC_S stAdc;

#endif //__ADC_H

adc.c

#include "sys.h"

ADC_S stAdc;
/************************************************
* @function name AdcInit
* @Description ADC loop task
* @parameter none
* @return value none
* @Note None
*********************************************/
voidAdcInit(void)
{
TRISA0 = 1; //Set RA0 as input
TRISA2 = 1; //Set RA2 as input
TRISA5 = 1; //Set RA5 as input
}

/************************************************
* @function name AD_Sample
* @Description AD sampling
* @parameter adch - detection channel
* @return value ad_result - 8 times AD average
* @Note: The sampling channel needs to be set as an analog port by itself, sample 10 times, and take the average of the eight middle times as the sampling result and store it in adresult.
*********************************************/
u16 ADC_Sample(u8 adch)
{
u32 adsum = 0;
u16 admin = 4096, admax = 0;
u8 adtimes = 0;
u16 ad_temp,adresult;
u8j;
ADCON1 = 0B00000101; //Left aligned, use VDD 2.0V as AD reference
ADCON0 = 0X81 | (adch << 2); //ADCclk = Fosc/32
asm("nop");
asm("nop");

for(j=0;j<10;j + + )
{
GODONE = 1; //Start conversion
u8 i = 64;
\t\t
while(GODONE)
{
__delay_us(100); //Delay 100us (compiler built-in function)
if(0 == (--i)) //Delay 6.4ms and still no AD conversion is completed, exit the program
return;
}
\t\t
ad_temp=(ADRESH<<4) + (ADRESL>>4); //Calculate the 12-bit AD value
adresult = ad_temp;
\t\t
if(ad_temp > admax)
admax = ad_temp; //AD sampling maximum value
else if(ad_temp < admin)
admin = ad_temp; //AD sampling minimum value
\t\t
adsum + = ad_temp;
}
adsum = adsum - admax - admin;
adresult = adsum >> 3; //8 averages as the final result
return adresult;
}

/************************************************
* @function name AdcSampleAllChanel
* @Description Sample all ADC channels
* @parameter none
* @return value none
* @Note None
*********************************************/
static void AdcSampleAllChanel(void)
{
stAdc.u16SampleValue[0] = ADC_Sample(0x0F); //0.6V voltage inside the chip
IO_Vbat_Sample_Ctrl = 1;
stAdc.u16SampleValue[1] = ADC_Sample(0x00); //battery voltage
IO_Vbat_Sample_Ctrl = 0;
stAdc.u16SampleValue[2] = ADC_Sample(0x05); //Input voltage
stAdc.u16SampleValue[3] = ADC_Sample(0x02); //Current sampling voltage
}

/************************************************
* @function name AdcMoveAverageFilter
* @Description ADC recursive average filtering method
Treat N consecutive sample values as a queue, and the length of the queue is fixed to N
Each time a new data is sampled, it is placed at the end of the queue, and the original data at the head of the queue is thrown away (first in, first out principle)
Perform an arithmetic average operation on the N data in the queue to obtain a new filtering result.
Advantages: good suppression of periodic interference, high smoothness; tried for high-frequency oscillation systems
Disadvantages: low sensitivity; poor suppression of occasional pulse interference, not suitable for occasions with severe pulse interference
* @parameter none
* @return value none
* @Note None
*********************************************/
static void AdcMoveAverageFilter(void)
{
u8 i = 0;
/**************************** u16ValueSum plus the latest value and remove the oldest value****** *************************/
for (i=0; i<AD_Channel_Num; i + + )
{
stAdc.u16SampleValue[i] &= 0x0FFF;
stAdc.u16ValueSum[i] + = stAdc.u16SampleValue[i];
stAdc.u16ValueSum[i] -= stAdc.u16ValueArray[i][stAdc.u8ArraryIndex];
}

/********************************** Average the number of AD_Sample_Num recently sampled********** *********************/
if ( 0 == stAdc.bSampleInit )
{
for (i=0; i<AD_Channel_Num; i + + )
{
stAdc.u16Value[i] = stAdc.u16SampleValue[i];
}
}
else
{
for (i=0; i<AD_Channel_Num; i + + )
{
stAdc.u16Value[i] = stAdc.u16ValueSum[i]>>2; //Recursive mean filtering based on mean filtering
}
}

/****************************** u16ValueArray adds the latest value**************** ******************/
for (i=0; i<AD_Channel_Num; i + + )
{
stAdc.u16ValueArray[i][stAdc.u8ArraryIndex] = stAdc.u16SampleValue[i];
}

/**************************** AD Cursor Update****************** ****************/
    stAdc.u8ArraryIndex + + ;
    if( stAdc.u8ArraryIndex >= AD_Sample_Num ) {
        stAdc.u8ArraryIndex = 0;
        stAdc.bSampleInit = 1;
    }
}

typedef struct {
u16 V_Chip_0V6;
    u16 V_Bat;
    u16 V_In;
    u16 I_Cur;
} ADC_TEST_S;
ADC_TEST_S stAdc1;
/************************************************
* @function name AdcLoopTask
* @Description ADC loop task
* @parameter none
* @return value none
* @Note None
*********************************************/
void AdcLoopTask(void)
{
\t
AdcSampleAllChanel();
stAdc.sampleCnt + + ;
if (stAdc.sampleCnt > 10)
{
stAdc.sampleCnt = 0;
AdcMoveAverageFilter();
stAdc.V_Chip_0V6 = stAdc.u16Value[0];
stAdc.V_Bat = stAdc.u16Value[1];
stAdc.V_In = stAdc.u16Value[2];
stAdc.I_Cur = stAdc.u16Value[3];
}
}

How to improve recursive average filtering that takes up a lot of RAM space

What is subtracted is not the value of the head of the team, but the average value obtained last time.

If there are 10 channels and the average number of recursive filters is 10, then after improvement, 10*10*2=200 bytes can be saved.

Here I collected 4 channels, and the number of recursive average filters is 4 (8 RAM overflowed before the improvement, crying and laughing)

Before improvement

After improvement:

Saves 4*4*2=32byte space