PDA

View Full Version : Help needed with ADCIN Averaging Routine



Aussie Barry
- 23rd December 2012, 21:54
Hi All,

My latest project is a temperature controlled switch using PIC16F684 and an LM35DZ.
The LM35DZ output is 10mV per degree celcius. The routine is designed such that a relay is energised when the ambient temperature exceeds a pre-set temperature. The relay is de-energised once the ambient temperature drops below the pre-set temperature (less 1 degree hysteresis to stop relay chatter). All works well with my code for a short period of time (5-10 minutes) but then something strange happens with the display temperature - the temperature displays a random value (eg 4.7) instead of the correct value (eg 25.0) even though the voltage at the ADC input pin is correct (eg 250mV).

To help stabilise the result from the LM35DZ output I averaged 1000 samples in a While...Wend loop. I suspect the problem I am experiencing has something to do with this averaging routine, and I have been over it a dozen times, but I can't work out what goes wrong. As stated above, the routine works for a period of time but then falls over. I am investigating an alternate averaging routine but for my own sanity I need to find out why this routine fails after a short time. My code is as follows:




'************************************************* ***************
'* Name : TempCont.pbp *
'* Author : Barry Phillips *
'* Notice : Copyright (c) 2012 Baztronics *
'* : All Rights Reserved *
'* Date : 16/12/2012 *
'* Version : 1.0 *
'* Notes : Temperature Controlled Switch *
'* : *
'************************************************* ***************
' PicBasic Pro program to display temperature from LM35DZ with
' 10-bit A/D conversion on LCD using PIC16F684 and switch relay on
' if ambient temperature is greater than temperature setting.
'
' LM35DZ output is 10mV per degree celcius
'
' 10-bit ADC equates to 4.88mV per step. For this application total accuracy
' has been forgone for simplicity and it is assumed that 1 step equates to
' 5mV or 0.5 degree celcius. The displayed temperature is calculated by
' ADCIN value multiplied by 5 (Adval*5).
'
' Connect LM35DZ to RA0 (Pin 13)
' Connect switch "Up" to RA3 (Pin 4) 10k Pull-Up to +5V
' Connect switch "Down" to RA4 (Pin 3) 10k Pull-Up to +5V
' Connect switch "Bypass" to RA5 (Pin 2) 10k Pull-Up to +5V
' Connect LED to RC5 (Pin 5) via 470R to +5V
' Connect Relay drive to RC4 (Pin 6)
' Connect LCD D4 to RC0 (Pin 10)
' Connect LCD D5 to RC1 (Pin 9)
' Connect LCD D6 to RC2 (Pin 8)
' Connect LCD D7 to RC3 (Pin 7)
' Connect LCD R/S to RA1 (Pin 12)
' Connect LCD E to RA2 (Pin 11)

' Define LCD registers and bits
Define LCD_DREG PORTC
Define LCD_DBIT 0
Define LCD_RSREG PORTA
Define LCD_RSBIT 1
Define LCD_EREG PORTA
Define LCD_EBIT 2
Define LCD_BITS 4
Define LCD_LINES 2

CMCON0 = %00000111 ' Disable comparators
ANSEL = %00000001 ' Set AN0 as analogue input
TRISA = %00111001 ' Set PORTA.0, PORTA.3 - .5 to input, all others output
TRISC = 0 ' Set PORTC as output
high PORTC.5 ' Ensure LED turned off
low PORTC.4 ' Ensure Relay is turned off
ADCON1 = %01010000 ' Set A/D clock Fosc/16
ADCON0 = %10000001 ' Right justify result
' Set Vref = Vdd
' Select channel AN0
' Turn on A/D converter

OSCCON = %01110001 ' Set clock frequency to 8MHz
define OSC 8

' Define ADCIN parameters
Define ADC_BITS 10 ' Set number of bits in result
Define ADC_CLOCK 3 ' Set clock source (3=rc)
Define ADC_SAMPLEUS 50 ' Set sampling time in uS

Pause 100 ' Wait 0.1 second

' Set up variables
adval var word 'ADCIN variable
Temp var word 'Temperature variable
Tempset var word 'Temperature setting variable
avecount var word 'Temperature average counter
tempsum var word 'Temperature averaging variable
B4 var byte 'Bypass flag
Tempup var PORTA.4 'Define Tempup switch port
Tempdown var PORTA.3 'Define Tempdown switch port
Bypass var PORTA.5 'Define Bypass switch port
LED1 var PORTC.5 'Define LED port output
Relay var PORTC.4 'Define Relay port output
DATA @1, 50 'Store 50 in EEPROM location 1
READ 1, Tempset 'Read EEPROM location 1 and save to Tempset
B4 = 0 'Reset bypass flag
high LED1 'Ensure LED is off
low Relay 'Ensure Relay is off

Lcdout $fe, 1 'Clear LCD
LCDOUT $FE, $80, " BAZTRONICS"
LCDOUT $FE, $C0, "TEMP. CONTROLLER"
pause 3000 'Pause 3 seconds
LCDOUT $FE, 1 'Clear LCD

Again:
avecount = 1 'Reset averaging counter
tempsum = 0 'Reset averaging variable
while avecount<1001 'Loop 1000 times
ADCIN 0, adval 'Read channel 0 to adval
tempsum = tempsum + adval 'sum temp average reading
avecount = avecount + 1 'Increment counter
if Bypass = 0 then manual 'Poll Bypass switch
If B4 = 0 then 'If Bypass not activated
if Tempup = 0 then Upstep 'Poll "Up" switch
if Tempdown = 0 then Downstep 'Poll "Down" switch
endif
wend
Temp = tempsum/200
Lcdout $FE, $80
LCDOUT " Temp: ", DEC Temp/10, ".", DEC1 Temp,$DF, "C "'Display Temp
If B4 = 1 then 'Test Bypass flag status
goto Again
Else
LCDOUT $FE, $C0+1 'Move cursor to beginning of second line
lcdout " Set: ", dec tempset/2, ".", dec1 tempset*5,$DF, "C "'Display Tempset
endif
If temp>tempset*5 then 'Test ambient temperature vs temperature setting
low LED1 'Turn on LED
high Relay 'Turn on relay
LCDOUT $FE, $C0, $DB
endif
if Temp < (tempset-2)*5 then'Test ambient temperature vs temperature setting
high LED1 'Turn off LED
low Relay 'Turn off relay
LCDOUT $FE, $C0, " "
endif
Goto Again 'Do it forever

Upstep:
If Tempset<99 then
Tempset = Tempset + 1 'Increment temperature setting
else
Tempset = Tempset 'Set Tempset upper limit
endif
LCDOUT $FE, $C0+1 'Move cursor to beginning of second line
lcdout " Set: ", dec tempset/2, ".", dec1 tempset*5,$DF,"C "'Display Tempset
WRITE 1, Tempset 'Store new Tempset to EEPROM location 1
Pause 250 'Pause 250ms for switch debounce
goto Again

Downstep:
if Tempset>2 then
Tempset = Tempset - 1 'Decrement temperature setting
else
Tempset = Tempset 'Set Tempset lower limit
endif
LCDOUT $FE, $C0+1 'Move cursor to beginning of second line
lcdout " Set: ", dec tempset/2, ".", dec1 tempset*5,$DF, "C "'Display Tempset
WRITE 1, Tempset 'Store new Tempset to EEPROM location 1
pause 250 'Pause 250ms for switch debounce
goto again

Manual:
If B4 = 0 then
B4 = 1 'Set Bypass flag
low LED1 'Turn on LED
high Relay 'Turn on Relay
LCDOUT $FE, $C0+1 ' Move cursor to beginning of second line
lcdout " BYPASS MODE " 'Display Bypass Mode message
else
B4 = 0 'Reset Bypass flag
high LED1 'Turn off LED
low Relay 'Turn off Relay
LCDOUT $FE, $C0+1 'Move cursor to beginning of second line
lcdout " Set: ", dec tempset/2, ".", dec1 tempset*5,$DF, "C "'Display Tempset
endif
Pause 250 'Pause 250ms for switch debounce
do while Bypass = 0 'Loop until Bypass switch is released
Loop
goto again

End


Any assistance would be greatly appreciated.

Cheers
Barry
VK2XBP

Jerson
- 24th December 2012, 01:51
Just thinking out loud. At first glance, it looks like an overflow is breaking the average.

You are averaging 1000 samples of a 10bit value. Assuming the value you want to reach as in your text is 25.0(or 250 counts) in 1000 iterations, the variable for tempsum should be at lease 250x1000 = 250000 big. or, it should be bigger than a word sized variable (max 65535)

You could try an alternate averaging technique called moving average which works continuously per sample and is not delayed by waiting for 1000 readings.

Vout = (Vout * 3 + Adcin )/4

Here, you are continuously averaging the readings like you would do 4 of them together. You could change the formula to any value you want to speed up / slow down the averaging. The numbers should always be (Vout * (n-1) + adc)/n

Hope that helps

Aussie Barry
- 24th December 2012, 02:37
Hi Jerson,

Thanks for the quick reply.

I did the maths again for a 25.0 C reading and all looked good. The ADC result for a temperature of 25.0C is 50 and after summing 1000 measurements the result is less that 65535.
That's when it hit me - yesterday was a very hot day with temps over 35.0C. The ADC result for a temperature of 35.0C is 70 and summing 1000 measurements creates the overflow.

So simple! I don't know why the answer eluded me for so long :(

As mentioned in my previous posting, I was investigating alternate averaging routines and the moving average routine was the one I was looking to employ in this project. For now I will just back off the number of loops in the averaging routine to make sure no overflow occurs at temps up to 50C. I will look at modifying the routine using Darrel's DT_Analog with Hysteresis routine after Christmas.

My mind is at rest and I can pack away the straight jacket (for now at least...)

Cheers
Barry
VK2XBP

Heckler
- 24th December 2012, 15:42
There may be something here...
http://www.pbpgroup.com/modules/wfsection/article.php?articleid=7
that will help you with the averaging.

Darrel's premis is that if you want to average several items together then do the math in a sort of "reverse" fashion. ie. instead of adding 3 values together then dividing by 3. first divide each value by 3 then add them together. This will help of avoid overflow situations.

He does a much better job of explaining though.

Jerson
- 24th December 2012, 16:05
Hi Heckler

You got to be careful using that technique with integer math. It is workable if you use floating point operations. Been there.....

Cheers