IIR filter on PIC24F microcontroller

Discussion in 'Embedded Systems and Microcontrollers' started by msr, Apr 3, 2011.

  1. msr

    Thread Starter Well-Known Member

    Jul 8, 2008

    Im trying to implement a simple first order IIR filter on a MCU (PIC24FJ32GA002), without success until now.
    The filter is a DC tracking filter (low pass filter) whose purpose is to track DC component of a 1.5Hz signal. The difference equation was taken from a TI application note:

    with K = 1/2^8

    I made a MATLAB script to test it and it works well in the simulation.
    Code used:

    Code ( (Unknown Language)):
    1.     K=1/2^8
    2.     b = K
    3.     a = [1 -(1-K)]
    5.     Fs=200; // sampling frequency
    6.     Ts=1/Fs;
    7.     Nx=5000; // number of samples
    8.     nT=Ts*(0:Nx-1);
    9.     fin=1.5; // signal frequency
    11.     randn('state',sum(100*clock));
    12.     noise=randn(1,Nx);
    13.     noise=noise-mean(noise);
    15.     xin=200+9*(cos(2*pi*fin*nT));
    16.     xin=xin+noise;
    18.     out = filter(b,a,xin);


    However I can't implement it on a PIC24F microcontroller. i'm representing the coefficients in Q15 (1.15) format, storing them in short variables and using a long one for multiplications. Here it is the code:

    Code ( (Unknown Language)):
    2. short xn;
    3. short y;
    4. short b0 = 128, a1 = 32640; // Q15
    5. long aux1, aux2;
    7.     // (...)
    9. while(1){
    11.     xn = readADC(adc_ch);
    13.     aux1 = ((long)b0*xn) << 1;
    14.     aux2 = ((long)a1*y) << 1;
    15.     y = ((aux1 + aux2) >> 16);
    17.     delay_ms(5);
    18. }

    Long cast is used to extend the signal so the multiplying operation is done correctly. After each multiplication I shift left one bit to remove the extended signal bit. When summing I shift right 16 bits to get y in Q15 format.

    Im debugging the MCU with Pickit2 and "View->Watch" window (MPLAB IDE 8.53) and testing the filter with a DC signal (I change the DC signal with a potenciometer to test different values). The ADC has 10bit resolution and the MCU is supplied with 3.3V.
    Some results:

    1V --> xn = 312 (correct), yn = 226 (incorrect).
    1.5V --> xn = 470 (correct), yn = 228 (completely wrong)

    What am I doing wrong?
    Any suggestions on how to implement this IIR filter on a 16bit MCU?

    Many thanks in advance :)
    Last edited: Apr 3, 2011
  2. guitarguy12387

    Active Member

    Apr 10, 2008

    Well I don't fully understand your testing method... that may help to explain a bit more.

    But I do notice that your pole is very very close to the unit circle. This means it will be providing lots of gain at high Q. You really need to be careful about overflow/instability with poles of this nature. IIR filters are a bit tricky for this reason.

    I would suggest re-designing the filter using FDA tool. Probably can do just fine with an FIR lowpass filter. Assuming you have some extra memory to spare. If you're married to IIR filters, use FDA tool to export second order sections (biquads). These are often easier to implement because you can match poles and zeros to combat overflow if necessary.

    Check out this quick discussion on selecting biquads:

    Hopefully this helps!
  3. guitarguy12387

    Active Member

    Apr 10, 2008
    Also, you can get away with using only one MAC rather than two if you want.

    accum = (long)x*b;
    accum += (long)y*a;
    y = (short) (accum + 0x4000) >> 16

    NOTE: the 0x4000 is for rounding before truncating... this could help with your overflow problems by reducing truncation errors too.