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