=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
    Date: Fri, 16 Jun 2000  09:24:53
    From: Nikolai Golovchenko <golovchenko-at-mail.ru>
      To: pic microcontroller discussion list <PICLIST-at-MITVMA.MIT.EDU>
 Subject: Re: [PIC]: - Who is awake?
--------------------------------------------------------------------------------

I would use all 8 bits available in AVEFRAC for fractional part. This
will reduce the round-off error to almost nothing. And I would like to
keep the input value in NEW not changed.

Also note that the averaging algorithm works only with unsigned
values. To work with signed you need first to bias the signed number
(add 2^(n-1) or xor sign bit), then calculate the average, and remove
bias (substract 2^(n-1) or xor sign bit).

And, to increase precision (or accuracy ?) I would round the final
result before using it by:
        btfsc AVEFRAC, 7
         incf AVE, w       ;w holds rounded result

The modified routine takes also 20 instruction/cycles:
        swapf AVFRAC, w         ;AVFRAC -= AVFRAC / 16
        andlw 0x0F
        subwf  AVFRAC, f         ;(no carry check is performed -
;there is no carry)

        swapf AVE, w            ;AVE:AVFRAC -= AVE / 16
        movwf TEMP
        andlw 0xF0
        subwf AVFRAC, f
        skpc
         decf AVE, f
        movf TEMP, w
        andlw 0x0F
        subwf AVE, f

        swapf NEW, w            ;AVE:AVFRAC += NEW / 16
        andlw 0xF0
        addwf AVFRAC, f
        skpnc
         incf AVE, f
        swapf NEW, w
        andlw 0x0F
        addwf AVE, f
;AVE holds the result
        
 Nikolai

P.S. And all that stuff could be replaced by two parts :)

-----/\/\/\----o----- to PIC ADC input
               |
             -----
             -----
               |
              ===


              
On Thursday, June 15, 2000 Andrew Warren wrote:
> Plunkett, Dennis <PICLIST-at-MITVMA.MIT.EDU> wrote:

>> A short time ago there was a thread on filters, and a suggestion
>> for a long term averaging filter was proposed that went along the
>> lines of:-
>>
>> average = (((temp*width)-average)+newsample)/width

>     Actually, Dennis, it was probably:

>         average = (((average*width)-average)+newsample)/width

>     which is usually simplified to:

>         average = (average * (width-1) + newsample) / width.

>> 1/ This filter assumes that the processor is a floating point

>     No; the equation is EASILY implemented using integer math.  It
>     works like this:

>     1.  Since integer division by a power of 2 is trivial, we make
>     "width" equal to a power of 2.

>     2.  Since multiplication by a power of 2 is also trivial, we
>     rewrite the equation thusly:

>         average = ((newsample-average) + (width*average)) / width

>     If we can make width = 256, the code is absurdly simple... But
>     that's a special case.  Here's the code for width = 16, which is
>     more representative of what'll usually be required:

>     ; Written by Andrew Warren (fastfwd-at-ix.netcom.com).
>     ;
>     ;       (NEW - AVE) + 16*AVE
>     ; AVE = --------------------
>     ;               16

>     NEW     EQU     [ANY FILE REGISTER]   ;HOLDS NEW SAMPLE [0-255].
>     AVE     EQU     [ANY FILE REGISTER[   ;HOLDS THE AVERAGE [0-255].
>     AVFRAC  EQU     [ANY FILE REGISTER]   ;HOLDS THE FRACTIONAL PART
>                                           ;OF THE AVERAGE [0-15].
>     TEMP    EQU     [ANY FILE REGISTER]   ;TEMPORARY STORAGE.

>     FILTER:

>             MOVLW   00001111B       ;MASK OFF ALL BUT THE FRACTIONAL
>             ANDWF   AVFRAC          ;PART OF THE OLD AVERAGE
>                                     ;(AVFRAC'S HI-NIBBLE CAN
>                                     ;CONTAIN GARBAGE).

>             MOVF    AVE,W           ;NEW = NEW - AVE.
>             SUBWF   NEW             ;CARRY = 0 IF THE DIFFERENCE IS
>                                     ;NEGATIVE, CARRY = 1 IF POSITIVE.

>             SWAPF   AVE,W           ;HI-NIBBLE OF W = MIDDLE NIBBLE
>                                     ;OF 16*AVE.

>             MOVWF   TEMP            ;LO-NIBBLE OF TEMP = HI-NIBBLE
>                                     ;OF 16*AVE (LO-NIBBLE OF 16*AVE
>                                     ;IS ALWAYS 0000).

>     ; NEW CONTAINS (NEW - AVE) AND [TEMP:W] HOLDS 16*AVE.
>     ; ADD THEM TOGETHER.

>             SKPC                    ;IF (NEW - AVE) WAS NEGATIVE,
>             DECF    TEMP            ;ADJUST THE HI-NIBBLE OF 16*AVE.

>             ANDLW   11110000B       ;W = LO-BYTE OF 16*AVE.
>             IORWF   AVFRAC,W        ;(INCLUDE AVE'S FRACTIONAL
>                                     ;PART).

>             ADDWF   NEW,W           ;W = LO-BYTE OF
>                                     ;((NEW-AVE) + 16*AVE).
>                                     ;CARRY = 1 IF SUM > 255,
>                                     ;CARRY = 0 OTHERWISE.

>             MOVWF   AVFRAC          ;LO-NIBBLE OF AVFRAC =
>                                     ;FRACTIONAL PART OF FINAL
>                                     ;RESULT.  HI-NIBBLE = GARBAGE.

>             ANDLW   11110000B       ;W = (LO-NIBBLE OF
>                                     ;((15*AVE+NEW)/16)) * 16.

>             MOVWF   AVE             ;AVE = (LO-NIBBLE OF
>                                     ;((15*AVE+NEW)/16)) * 16.

>             SWAPF   AVE             ;AVE = LO-NIBBLE OF
>                                     ;((15*AVE+NEW)/16).

>     ; AVE'S LO-NIBBLE CONTAINS THE LO-NIBBLE OF THE FINAL RESULT.
>     ; AVE'S HI-NIBBLE = 0000.

>             SKPNC                   ;LO-NIBBLE OF TEMP =
>             INCF    TEMP            ;HI-NIBBLE OF ((15*AVE+NEW)/16).

>             SWAPF   TEMP,W          ;HI-NIBBLE OF W = HI-NIBBLE OF
>             ANDLW   11110000B       ;((15*AVE+NEW)/16).
>                                     ;LO-NIBBLE OF W = 0000.

>     ; W'S HI-NIBBLE CONTAINS THE HI-NIBBLE OF THE FINAL RESULT.
>     ; LO-NIBBLE = 0000.

>             IORWF   AVE             ;COMBINE THE HI- AND LO-NIBBLES
>                                     ;AND STORE THE FINAL RESULT IN
>                                     ;AVE.

>     ; AVE CONTAINS THE NEW AVERAGE.

>     See?  That wasn't too hard; just 20 words of program space and
>     20 instruction cycles...  And not a single floating-point
>     operation anywhere in there.

>> 2/ The filter is great for downward changing signals only, as an
>> upward change will not be reflected until the delta is greater
>> than the width

>     Untrue.  In the code above, AVFRAC accumulates deltas smaller
>     than the width until their sum is large enough to affect the
>     average.

>> 3/ The impulse response is dependant on the width etc.

>     No kidding.  Isn't that, um, THE POINT?

>     -Andy


> === Andrew Warren - fastfwd-at-ix.netcom.com
> === Fast Forward Engineering - San Diego, California
> === http://www.geocities.com/SiliconValley/2499