=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Date: Fri, 16 Jun 2000 09:24:53 From: Nikolai Golovchenko <golovchenko-at-mail.ru> To: pic microcontroller discussion list <PICLIST-at-MITVMA.MIT.EDU> Subject: Re: [PIC]: - Who is awake? -------------------------------------------------------------------------------- I would use all 8 bits available in AVEFRAC for fractional part. This will reduce the round-off error to almost nothing. And I would like to keep the input value in NEW not changed. Also note that the averaging algorithm works only with unsigned values. To work with signed you need first to bias the signed number (add 2^(n-1) or xor sign bit), then calculate the average, and remove bias (substract 2^(n-1) or xor sign bit). And, to increase precision (or accuracy ?) I would round the final result before using it by: btfsc AVEFRAC, 7 incf AVE, w ;w holds rounded result The modified routine takes also 20 instruction/cycles: swapf AVFRAC, w ;AVFRAC -= AVFRAC / 16 andlw 0x0F subwf AVFRAC, f ;(no carry check is performed - ;there is no carry) swapf AVE, w ;AVE:AVFRAC -= AVE / 16 movwf TEMP andlw 0xF0 subwf AVFRAC, f skpc decf AVE, f movf TEMP, w andlw 0x0F subwf AVE, f swapf NEW, w ;AVE:AVFRAC += NEW / 16 andlw 0xF0 addwf AVFRAC, f skpnc incf AVE, f swapf NEW, w andlw 0x0F addwf AVE, f ;AVE holds the result Nikolai P.S. And all that stuff could be replaced by two parts :) -----/\/\/\----o----- to PIC ADC input | ----- ----- | === On Thursday, June 15, 2000 Andrew Warren wrote: > Plunkett, Dennis <PICLIST-at-MITVMA.MIT.EDU> wrote: >> A short time ago there was a thread on filters, and a suggestion >> for a long term averaging filter was proposed that went along the >> lines of:- >> >> average = (((temp*width)-average)+newsample)/width > Actually, Dennis, it was probably: > average = (((average*width)-average)+newsample)/width > which is usually simplified to: > average = (average * (width-1) + newsample) / width. >> 1/ This filter assumes that the processor is a floating point > No; the equation is EASILY implemented using integer math. It > works like this: > 1. Since integer division by a power of 2 is trivial, we make > "width" equal to a power of 2. > 2. Since multiplication by a power of 2 is also trivial, we > rewrite the equation thusly: > average = ((newsample-average) + (width*average)) / width > If we can make width = 256, the code is absurdly simple... But > that's a special case. Here's the code for width = 16, which is > more representative of what'll usually be required: > ; Written by Andrew Warren (fastfwd-at-ix.netcom.com). > ; > ; (NEW - AVE) + 16*AVE > ; AVE = -------------------- > ; 16 > NEW EQU [ANY FILE REGISTER] ;HOLDS NEW SAMPLE [0-255]. > AVE EQU [ANY FILE REGISTER[ ;HOLDS THE AVERAGE [0-255]. > AVFRAC EQU [ANY FILE REGISTER] ;HOLDS THE FRACTIONAL PART > ;OF THE AVERAGE [0-15]. > TEMP EQU [ANY FILE REGISTER] ;TEMPORARY STORAGE. > FILTER: > MOVLW 00001111B ;MASK OFF ALL BUT THE FRACTIONAL > ANDWF AVFRAC ;PART OF THE OLD AVERAGE > ;(AVFRAC'S HI-NIBBLE CAN > ;CONTAIN GARBAGE). > MOVF AVE,W ;NEW = NEW - AVE. > SUBWF NEW ;CARRY = 0 IF THE DIFFERENCE IS > ;NEGATIVE, CARRY = 1 IF POSITIVE. > SWAPF AVE,W ;HI-NIBBLE OF W = MIDDLE NIBBLE > ;OF 16*AVE. > MOVWF TEMP ;LO-NIBBLE OF TEMP = HI-NIBBLE > ;OF 16*AVE (LO-NIBBLE OF 16*AVE > ;IS ALWAYS 0000). > ; NEW CONTAINS (NEW - AVE) AND [TEMP:W] HOLDS 16*AVE. > ; ADD THEM TOGETHER. > SKPC ;IF (NEW - AVE) WAS NEGATIVE, > DECF TEMP ;ADJUST THE HI-NIBBLE OF 16*AVE. > ANDLW 11110000B ;W = LO-BYTE OF 16*AVE. > IORWF AVFRAC,W ;(INCLUDE AVE'S FRACTIONAL > ;PART). > ADDWF NEW,W ;W = LO-BYTE OF > ;((NEW-AVE) + 16*AVE). > ;CARRY = 1 IF SUM > 255, > ;CARRY = 0 OTHERWISE. > MOVWF AVFRAC ;LO-NIBBLE OF AVFRAC = > ;FRACTIONAL PART OF FINAL > ;RESULT. HI-NIBBLE = GARBAGE. > ANDLW 11110000B ;W = (LO-NIBBLE OF > ;((15*AVE+NEW)/16)) * 16. > MOVWF AVE ;AVE = (LO-NIBBLE OF > ;((15*AVE+NEW)/16)) * 16. > SWAPF AVE ;AVE = LO-NIBBLE OF > ;((15*AVE+NEW)/16). > ; AVE'S LO-NIBBLE CONTAINS THE LO-NIBBLE OF THE FINAL RESULT. > ; AVE'S HI-NIBBLE = 0000. > SKPNC ;LO-NIBBLE OF TEMP = > INCF TEMP ;HI-NIBBLE OF ((15*AVE+NEW)/16). > SWAPF TEMP,W ;HI-NIBBLE OF W = HI-NIBBLE OF > ANDLW 11110000B ;((15*AVE+NEW)/16). > ;LO-NIBBLE OF W = 0000. > ; W'S HI-NIBBLE CONTAINS THE HI-NIBBLE OF THE FINAL RESULT. > ; LO-NIBBLE = 0000. > IORWF AVE ;COMBINE THE HI- AND LO-NIBBLES > ;AND STORE THE FINAL RESULT IN > ;AVE. > ; AVE CONTAINS THE NEW AVERAGE. > See? That wasn't too hard; just 20 words of program space and > 20 instruction cycles... And not a single floating-point > operation anywhere in there. >> 2/ The filter is great for downward changing signals only, as an >> upward change will not be reflected until the delta is greater >> than the width > Untrue. In the code above, AVFRAC accumulates deltas smaller > than the width until their sum is large enough to affect the > average. >> 3/ The impulse response is dependant on the width etc. > No kidding. Isn't that, um, THE POINT? > -Andy > === Andrew Warren - fastfwd-at-ix.netcom.com > === Fast Forward Engineering - San Diego, California > === http://www.geocities.com/SiliconValley/2499