PDA

View Full Version : Help with DT's 16-bit averaging subroutine?



jellis00
- 21st March 2011, 22:31
I am trying to get DT's 16-bit averaging subroutine to work correctly. I created the attached code as a way to test it. As you can see from the code, it writes values at various locations in the code to EEPROM so that after each execution I can read the data from the EEPROM as a way to see what values are being created.
What I don't undestand and am asking for help from anyone on this forum is why the variable VALUE is always showing up as zero (EEPROM addresses 19 and 36). By the way I set up this test routine, starting with a decimal value of 35260 and incrementing it 6 times, the average stored in VALUE should be 35263...not zero.
Any help is appreciated on this.


'************************************************* ****************
'* Name : TEST_AVERAGE2.BAS *
'* Author : John R. Ellis *
'* Notes : Adapted from code by Darrel Taylor *
'* "This routine will keep a "Running Average. *
'* Instead of adding a bunch of values together and then dividing*
'* by the number of samples, it averages each sample into the *
'* final result immediately. This eliminates the need for 32 bit *
'* math. *
'* To allow it to "Catch up" to large changes, set the FAspread *
'* to an acceptable range. Simply place the new number in VALUE *
'* and GoSub AVERAGE The Average will be returned into the same *
'* variable VALUE." *
'* Memory : 546 Words of program memory required *
'************************************************* ****************
ADavg VAR WORD
AvgCount CON 16 ' = Number of samples to average
FAspread CON 1000 ' = Fast Average threshold +/-
i VAR Byte ' Index for measurment loop
pw VAR WORD ' Stores each pw measurment
pw0 Var pw.BYTE0 ' LSB of pw value
PW1 var PW.BYTE1 ' MSB of pw value
range VAR WORD ' Stores calculated range value
range0 VAR range.Byte0 ' LSB of range value
range1 VAR range.Byte1 ' MSB of range value
us_inch con 147 ' Scale factor converts sensor 147 microsecs to inches
VALUE VAR WORD ' Stores average of measurements
value0 VAR value.byte0 ' LSB of value
value1 VAR value.byte1 ' MSB of value

GOTO Main ' Jump over Subroutines

' -----------------{ Subroutines }----------------------------------------
Average_Single:
'Embedded use of Darrel Taylor's 16 bit averaging routine
' USE INTRUCTIONS: First, Select the sensor. ie. Sensor = Temperature
' then place the new number in VALUE and then GoSub AVERAGE_SINGLE.
' The Average will be returned into the same variable VALUE.

IF Value = ADavg Then NoChange
IF ABS (Value - ADavg) > FAspread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount)
ADavg = ADavg + (Value/AvgCount)
GoTo AVGok

FastAvg:
ADavg = Value
GoTo AVGok

RealClose:
'If averaging less than 8 samples, change AvgCount/4 to AvgCount/2.
ADavg = ADavg - (ADavg/(AvgCount/2))
ADavg = ADavg + (Value/(AvgCount/2))

AVGok:
Value = ADavg ' Put Average back into Value

NoChange:
Return

'--------------------------------------
Main:
'DEFINE WRITE_USED 1 'Required if only writing WORD variables in version 2.6
pw = 35260
WRITE 32,pw ' Comment out for test only
For i = 0 to 5
'PULSIN PORTA.4,1,pw ' Make 5 measurements from PW pin
' of transceiver into array pw(i)
' with running average in value
pw = pw +1 ' Simulates pw measurement
WRITE 34,pw ' Comment out for test only
Value = pw
WRITE 36,VALUE ' Comment out for test only
'Running average in value...Max pw = 20' = 240" = 35280 usec. Therefore
'pw, value, & range are 16 bit WORDs.
GOSUB Average_Single
NEXT
WRITE 17,i ' Comment out for test only
WRITE 19,VALUE ' Comment out for test only
range = (Value / us_inch) +1 ' Convert value to range in inches (max 240)
' add 1 to account for division rounding.
WRITE 21,range ' Store day's range in EEPROM location
END

Darrel Taylor
- 21st March 2011, 23:16
If you want to write WORD variables to EEPROM, you need to either add the WORD modifier to the WRITE statement, or WRITE the lowbyte and highbyte seperately in 2 WRITE statements.


WRITE 36,WORD VALUE

jellis00
- 21st March 2011, 23:40
OK, Darrel,...that is one stupid mistake on my part. But I fixed the WRITE statements per the attached code, and VALUE is still coming out of the subroutine with a value of zero, as evidenced in the EEPROM WRITE address 19. Yet when I WRITE to EEPROM address 36 within the loop where the subroutine is called, the values in VALUE are correct going into the averaging subroutine.
I still can't figure out why the averaging isn't working?? Any ideas?


'************************************************* ****************
'* Name : TEST_AVERAGE2.BAS *
'* Author : John R. Ellis *
'* Notes : Adapted from code by Darrel Taylor *
'* This routine will keep a "Running Average". *
'* Instead of adding a bunch of values together and then dividing*
'* by the number of samples, it averages each sample into the *
'* final result immediately. This eliminates the need for 32 bit *
'* math. *
'* To allow it to "Catch up" to large changes, set the FAspread *
'* to an acceptable range. Simply place the new number in VALUE *
'* and GoSub AVERAGE The Average will be returned into the same *
'* variable VALUE. *
'* Memory : 546 Words of program memory required *
'************************************************* ****************
ADavg VAR WORD
AvgCount CON 16 ' = Number of samples to average
FAspread CON 1000 ' = Fast Average threshold +/-
i VAR Byte ' Index for measurment loop
pw VAR WORD ' Stores each pw measurment
pw0 Var pw.BYTE0 ' LSB of pw value
PW1 var PW.BYTE1 ' MSB of pw value
range VAR WORD ' Stores calculated range value
range0 VAR range.Byte0 ' LSB of range value
range1 VAR range.Byte1 ' MSB of range value
us_inch con 147 ' Scale factor converts sensor 147 microsecs to inches
VALUE VAR WORD ' Stores average of measurements
value0 VAR value.byte0 ' LSB of value
value1 VAR value.byte1 ' MSB of value

GOTO Main ' Jump over Subroutines

' -----------------{ Subroutines }----------------------------------------
Average_Single:
'Embedded use of Darrel Taylor's 16 bit averaging routine
' USE INTRUCTIONS: First, Select the sensor. ie. Sensor = Temperature
' then place the new number in VALUE and then GoSub AVERAGE_SINGLE.
' The Average will be returned into the same variable VALUE.

IF Value = ADavg Then NoChange
IF ABS (Value - ADavg) > FAspread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount)
ADavg = ADavg + (Value/AvgCount)
GoTo AVGok

FastAvg:
ADavg = Value
GoTo AVGok

RealClose:
'If averaging less than 8 samples, change AvgCount/4 to AvgCount/2.
ADavg = ADavg - (ADavg/(AvgCount/2))
ADavg = ADavg + (Value/(AvgCount/2))

AVGok:
Value = ADavg ' Put Average back into Value

NoChange:
Return

'--------------------------------------
Main:
'DEFINE WRITE_USED 1 'Required if only writing WORD variables in version 2.6
pw = 35260
WRITE 32,WORD pw ' Comment out for test only
For i = 0 to 5
'PULSIN PORTA.4,1,pw ' Make 5 measurements from PW pin
' of transceiver into array pw(i)
' with running average in value
pw = pw +1 ' Simulates pw measurement
WRITE 34,WORD pw ' Comment out for test only
Value = pw
WRITE 36,WORD VALUE ' Comment out for test only
'Running average in value...Max pw = 20' = 240" = 35280 usec. Therefore
'pw, value, & range are 16 bit WORDs.
GOSUB Average_Single
NEXT
WRITE 17,i ' Comment out for test only
WRITE 19,WORD VALUE ' Comment out for test only
range = (Value / us_inch) +1 ' Convert value to range in inches (max 240)
' add 1 to account for division rounding.
WRITE 21,WORD range ' Store day's range in EEPROM location
END

Darrel Taylor
- 22nd March 2011, 16:33
Maybe you've worn out your EEPROM.
I never liked that debugging method.

I've run it here and it works fine on a 16F887.

Here's a run in Proteus.

5303

And here's a readback from a real chip running the same program.

5304