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