PIC Microcontoller 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

	mov	W, #Average	; initialise FSR
	mov	FSR, W

        ...
;
; PLACE A2D RESULTS INTO A ROTATING BUFFER
;
	mov	W, ADRES
	mov	INDF, W
	inc	FSR
	mov	W, #Average + 8h
	xor	W, FSR
	sb	Z
	jmp	NFSR

	mov	W, #Average
	mov	FSR, W

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:

	mov	W, ++fsr
	and	W, #7
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;   addlw  Average
	mov	Hack, W
	mov	W, #Average
	add	W, Hack
	mov	fsr, W


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

	mov	W, ++fsr
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;   addlw  -Average
	mov	Hack, W
	mov	W, #-Average
	add	W, Hack
	and	W, #7
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;   addlw  Average
	mov	Hack, W
	mov	W, #Average
	add	W, Hack
	mov	fsr, W


;
; AVERAGE OUT THE 8 STORED VALUES WHEN A RESULT IS NEEDED
;
	mov	W, #Average
	mov	FSR, W
	clr	tempH
	clr	tempL

avLoop	mov	W, INDF	; add up 8 word values
	add	tempL, W
	snb	C
	inc	tempH

	inc	FSR
	mov	W, #Average +8h
	xor	W, FSR
	sb	Z
	jmp	avLoop

	mov	W, #Average
	mov	FSR, W
	clrb	C	; div result by 8
	rr	tempH
	rr	tempL
	clrb	C
	rr	tempH
	rr	tempL
	clrb	C
	rr	tempH
	rr	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:

	add	avg_lo, W	;add newest sample to the average
	snb	C
	inc	avg_hi

	xor	indf, W	; swap oldest and newest samples
	xor	W, indf
	xor	indf, W

	sub	avg_lo, W	;remove the oldest sample from the average
	sb	C
	dec	avg_hi

	mov	W, ++fsr	;advance the sample pointer
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;    addlw  -sample
	mov	Hack, W
	mov	W, #-sample
	add	W, Hack
	and	W, #7
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;    addlw  sample
	mov	Hack, W
	mov	W, #sample
	add	W, Hack
	mov	fsr, W

	ret

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:

	mov	W, >>avg_hi
	mov	temp_hi, W
	mov	W, >>avg_lo
	mov	temp_lo, W
	rr	temp_hi
	rr	temp_lo
	rr	temp_hi
	mov	W, >>temp_lo

and if you wanted rounding:

	snb	C
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;     addlw 1
	mov	Hack, W
	mov	W, #1
	add	W, Hack

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