Shop OBEX P1 Docs P2 Docs Learn Events
Fast Averaging in PASM — Parallax Forums

Fast Averaging in PASM

PhilldapillPhilldapill Posts: 1,283
edited 2009-04-17 03:48 in Propeller 1
I've got a really nice ADC object for the TLV2543 from TI. The driver was written by Tim Moore, but I'd like to change it up a little. First off, the driver keeps an average of every channel. Basically, it gets the last "average", multiplies it by 7, adds the new sample to it, then divides the total by 8. I don't like this kind of average because it takes a while for the true average value to stabilize. What I have in mind, is to keep a running average - i.e. a buffer of the last 8 samples.

I think I've got the concept down, but I'm just wondering if there is a faster way to do it. In my approach, the last 8 samples would be stored in contiguous memory blocks, call them $00, $01, $02...$07. My approach would be to move the sample in block $01 into block $00(dumping the sample in $00), then moving $02 into $01, etc... $07 into $06, then move my new sample into $07. Once I have the updated sample list, I would add all 8 samples, and shift right three bits to divide by eight. Is there a faster/better way to do this?

Comments

  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2009-04-16 22:12
    Philldapill,
    ·
    Why·move the values from 01-->00 ... 02-->01 ... etc.. in the first place?· Just keep a pointer where you’re writing in the·most recent sample.
    ·
    The Average doesn't care what order the numbers are in... Just add them all together and divide.· If you use a buffer that is a power of two, then all you need is a·shift instruction for the division after you add the numbers.
    ·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.
  • PhilldapillPhilldapill Posts: 1,283
    edited 2009-04-16 22:23
    Good idea, Beau. I tinker with existing PASM code and don't write my own. I never knew you could do that... I assume I would need the pointer to wrap back around to the first data block once I get to the end of the buffer?
  • JonnyMacJonnyMac Posts: 9,194
    edited 2009-04-16 22:32
    Yes, and that's easy: if you have an eight-element buffer all you have to do is add one (to your pointer) and then AND with %111 -- when you hit eight the AND will wrap you back to zero. To get the average add all eight elements and then shift the result right by 3 to do the division. As Beau points out, using a power of two can be very advantageous in these circumstances.
  • PhilldapillPhilldapill Posts: 1,283
    edited 2009-04-16 22:35
    Wow, I like that AND idea. That's very clever... I was thinking I would have to implement some "if" logic just to handle the pointer. So basically, wrapping back around would take ONE instruction each sampling cycle? That's better than I thought - MUCH better!
  • TomSTomS Posts: 128
    edited 2009-04-16 22:42
    The methods have a different response to step changes in the ADC value. The circular buffer will start responding immediately, the single memory location for all eight samples could take 8 cycles before you see a change. Also you can do rounding if it suites you. Just test the second bit position and if it is a one, add one to the result of the division.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tom
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2009-04-16 22:51
    This topic was covered here just this past Monday.

    -Phil
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2009-04-16 23:10
    When you say, "Iit takes a while for the true average value to stabilize." that might mean the code is not initializing the accumulator. On the first pass, set the accumulator equal to the first value. Then it will be correct from the start without having to build up. The same applies to the window average, on the first pass, fill the buffer with the first value.

    When doing this type of averaging, whether the exponential smoothing filter or the window average, I like to maintain the integer accumulator at a multiple of the input value. This helps to maintain precision or also to improve the resolution if desired due to the interpolation effect. If done in floating point, all the extra digits of precision allow the average to slop around. But with integer math, it is easier to go down than to go up and that introduces a bias. For example, suppose the value has been 100 for a long time, and suddenly changes to 101. If the window is 8 readings long, the entire buffer will have to fill with 101 before the average (with integer division /8) jumps up to 101. But on the way down, if the buffer is full of 101s, it only takes one 100 to make the resulting average to jump back down. There is a similar effect with the exponential filter. Instead, initialize the accumulator with at least 8 times the initial value. Then take the accumulator * 7/8 and add the new value. The state of the accumulator effectively has three fractional bits that can slop around or can be used for interpolation. The more extra bits the better.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • PhilldapillPhilldapill Posts: 1,283
    edited 2009-04-16 23:29
    Ok, next question about bit logic...

    If you OR %0100 with %0010, you get %0110. This is essentially turning a certain bit to a 1.
    If you "andn" %1101 with %0010, you get %1111 which toggles the bit. If you andn again on the result, you get the original binary number.

    Since I know how to toggle, and turn on a certain bit, how do you turn OFF a bit? i.e. set it to zero regardless of the state?
  • kuronekokuroneko Posts: 3,623
    edited 2009-04-16 23:52
    Philldapill said...
    If you "andn" %1101 with %0010, you get %1111 which toggles the bit. If you andn again on the result, you get the original binary number.
    XOR toggles, AND[noparse][[/noparse]N] is used for clearing stuff.
  • mparkmpark Posts: 1,305
    edited 2009-04-16 23:54
    Philldapill said...

    If you "andn" %1101 with %0010, you get %1111 which toggles the bit.
    I think you're thinking of xor here.
    andn is what you use to clear a bit: given destination %1111 and source %0010, andn leaves %1101 in the destination.
  • kwinnkwinn Posts: 8,697
    edited 2009-04-17 00:21
    If you average a number of samples that is a power of 2, lets say 8, there is a fast and simple way to calculate the average.

    INITIALIZE THE VARIABLES

    for i = 0 to 7
    buffer(i)=0
    next i
    sum = 0
    average = 0

    READ AND CALCULATE THE AVERAGE

    and i %0111
    sum = (sum - buffer(i)) + adcreading
    buffer(i) = adcreading
    average = ( sum + 4 ) >> 3
    i = i + 1
  • PhilldapillPhilldapill Posts: 1,283
    edited 2009-04-17 01:15
    Next question: In PASM, how do you get the ina value of a single pin? I want to get this value_1, shift another value_2 left, and then OR value_1 with value_2 in order to concatenate this bit. I'm trying to read in the data from a device...

    EDIT: Is this what I'm looking for?

    ············· test····· datainm,ina············ wc····· 'read > 250ns (Td(I/O-Data)) after clock low
    ············· rcr······ datain,#1······················ 'read LSB, ends up in top 12 bits

    Post Edited (Philldapill) : 4/17/2009 1:21:30 AM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2009-04-17 03:48
    Philldapill,

    you have the right idea... set the pin mask so that it is all "zeros" except for the pin you are interested in

        test   {pin-mask} , ina wc
        rcr    datain,    #1
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 4/17/2009 2:53:41 PM GMT
Sign In or Register to comment.