It provides inertion to changing filter value, with inertion resistance as TFilterFreq parameter. Higher parameter value gives higher resistance to changing fiter value. First picture explains everything. Code exploits shifting bits in a word for fast division, and takes just between 354 cycles (ffDiv2) and 423 cycles (ffDiv256) to provide result quickly. I guess that W in LowPassFW name stands that it is a word parameter based version. Longword, byte, or a positive fixed point number version could be also made of it.
Now I will try to explain how it works. For example, if you start with FiltData.Value = 0 and want to apply NewValue = 1000 once using ffDiv2, then after first LowPassFW call FiltData.Value will have half of a difference between FiltData.Value and NewValue which is (1000-0)/2=500. Then in next call to LowPassFW difference between FiltData.Value and NewValue is (1000-500)/2=250 and FiltData.Value will be 500+250=750. After next call FiltData.Value will be 750+125=875, next 875+62=937, and so on and so on (if NewValue is still 1000, of course). So, ffDiv2 will adjust on each call for half of a difference, ffDiv4 will adjust for one fourth of a difference, ffDiv8 one eight of a difference, etc. You get the picture. Therefore ffDiv2 allows filter value to change with much higher frequency then with ffDiv256, which is much, much slower. So, choosing higher TFilterFreq allows us to filter more peaks in analog signal data acquisition and smoother trending data without instant jumps.
Here is some simple test code for better understanding:
Code
program LowPassFilterTest;
{$NOSHADOW}
{ $WG} {global Warnings off}
Device = mega88, VCC=5;
{ $BOOTRST $00C00} {Reset Jump to $00C00}
Import SysTick, SerPort, LCDport;
From System Import LongWord, LongInt, Float;
Define
ProcClock = 16000000; {Hertz}
SysTick = 10; {msec}
StackSize = $0032, iData;
FrameSize = $0032, iData;
SerPort = 9600, Stop1; {Baud, StopBits|Parity}
RxBuffer = 8, iData;
TxBuffer = 8, iData;
LCDport = PortB;
LCDtype = 44780;
LCDrows = 2; {rows}
LCDcolumns = 40; {columns per line}
Implementation
{$IDATA}
{--------------------------------------------------------------}
{ Type Declarations }
type
TFiltData = record
Value : word;
Err : byte;
end;
{--------------------------------------------------------------}
{ Const Declarations }
{--------------------------------------------------------------}
{ Var Declarations }
{$IDATA}
{--------------------------------------------------------------}
{ functions }
TFilterFreq = (ffdiv2,ffdiv4,ffdiv8,ffdiv16,ffdiv32,ffdiv64,ffdiv128,ffdiv256);
procedure LowPassFW(var FiltData : TFiltData; NewVal : word; FilterFreq : TFilterFreq);
var
lw : longword;
LowWord[@lw] : word;
UpWord[@lw+2]: word;
ShiftCount : byte;
BitMask : byte;
LowByte[@lw] : byte;
begin
ShiftCount := ord(FilterFreq) + 1;
BitMask := %11111111 shr (8 - ShiftCount); //Calculate ErrorBitMask
UpWord := 0; //Reset upper part of lw
LowWord := FiltData.Value; //Set Lower part of lw with old FilterOutput
lw := (lw shl ShiftCount) - lw; //Equal to lw := lw * (2^ShiftCount - 1)
lw := lw + LongWord(NewVal + word(FiltData.Err)); //Add NewVal and Old Error
FiltData.Err := LowByte AND BitMask; //Save Modulo of 2^n division
lw := lw shr ShiftCount; //Perform 2^n division
FiltData.Value := LowWord; //Store ResultValue of Filter
end;
{--------------------------------------------------------------}
{ Main Program }
{$IDATA}
var
FiltData: TFiltData;
FilterFreq: TFilterFreq;
Pass, NewValue: word;
i: byte;
begin
FiltData.Value := 0;
NewValue := 1000;
Pass := 0;
LCDclr; { clear display }
LCDcursor(false, false); { display on, cursor off & no blink }
LCDhome; { cursor home }
EnableInts;
loop
Inc(Pass);
LCDxy(0, 0);
Write(LCDout, 'Pass=' + IntToStr(Pass) + ', ');
Write(LCDout, 'NewValue=' + IntToStr(NewValue) + ' ');
LCDxy(0, 1);
LowPassFW(FiltData, NewValue, ffDiv2);
Write(LCDout, IntToStr(FiltData.Value) + ', ');
endloop;
end LowPassFilterTest.