问题
I am trying to implement an IIR filter in C for the FRDMKL25Z board. My current code is shown below:
#include "Cpu.h"
#include "Events.h"
#include "ADC_1.h"
#include "AdcLdd1.h"
#include "DAC_1.h"
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"
#define NSP 16
static uint16_t DACvalue, ADCvalue;
static LDD_TError Error;
static LDD_TDeviceData *MyDacPtr;
int N=10; // Filter order
double NumCoeff[11]={0.8017, -8.0174, 36.0785, -96.2094, 168.3664, -202.0397,
168.3664, -96.2094, 36.0785, -8.0174, 0.8017};
double DenomCoeff[11]={1.0000, -9.5582, 41.1210, -104.8588, 175.5143, -201.4924,
160.6706 , -87.8720, 31.5447, -6.7119, 0.6428};
double Signal[NSP], FilteredSignal[NSP];
int main(void)
{
/* Write your local variable definition here */
/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
int j, k;
double y, Reg[NSP];
PE_low_level_init();
MyDacPtr = DAC_1_Init(NULL);
for(j=0; j<NSP; j++) Reg[j] = 0.0; // Init the delay registers
for(;;){
for(j=0;j<NSP;j++)
{
for(k=N; k>0; k--) Reg[k] = Reg[k-1]; // Shift the delay register values.
(void)ADC_1_Measure(TRUE); /* do conversion and wait for the result */
(void)ADC_1_GetValue16(&ADCvalue); /* get the result into value variable */
Signal[j]=(ADCvalue/65535)*3.5; /*Convert to volts*/
Reg[0] = Signal[j]; // The denominator
for(k=1; k<=N; k++) Reg[0] -= DenomCoeff[k] * Reg[k];
y = 0; // The numerator
for(k=0; k<=N; k++)y += NumCoeff[k] * Reg[k];
FilteredSignal[j] = y;
DACvalue=(FilteredSignal[j]*65535)*3.5; //Convert back to 16 bit
Error = DAC_1_SetValue(MyDacPtr, DACvalue); /* Set DA converter output */
}
}
Some details of the code:
- I'm using Processor Expert (PE) from freescale to program the embedded board;
- IIR filters coefficient generated using MATLAB;
- As the device needs to filter data in real time I take the minimum required data from the
ADC and filter it through then outputting it to the DAC right after the process;
Once flashed to the board, I get no output from the DAC port whatsoever.
I am open to all suggestions and debugging methods.
回答1:
Here's what you done wrong
- you need to also keep track of previous outputs
- maybe you were trying to, but the funny nested loop wasn't working
- The denominator is applied to the outputs, not the inputs
- The denominator is not really the denominator when you apply the coefficients; it's only called that because the frequency response works like a denominator. The denominator coefficients are multiplied to the history of outputs and their sum is subtracted from the output. (think of moving all the
y(n-1)... y(n-k)
terms to the other side of the equal sign, leaving only the filter output on one side) - The funny denominator coefficient is the that first one, it is multiplied by the new output you're actually trying to calculate! You will divide your whole result by that one to find out what the new output will be. (in most IIR filters this is 1.0, so you can skip this).
- The denominator is not really the denominator when you apply the coefficients; it's only called that because the frequency response works like a denominator. The denominator coefficients are multiplied to the history of outputs and their sum is subtracted from the output. (think of moving all the
- 16 bit Integer divide by 65535 is zero!, and no need to convert to volts (as others have mentioned).
- Only do engineering only at the end, and only if you need to; you don't
Here's my suggestion, while trying to keep the same code structure...
unsigned int ADCvalue,
int Reg[NSP]; // use signed values instead of unsigned, and no need for double for history of ADCvalue
for(j=0; j<NSP; j++) Reg[j] = 0; // Init the delayed input registers
for(j=0; j<NSP; j++) FilteredSignal[j] = 0.0; // Init the delayed output registers
for(;;)
{
for(k=N; k>0; k--) Reg[k] = Reg[k-1]; // Shift the delay register values.
for(k=N; k>0; k--) FilteredSignal[k] = FilteredSignal[k-1];
(void)ADC_1_Measure(TRUE); /* do conversion and wait for the result */
(void)ADC_1_GetValue16(&ADCvalue); /* get the result into value variable */
Reg[0] = ADCvalue - 0x8000; // Save the previous inputs samples (and shift the zero value to 0)
y = 0;
for(k=0; k<=N; k++) y += NumCoeff[k] * Reg[k]; // The numerator
for(k=1; k<=N; k++) y -= DenomCoeff[k] * FilteredSignal[k]; // The denominator
FilteredSignal[0] = y/DenomCoeff[0];
DACvalue= FilteredSignal[0] + 0x8000; // shift the zero value back to unsigned, centered at 0x8000
Error = DAC_1_SetValue(MyDacPtr, DACvalue); /* Set DA converter output */
}
回答2:
Second order IIR-Filter Implementation in C:
static double b[] = {1, -1.4, 1};
static double a[] = {1, -1.3, 0.5};
static double v1m1 = 0, v2m1 = 0, v1m, v2m;
static double iirfilter(double x1) {
double y1 = 0;
y1 = (b[0] * x1 + v1m1) / a[0];
v1m = (b[1] * x1 + v2m1) - a[1] * y1;
v2m = b[2] * x1 - a[2] * y1;
v1m1 = v1m;
v2m1 = v2m;
return y1;
}
来源:https://stackoverflow.com/questions/22765809/iir-filter-implementation-in-c