SX Microcontroller Input / Ouput Method

Averaging A2D results

By Nikolai Golovchenko, Tony Nixon [sales at picnpoke.com] and Scott Dattalo

        DEVICE SX28L, OSCHS2, TURBO, STACKX, OPTIONX
        RESET start

        ORG $08         ;global bank
rand0   DS 1            ;random generator state
count   DS 1            ;general purpose counter
temp	DS 1		;temp needed in Scott's version
tempH   DS 1            ;temps needed in Tony's version
tempL   DS 1            ;

        ORG $10         ;bank 0
av_bank EQU $
samples EQU $           ;alias name for 'average'
average DS 8            ;circular buffer for 8 bytes
av_ptr  DS 1            ;circular buffer pointer (0..7)
avg_lo  DS 1            ;cumulative average (required in Scott's version)
avg_hi  DS 1            ;

        ORG $000        ;page 0

;************************************************************************
start

;make w addressable
        mov w, #$7F
        mov !OPTION, w
;x--- ----      0 = register 01h addresses w 
;-x-- ----      1 = RTCC roll-over interrupt is disabled
;--x- ----      1 = RTCC increments upon transition on RTCC pin
;---x ----      1 = RTCC increments on high-to-low transitions
;---- x---      1 = Prescaler is assigned to WDT, and divide rate on
;                   RTCC is 1:1
;---- -xxx      111 = Prescaler divider 1:128 (WDT)


; * METHOD 1 *

;enter 8 entries in the buffer and calculate the average
;using method 1

        bank av_bank    ;select working bank

        clr rand0       ;init the random generator state
        clr av_ptr      ;clear buffer pointer
        mov w, #8
        mov count, w
loop1
        call Rand8      ;generate a random input value in w
        call av_put     ;put it into the buffer
        decsz count     
         jmp loop1      ;repeat 8 times

        call av_calc    ;calculate average and return in w

	;input data = 35,F6,93,1C,61,F2,1F,F8
        ;result w = 88

; * METHOD 2 *

;enter 8 entries in the buffer and calculate the average
;using method 2

        bank av_bank    ;select working bank

        clr rand0       ;init the random generator state
	call av_init2	;init average calculation

        mov w, #8
        mov count, w
        
loop2
        call Rand8      ;generate a random input value in w
        call av_put2    ;put it into the buffer
        decsz count     
         jmp loop2      ;repeat 8 times
        
        call av_calc2   ;calculate average and return in w
        ;result w = 88

        call av_calc3   ;calculate average and round
        ;result w = 89


        jmp $           ;halt
;************************************************************************
Rand8
        clc
        rl      rand0           ; w = (32 * rand) % 256
        mov     w, <>rand0
        and     w, #$E0
        rr      rand0           ; restore rand
        add     w, rand0        ; rand' = (w + 3 * rand) % 256=
        add     w, rand0        ;       = (32 * rand + 3 * rand) % 256
        add     rand0, w
        not     rand0           ; rand'' = -rand'-1
        mov     w, #$36         ; rand = (53 - 32 * rand - 3 * rand) % 256
        add     rand0, w
        mov     w, rand0        ; copy result to w
        retp                    ; Done

;************************************************************************
;
; PLACE A2D RESULTS (w) INTO A ROTATING BUFFER
; (by Tony Nixon and Scott Dattalo)
;
;************************************************************************
av_put
        mov     temp, w         ;save w in temp (in global bank)

        mov     w, #average     ;initialise FSR
        mov     FSR, w
        mov     w, av_ptr
        add     FSR, w

        mov     w, temp         ;get input
        mov     IND, w          ;save in buffer

                                ;update pointer
        mov     w, ++av_ptr     
        and     w, #$07         ;implement wrap-around
        mov     av_ptr, w

        retp

;************************************************************************
;
; AVERAGE OUT THE 8 STORED VALUES WHEN A RESULT IS NEEDED (return in w)
;
;************************************************************************
av_calc
        mov     w, #average     ;initialise FSR
        mov     FSR, w
        clr     tempH
        clr     tempL

;add up 8 word values
av_loop
        mov     w, IND          ;load pointed value
        add     tempL, w        ;add to tempH:tempL
        snc
         inc    tempH

        inc     FSR
        mov     w, #average + 8
        xor     w, FSR
        sz
         jmp    av_loop

;div result by 8
        rr      tempH
        rr      tempL
        rr      tempH
        rr      tempL
        rr      tempH
        mov     w, >>tempL      ;last shift result place in w

        retp                    ;return with result in w

;************************************************************************
; Scott Dattalo says:
; Again, I think the best way to maintain a running average is by
; subtracting the oldest value and adding in the newest.
;
; 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
;************************************************************************
av_put2
	mov 	temp, w		;save input
        mov     w, #average     ;initialise FSR
        mov     FSR, w
        mov     w, av_ptr
        add     FSR, w
        
	mov	w, temp		;read input      
        add     avg_lo, w       ;add newest sample to the average
        snc
         inc    avg_hi

        xor     IND, w          ;swap oldest and newest samples
        xor     w, IND
        xor     IND, w

        sub     avg_lo, w       ;remove the oldest sample from the average
        sc
         dec    avg_hi

        mov     w, ++av_ptr     ;advance the sample pointer
        and     w, #$07
        mov     av_ptr, w

        retp
;************************************************************************
;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:
;************************************************************************
av_calc2
        bank    av_bank         ;select working bank

        mov     w, >>avg_hi
        mov     temp, w
        mov     w, >>avg_lo
        rr      temp
        rr      WREG
        rr      temp
        rr      WREG
        retp        

;and if you wanted rounding:
av_calc3
        bank    av_bank         ;select working bank

        mov     w, >>avg_hi
        mov     temp, w
        mov     w, >>avg_lo
        rr      temp
        rr      WREG
        rr      temp
        rr      WREG
        snc
         inc    WREG
        retp

;************************************************************************
; Initialize buffer
;************************************************************************
av_init2
        mov w, #average	;init bank and FSR pointer
        mov FSR, w
        clr av_ptr      ;clear buffer pointer
        clr avg_hi      ;clear running average
        clr avg_lo      ;
        mov w, #8
        mov temp, w
av_init2_loop
        clr IND		;clear the buffer
        inc FSR
        decsz temp
         jmp av_init2_loop
        retp
;************************************************************************

	ORG $200	;page 1