IIR Filter Implementation in C

Nick picture Nick · Mar 31, 2014 · Viewed 16.7k times · Source

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.

Answer

Michael picture Michael · Apr 1, 2014

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).
  • 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 */
}