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