Filter algorithm?

Author
mihooper
Super Member
  • Total Posts : 212
  • Reward points : 0
  • Joined: 2004/06/28 13:50:00
  • Status: offline
2005/11/27 06:41:53 (permalink)
0

Filter algorithm?

I am sampling several voltages on a somewhat noisy PCB using the A/Ds on a 18F device. The AD readings come back "jittery" and I would like to write a filtering algorithm to process these readings before sending them to my PC-based app (which displays them). Can anyone point me to a simple filter algorithm (preferrably in C)?

thx

MikeH
#1

17 Replies Related Threads

    p.erasmus
    Super Member
    • Total Posts : 1799
    • Reward points : 0
    • Joined: 2004/11/25 03:18:34
    • Location: Saratov Russia
    • Status: offline
    RE: Filter algorithm? 2005/11/27 07:02:21 (permalink)
    0
    Read Application note AN852 it is written for the 18 series,
    Sorry the example code is in asm but explains you thebasics may be you can create you own C code
    Peter

    www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1490&filterID=384
    #2
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/27 07:07:58 (permalink)
    0
    Thanks for the direction! I'll do a little homework.Smile
    #3
    Olin Lathrop
    Super Member
    • Total Posts : 7463
    • Reward points : 0
    • Joined: 2004/02/26 17:59:01
    • Location: Littleton Massachusetts
    • Status: offline
    RE: Filter algorithm? 2005/11/27 07:34:55 (permalink)
    0
    I am sampling several voltages on a somewhat noisy PCB using the A/Ds on a 18F device. The AD readings come back "jittery" and I would like to write a filtering algorithm to process these readings before sending them to my PC-based app (which displays them).

    This is very common. I use 1 to 3 poles of low pass filters on A/D readings probably 90% of the time. This technique is particularly useful is you can sample much more often than you need a new reading. For example, if you need a new reading every 100mS (fast enough for human display) but can sample every 500uS (usually not a hardship for a 18F), then you can apply quite a bit of low pass filtering.

    The basic single pole low pass filter algorithm is:

    FILT <-- FILT + FF(NEW - FILT)

    This algorithm is run once for each new reading from the A/D. NEW is the new incoming value, and FILT is the filtered output value. FF is the "filter fraction" and governs how "heavy" the filter is. As you can see, FF=0 is infinitely heavy since FILT never changes. FF=1 just passes the input to the output without filtering. Useful values are in between, and provide a tradeoff between random noise attenuation and response time.

    Usually on a processor like a 18F, you try to arrange FF to be 2**-N, in other words the multiply by FF is a divide by a power of 2. The multiply by FF then becomes just a right shift by N. Hopefully you can see that the above algorithm is relatively easy to compute.

    You can apply more compute power to get a better tradeoff between random noise attenuation and output response time. Often it's a good idea to use multiple of these filters in series. In other words, the FILT of the first filter becomes the NEW of the second, and so on. The total random noise attenuation is all the FF values multiplied together. However, the response time is shorter when the same overall FF is broken into multiple filters.

    Using the example above, let's say you want at least 90% step response time in 100mS, which is 200 samples. Let's also constrain FF to be 2**-N where N is the largest integer that yields the minimum response time. For a single pole filter, N=7 yields a 90% step response in just under 300 iterations, and N=6 in about 145 iterations. We therefore must pick N=6 for a total random noise attenuation of 64x.

    If a three pole filter is used, each stage can have N=5. This yields a 90% step response in about 170 iterations and has random noise attenuation of 33000x. The second filter has better characteristics at the cost of 3 times more computation. Often that's a good tradeoff.
    #4
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/27 07:43:37 (permalink)
    0
    Olin,

    Excellent! Much simpler than AN852wink. Let me play with this a while. I'll probably be back with questions.

    Thanks!

    MikeH
    #5
    Olin Lathrop
    Super Member
    • Total Posts : 7463
    • Reward points : 0
    • Joined: 2004/02/26 17:59:01
    • Location: Littleton Massachusetts
    • Status: offline
    RE: Filter algorithm? 2005/11/27 07:48:36 (permalink)
    0
    Excellent! Much simpler than AN852wink.

    Thanks. I forgot to mention the FILTBITS program and PLOTFILT script that come with my PIC development environment at http://www.embedinc.com/pic/dload.htm. After you install the software, look at the documentation file for FILTBITS. I wrote that because I use these kinds of filters a lot. I used the PLOTFILT script to quickly see the tradeoffs between N values, number of filter poles, and the unit step response. That's where I got the numbers from in my previous post.
    < Message edited by Olin Lathrop -- Nov. 27, 2005 9:46:47 AM >
    #6
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/28 06:24:46 (permalink)
    0
    I have (partially) successfully implemented the simple filter algorithm suggested above. My first implementation was on the PC side (using C#), which worked well, but I really wanted to PIC to do the filtering before sending the data to my PC application.

    So I have attempted to implement the filtering in my 18F4455 using the routines below. The filtering works, but every few seconds (samples ~ 100ms) the returned values go to something that is nowhere near the actual analog measured values returned from ADRESL & ADRESH. I am using the ICD2 which does not have the ability to trigger on a specified value, so I use my PC application to trigger when the returned value deviates from its expected value. The erroneous values seem to always be around 9e (lsb) and d7 (msb) (correct values tend to be around 50(lsb) and 02(msb). My suspicion is that I am doing something wrong with either the integer math (roundoff) or with the correct type definitions (unsigned char vs ??). Again, the correct values are returned for several seconds before the erroneous values are returned.

    Below are the typedefs for the dataPacket (this is sent via USB from the 18F to my PC-based app), and the filter-related variables (defined in udata area). I have declared an array of byte (prior_average) since I am actually filtering several AD channels. This holds the previous results of each channels' computation. The code snippet below is retyped and simplified for clarity.

    BTW...it took me several hours to realize that <<8 won't actually rotate 8 bits unless you enable integer promotion. Shame on you Microchip!!![:@] Give us newbies better warning of this little gotcha.

    Please let me know if you see an error...

    typedef byte DATA_PACKET[64];

    #pragma udata
    DATA_PACKET dataPacket;
    unsigned char prior_average[12];
    unsigned int current_sample, pa, new_average;

    void GetAnalogInputs(void)
    {
    mADCH7(); //Macro to set analog channel
    DoAD(0,1 ); //A/D routine to set dataPacket.bytes 0 & 1 to ADRESL & ADRESH
    Filter(0,1); //Filter AD results
    }

    void Filter(int lsb, int msb) // Modifies dataPacket[x] & [y]
    {
    current_sample = (dataPacket._byte[msb]<<8)|(dataPacket._byte[lsb]);
    pa = (prior_average[msb]<<8)|(prior_average[lsb]);
    new_average = pa + ((current_sample-pa)/4);
    dataPacket._byte[lsb] = new_average & 0x00ff;
    dataPacket._byte[msb] = (new_average & 0xff00)>>8;
    prior_average[lsb] = dataPacket._byte[lsb];
    prior_average[msb] = dataPacket._byte[msb];
    }
    #7
    jspaarg
    Super Member
    • Total Posts : 2926
    • Reward points : 0
    • Joined: 2005/05/25 16:47:08
    • Location: PA, now MN via NJ,AZ,OR,CA
    • Status: offline
    RE: Filter algorithm? 2005/11/28 09:03:25 (permalink)
    0
    BTW...it took me several hours to realize that <<8 won't actually rotate 8 bits unless you enable integer promotion


    I believe that the ANSI standard disables promotion by default. There is a checkbox on one of the options pages to enable promotion automatically.

    PM personal questions and observations only. Keep technical questions to the forums where everyone can benefit.
    When it comes to binary, there are 10 kinds of people: those who understand, and those who don't.
    #8
    Guest
    Super Member
    • Total Posts : 80499
    • Reward points : 0
    • Joined: 2003/01/01 00:00:00
    • Location: 0
    • Status: online
    RE: Filter algorithm? 2005/11/28 09:14:08 (permalink)
    0
    Also, in the C18 User's Guide, Section 2.7.1 states clearly that C18 does not have integer promotions set as default.

    The ANSI standard mandates that all operations and constants be evaluated as int (16bit). C18 evaluates to the shortest operand in an expression, and to the shortest constant type for numeric constants.

    This is a sensible decision, as it will generate smaller code by default, and in very few cases this will result in problems.
    #9
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/28 15:27:19 (permalink)
    0
    Thanks for pointing out the adherence ANSI per my incidental side comment and where to look in the 136 page user's guide to find the obvious statement that I initially overlooked (in fact, I bet if I really re-read the 136 page user's guide, and the 128 page Getting Started guide again, I wouldn't need to ask any questions...) , but can anyone provide any insight into my primary problem?
    < Message edited by mihooper -- Nov. 28, 2005 3:24:23 PM >
    #10
    Guest
    Super Member
    • Total Posts : 80499
    • Reward points : 0
    • Joined: 2003/01/01 00:00:00
    • Location: 0
    • Status: online
    RE: Filter algorithm? 2005/11/28 15:33:35 (permalink)
    0
    By your primary problem, you mean the noisy analog readings? I find Olin suggestion a very elegant and simple filter. Have you got problems implementing it?

    About the << 8, this is simply a case of char versus int. If you keep the integer promotions turned off, but cast the shift like (unsigned int) a_byte << 8U, you will probably get what you want.
    #11
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/28 15:42:33 (permalink)
    0
    The filtering works, but every few seconds (samples ~ 100ms) the returned values go to something that is nowhere near the actual analog measured values returned from ADRESL & ADRESH.


    Sorry this wasn't more clear. What I am asking for is help understanding why the A/D readings (after filter math maniuplations) go to some totally unreasonable value every few seconds. Am I missing something either in the math routines or the type assignments that would cause the filtered value to return a 0x9ed7 instead of 0x0250?
    < Message edited by mihooper -- Nov. 28, 2005 3:40:18 PM >
    #12
    Guest
    Super Member
    • Total Posts : 80499
    • Reward points : 0
    • Joined: 2003/01/01 00:00:00
    • Location: 0
    • Status: online
    RE: Filter algorithm? 2005/11/28 16:03:33 (permalink)
    0
    Sorry, I can't say anything without trying to replicate it here. If I have time, I will implement some filters with K=0.25, like yours, and run some analog sequences through them.
    In case I do that, will post here again.
    #13
    DSchabel
    Super Member
    • Total Posts : 1714
    • Reward points : 0
    • Joined: 2005/05/24 14:00:34
    • Location: Western NY State
    • Status: offline
    RE: Filter algorithm? 2005/11/28 16:42:15 (permalink)
    0
    ORIGINAL: mihooper

    The filtering works, but every few seconds (samples ~ 100ms) the returned values go to something that is nowhere near the actual analog measured values returned from ADRESL & ADRESH.


    Sorry this wasn't more clear. What I am asking for is help understanding why the A/D readings (after filter math maniuplations) go to some totally unreasonable value every few seconds. Am I missing something either in the math routines or the type assignments that would cause the filtered value to return a 0x9ed7 instead of 0x0250?

    Just a quick guess - you're using unsigned ints, and doing a subtraction. You might be getting an underflow, which sends an impulse into your filter.

    Oh, and one of the best explainations of this kind of filter is on the web. See Chapter 19 of The Scientist and Engineer's Guide to Digital Signal Processing.
    #14
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/28 16:52:28 (permalink)
    0
    BINGO! I'm doing a subtraction of unsigned ints. Instead of me scouring this entier website, can you give me some hints where I might focus my efforts? This definitely appears to be an inpulse since the algorithm output looks like a spike in values.

    Thanks!!
    #15
    DSchabel
    Super Member
    • Total Posts : 1714
    • Reward points : 0
    • Joined: 2005/05/24 14:00:34
    • Location: Western NY State
    • Status: offline
    RE: Filter algorithm? 2005/11/28 17:13:39 (permalink)
    0
    ORIGINAL: mihooper

    BINGO! I'm doing a subtraction of unsigned ints. Instead of me scouring this entier website, can you give me some hints where I might focus my efforts? This definitely appears to be an inpulse since the algorithm output looks like a spike in values.

    Thanks!!

    Just don't use unsigned ints. Use ints.

    Just use right-justified data, and you won't need that MSB for anything except a sign bit (always 0 for your raw data).

    Also, wouldn't it be easier (and less confusing) to just carry the readings AS ints, rather than their component bytes?
    #16
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/11/28 18:44:08 (permalink)
    0
    You win the prize!! Changing the unsigned int to int cured the problem!! Thanks for taking the time to look at this. Perhaps one day I can return the favor.

    Attached Image(s)

    #17
    mihooper
    Super Member
    • Total Posts : 212
    • Reward points : 0
    • Joined: 2004/06/28 13:50:00
    • Status: offline
    RE: Filter algorithm? 2005/12/01 18:50:31 (permalink)
    0
    Also, wouldn't it be easier (and less confusing) to just carry the readings AS ints, rather than their component bytes?


    Ah, yes. Much easier! Thanks for your guidance and recommendations!

    (filtering 6 A/D channels)

    int prior_average[6] = {0,0,0,0,0,0};
    int new_average;

    result = Filter(result, 0); //Filter AD results

    int Filter(int result, int prior_index) // Returns filtered result
    {
    new_average = prior_average[prior_index] +
    ((result-prior_average[prior_index])/4);
    prior_average[prior_index] = new_average;
    return new_average;
    }
    < Message edited by mihooper -- Dec. 1, 2005 6:47:41 PM >
    #18
    Jump to:
    © 2018 APG vNext Commercial Version 4.5