PIC Microcontroller Input / Ouput Method

Averaging A2D results

From Tony Nixon [sales at picnpoke.com] and Scott Dattalo

Tony says:

Heres a simple setup using the FSR. Averages for 8 readings though.
        CBLOCK
Average: 8h
tempH
tempL
        ENDC

        movlw Average   ; initialise FSR
        movwf FSR

        ...
;
; PLACE A2D RESULTS INTO A ROTATING BUFFER
;
        movf ADRES,W
        movwf INDF
        incf FSR
        movlw Average + 8h
        xorwf FSR,W
        btfss STATUS,Z
        goto NFSR

        movlw Average
        movwf FSR

NFSR    ...
;Scott Dattalo interjects:
;If you have only 8 samples, and you were willing to align the array on a mod 8
;boundary, then advancing the pointer could be done easily:

   incf   fsr,w
   andlw  7
   addlw  Average
   movwf  fsr


;if you don't wish to align the array, then try this instead:

   incf   fsr,w
   addlw  -Average
   andlw  7
   addlw  Average
   movwf  fsr


;
; AVERAGE OUT THE 8 STORED VALUES WHEN A RESULT IS NEEDED
;
        movlw Average
        movwf FSR
        clrf tempH
        clrf tempL

avLoop  movf INDF,W             ; add up 8 word values
        addwf tempL
        btfsc STATUS,C
        incf tempH

        incf FSR
        movlw Average +8h
        xorwf FSR,W
        btfss STATUS,Z
        goto avLoop

        movlw Average
        movwf FSR
        bcf STATUS,C            ; div result by 8
        rrf tempH
        rrf tempL
        bcf STATUS,C
        rrf tempH
        rrf tempL
        bcf STATUS,C
        rrf tempH
        rrf tempL

        ....

And Scott also says:

Again, I think the best way to maintain a running average is by subtracting the oldest value and adding in the newest. Here's something I'll whip out as fast as I can type it - which means there's a good chance for a thinko.
; avg_lo:avg_hi - 16bit cumulative sum of last 8 samples
; samples - a circular array that holds the last 8 samples
; assume that samples and avg_lo:avg_hi are initialized to zero
;
; FSR is initialized to the start of samples
;
; W = newest sample

AVERAGE:

    addwf  avg_lo,f    ;add newest sample to the average
    skpnc
     incf  avg_hi,f

    xorwf  indf,f      ; swap oldest and newest samples
    xorwf  indf,w
    xorwf  indf,f

    subwf  avg_lo,f    ;remove the oldest sample from the average
    skpc
     decf  avg_hi,f

    incf   fsr,w       ;advance the sample pointer
    addlw  -sample
    andlw  7
    addlw  sample
    movwf  fsr

    return

There - no more looping through all of the data after every sample is acquired.

Now, I'm assuming that the division is not needed at every iteration. I tend to
put off the difficult stuff as long as possible - often times, the intermediate
calculations are more than sufficient.

But, if you want, then this is how I'd do it:

    rrf   avg_hi,w
    movwf temp_hi
    rrf   avg_lo,w
    movwf temp_lo
    rrf   temp_hi,f
    rrf   temp_lo,f
    rrf   temp_hi,f
    rrf   temp_lo,w

and if you wanted rounding:

    skpnc
     addlw 1

(You don't need to worry about clearing the carry because the average is
guranteed to be less than or equal to 255.)