PDA

View Full Version : Strange problem with an 18F4580 based project



malc-c
- 18th December 2010, 11:44
Guy's, this is probably aimed at DT or Henrik as they are really familiar with my thermostat project, but I'm open to suggestions.

My prototype thermostat unit (details can be found at http://www.picbasic.co.uk/forum/showthread.php?t=12712) has been running now for around 6 months, but recently I've had two incidences where the unit appears to be running, but has actually locked up in some way, after I tried fixing a problem with a light that is driven from the unit.

The first instance the light was out, so whilst everything was still running I detached the cable and reseated the tube then plugged the light (an under cabinet florescent T5 tube) back in. Everything seemed OK until I noticed the snake in each vivarum move right up the cold end of the enclosure. The temperature on the LCD still showed 33.1C and both LEDs were pulsing away as normal, but on opening the viv the heat was obvious. I reset the controller and off went the alarms as both vivs were running at 45c (which was now shown on the display).

The second instance was the same, but this followed a simple tapping of the tube to get it to light. Only this time I noticed that the temperature readings from the DS18B20's didn't fluctuate (normally they are constantly changing every few seconds by +/- 0.1c).

It is as if the program in the chip stops running the PID loop or something but where you would expect the LCD to blank out, or the LEDs to stop pulsing if the chip stopped running that doesn't happen. Could it be the fuse settings? Can a PIC go into a state of limbo like this ??

I've been told that some of the commercial companies hang out on the reptile forums have got wind of my project so I don't really want to paste up the current version of the code in full, so PM me if you need the files. I can obviously post up spinets, if required.

Comments please ;)

Acetronics2
- 18th December 2010, 12:44
Hi, Malc

@ First sight ... I could think the DS1820 do not update its scratchpad for any reason.
I read you use fluorescent tubes for lighting ... that rings my bell and I ask you

1) Which value for the pullup resistor ( usually 4.7k ... when sensor close to processor ! )
2) do you use ground shielded wire to connect your DSs ???
3) could you post me the DS identification and temp reading section of your program ? ( to see if any " health " control of the sensor is done )

may be we could think to some enforced DS chip " good running check " ...
some control like " heating power always rises, but temp not = default somewhere )

Alain

malc-c
- 18th December 2010, 13:03
Hi Alain, and thanks for posting.

Yes I'm using 4.7K resistors on each of the DS18B20's. The cable length is approx 2m from the tip of the sensor to the unit, and made up of three wires twisted (used a drill). My research didn't throw up anything about having to use shielded cable between the sensor and the PIC.

I'll zip up all the code (including the includes !) and let you check the code. To make things simpler, could you pm me your e-mail address.

Thanks

malc-c
- 18th December 2010, 13:43
Hope the following help

Initialization


;----[DS1820 Options]----Value----Default-----------------------------------
DEFINE DS1820_DECIMALS 1 ' 1
DEFINE DS1820_VERIFYCRC YES ' NO
DEFINE DS18B20_ONLY YES ' NO
INCLUDE "DT18x20.pbp" ' Include DT18x20 module




FOR Bvar = 0 to 3 ; assume all sensors are working
SensorActive(Bvar) = 1
NEXT Bvar

FOR pid_Channel = 0 TO 3 ; Set # of bits in resolution
GOSUB SelectSensor ; for all sensors
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Resolution 12
GIE = 1 ; enable interrupts after 1-wire
NEXT pid_Channel

LOW HeaterOut1 ; set heater pins Output to off
LOW HeaterOut2
LOW HeaterOut3
LOW HeaterOut4

Low Lights1 ; set lighting pins to off
Low Lights2

GOSUB SendPWRstat


The subroutine referred to above


SendPWRstat:
Bvar = 0
Bvar.0 = ChannelPWR(0)
Bvar.1 = ChannelPWR(1)
Bvar.2 = ChannelPWR(2)
Bvar.3 = ChannelPWR(3)
RETURN


Now for the main part



FOR pid_Channel = 0 TO 3 ; cycle thru all sensors
GOSUB SelectSensor
DS1820_Error = 0 ; clear any previous errors
DS1820_Flags = 0 ; clear status flags
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Stat ; check the sensors status
GIE = 1 ; enable interrupts after 1-wire
PAUSEUS 20
IF !DS1820_Done THEN SensorError ; if it's not done by now, error
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Read ; get the temperature result
GIE = 1 ; enable interrupts after 1-wire
GOSUB Check4Zeros

DS1820_Flags = 0
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Stat
GIE = 1 ; enable interrupts after 1-wire
IF (DS1820_Error = 0) AND DS1820_Done THEN ; if there were no errors
Temperatures(pid_Channel) = TempC
pid_Error = SetPoints(pid_Channel) - TempC
GOSUB PID
IF pid_Out.15 THEN pid_Out = 0 ; only keep positive values
IF ChannelPWR(pid_Channel) THEN
HeaterDrives(pid_Channel) = pid_Out
ELSE
HeaterDrives(pid_Channel) = 0
ENDIF
IF Temperatures(pid_Channel) > alarmhigh(pid_Channel) and Alarm = 1 then AlarmPin = 1
IF Temperatures(pid_Channel) > alarmhigh(pid_Channel) and Alarm = 0 then AlarmPin = 0
IF Temperatures(pid_Channel) <= alarmhigh(pid_Channel) then AlarmPin = 0
IF Temperatures(pid_Channel) <= alarmlow(pid_Channel) then AlarmPin = 0
if Temperatures(pid_Channel) < alarmlow(pid_Channel) and Alarm = 1 then AlarmPin = 1
if Temperatures(pid_Channel) < alarmlow(pid_Channel) and Alarm = 0 then AlarmPin = 0

GOSUB ShowLCD

ELSE
SensorError:
HeaterDrives(pid_Channel) = 0 ; turn off heater if sensor's bad
SensorActive(pid_Channel) = 0
GOSUB ShowError ; display error message
ENDIF
NEXT pid_Channel


The other two parts you may be interested in is the checking and displaying sections



AllZeros VAR Bit
Check4Zeros:
AllZeros = 1
FOR Bvar = 0 TO 8
IF DS1820_Buffer(Bvar) != 0 THEN AllZeros = 0
NEXT Bvar
IF AllZeros THEN DS1820_CRCfailed = 1
;------------------------------------------------------------------------------

;----[show output on LCD]-------------------------------------------------------
ShowLCD:
LOOKUP pid_Channel,[$80,$C0,$89,$C9],Bvar ; Find location
LCDOUT $FE,Bvar,DEC1 pid_Channel+1,"= " ; print to LCD
TempWD = TempC : GOSUB TempToLCD ; display TempC
LCDOUT $DF ; deg symbol



and the select sensor sub


SelectSensor:
SELECT CASE pid_Channel
CASE 0 : @ DS1820_Select _TempSensor1 ; Select the DS18x20 pin
CASE 1 : @ DS1820_Select _TempSensor2
CASE 2 : @ DS1820_Select _TempSensor3
CASE 3 : @ DS1820_Select _TempSensor4
END SELECT
RETURN



If you need more drop me an e-mail or I can post up more snipets

Darrel Taylor
- 18th December 2010, 16:09
Malcolm,

The heater outputs are interrupt driven, so if the main program locks up somewhere due to an unexpected condition, the heaters will continue pulsing at the last drive level given from the PID loops.

I can't imagine all four DS18B20's having a problem at the same time. The program is checking CRC's.
If 1 VIV got hot and the others were OK, I might suspect a DS.

You may need to use the WDT to monitor the main loop.
Then it'll reset the processor if it gets lost.

Using ...
DEFINE NO_CLRWDT 1
PBP will not add CLRWDT instructions throughout the code.
Then "strategically" placing CLRWDT statements in your program will clear it as long as the loop is running.

I say strategically, because if you place a CLRWDT in the area where it's locking up, it won't help.

Since the problem seems to be repeatable by fiddling with the lights, it should be fairly easy to locate good placements of the CLRWDT statements.

Blinking an LED in the main loop may help identify when or if it's locked.

malc-c
- 18th December 2010, 17:29
Darrel,

Thanks for your input - with your prior knowledge of this project I thought you would be best placed to know what is going on.

OK I'll be perfectly honest, I'm not 100% sure where I would need to place the CLRWDT commands, so here is the main program loop



;-----------------------------------------------------------------------------
; ***** MAIN PROGRAMMING LOOP *****
;-----------------------------------------------------------------------------

Main:


If SetButton=0 then ; jump to programming
Gosub SetButtonRelease
goto mainmenu
endif

If DecButton=0 then
Gosub SetButtonRelease
goto lightoveride ; manual overide of light
endif

If IncButton=0 then
Gosub SetButtonRelease
goto cancelalarm ; manual overide of alarm
endif


FOR pid_Channel = 0 TO 3 ; cycle thru all sensors
GOSUB SelectSensor
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Convert ; start a temperature conversion
GIE = 1 ; enable interrupts after 1-wire
NEXT pid_Channel

;----[check for data on com port]

FOR TempWD = 0 TO 10000
IF RCIF THEN GOto coms
PauseUs 100
NEXT TempWD
;--------------------------------


FOR pid_Channel = 0 TO 3 ; cycle thru all sensors
GOSUB SelectSensor
DS1820_Error = 0 ; clear any previous errors
DS1820_Flags = 0 ; clear status flags
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Stat ; check the sensors status
GIE = 1 ; enable interrupts after 1-wire
PAUSEUS 20
IF !DS1820_Done THEN SensorError ; if it's not done by now, error
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Read ; get the temperature result
GIE = 1 ; enable interrupts after 1-wire
GOSUB Check4Zeros

DS1820_Flags = 0
GIE = 0 ; disable interrupts before 1-wire
@ DS1820_Stat
GIE = 1 ; enable interrupts after 1-wire
IF (DS1820_Error = 0) AND DS1820_Done THEN ; if there were no errors
Temperatures(pid_Channel) = TempC
pid_Error = SetPoints(pid_Channel) - TempC
GOSUB PID
IF pid_Out.15 THEN pid_Out = 0 ; only keep positive values
IF ChannelPWR(pid_Channel) THEN
HeaterDrives(pid_Channel) = pid_Out
ELSE
HeaterDrives(pid_Channel) = 0
ENDIF
IF Temperatures(pid_Channel) > alarmhigh(pid_Channel) and Alarm = 1 then AlarmPin = 1
IF Temperatures(pid_Channel) > alarmhigh(pid_Channel) and Alarm = 0 then AlarmPin = 0
IF Temperatures(pid_Channel) <= alarmhigh(pid_Channel) then AlarmPin = 0
IF Temperatures(pid_Channel) <= alarmlow(pid_Channel) then AlarmPin = 0
if Temperatures(pid_Channel) < alarmlow(pid_Channel) and Alarm = 1 then AlarmPin = 1
if Temperatures(pid_Channel) < alarmlow(pid_Channel) and Alarm = 0 then AlarmPin = 0

GOSUB ShowLCD

ELSE
SensorError:
HeaterDrives(pid_Channel) = 0 ; turn off heater if sensor's bad
SensorActive(pid_Channel) = 0
GOSUB ShowError ; display error message
ENDIF
NEXT pid_Channel


; ---------------------------------------------------------------------------
; check lighting periods and turn lights on or off accordingly
; ---------------------------------------------------------------------------

fn = 0 ; select the first Lights
if lightover = 0 then GOSUB CheckTimes ; if manual override set to off then go compare the programed period
if lightover = 1 then progON=1 ; if manual override flag set to on then lights on flag set to 1
IF ProgON THEN ; If in the program period
IF Lights1 = 0 THEN Lights1 = 1
ELSE
IF Lights1 = 1 THEN Lights1 = 0
IF Lights1 = 0 then LCDOut $FE, $94+9," "
ENDIF

fn = 1 ; select the second Lights
if lightover = 0 then GOSUB CheckTimes ; compare the programed period
IF ProgON THEN
IF Lights2 = 0 THEN Lights2 = 1

ELSE
IF Lights2 = 1 THEN Lights2 = 0
LCDOut $FE, $94+9," "
ENDIF
IF Lights1 = 1 then LCDOut $FE, $94+9,"Light 1 ON"
IF Lights2 = 1 then LCDOut $FE, $94+9,"Light 2 ON"
if Lights1 = 1 and Lights2 = 1 then LCDOut $FE, $94+9,"Lights ON "
If Lights1 = 0 and Lights2 = 0 then LCDOut $FE, $94+9," "


; ---------------------------------------------------------------------------
; Check for night time drops - if condition matched, drop temp
; ---------------------------------------------------------------------------

fn = 0 ; select the first setting
GOSUB CheckTimes2 ; compare the programed period
IF ProgON2 THEN
SetPoints[0]=Droptemp[0] ; change the corresponding set point to the drop temperature
LCDOut $FE, $94,"Night"
ELSE
SetPoints[0]= normtemp[0] ; change the corresponding drop temperature to set point
LCDOut $FE, $94," "
ENDIF

fn = 1 ; select the second setting
GOSUB CheckTimes2 ; compare the programed period
IF ProgON2 THEN
SetPoints[1]=Droptemp[1] ; change the corresponding set point to the drop temperature
LCDOut $FE, $94,"Night"
ELSE
SetPoints[1]= normtemp[1] ; change the corresponding drop temperature to set point
LCDOut $FE, $94," "
ENDIF

fn = 2 ; select the third setting
GOSUB CheckTimes2 ; compare the programed period
IF ProgON2 THEN
SetPoints[2]=Droptemp[2] ; change the corresponding set point to the drop temperature
LCDOut $FE, $94,"Night"
ELSE
SetPoints[2]= normtemp[2] ; change the corresponding drop temperature to set point
LCDOut $FE, $94," "
ENDIF

fn = 3 ; select the fouth setting
GOSUB CheckTimes2 ; compare the programed period
IF ProgON2 THEN
SetPoints[3]=Droptemp[3] ; change the corresponding set point to the drop temperature
LCDOut $FE, $94,"Night"
ELSE
Setpoints[3]= normtemp[3] ; change the corresponding drop temperature to set point
LCDOut $FE, $94," "
ENDIF



;----[Flash Star on LCD]-------------------------------------------------------
If alarm=1 then
LCDOUT $FE,$80 + 18,("*"&FlashStar*$FF)|(" "&~(FlashStar*$FF)) ; flash a star to show sample time, but used to indicate Alarm settings monitored
FlashStar = !FlashStar
endif




;-----------------------------------------------------------------------------
; ***** MAIN PROGRAMMING LOOP END *****
GOTO Main
;-----------------------------------------------------------------------------


Could you advise me ??

Thanks

malc-c
- 18th December 2010, 18:32
Darrel, just checked the hardware file and these are the settings



ASM
__CONFIG _CONFIG1H, _OSC_HSPLL_1H
__CONFIG _CONFIG2L, _PWRT_ON_2L
__CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_512_2H
__CONFIG _CONFIG3H, _PBADEN_OFF_3H
__CONFIG _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
ENDASM


If I read that then the WDT is running, and as I've not inserted DEFINE NO_CLRWDT 1 into the code then I assume that PBP has automatically inserted the resets through the code automatically. However on checking the corresponding asm file there are just the following references to CLRWDT in that file (towards the beginning)



;---[Add an Interrupt Source to the user's list of INT Handlers]--------------
INT_Handler macro IntFlagReg, IntFlagBit, Label, Type, Reset
list
local AfterSave, AfterUser, NoInt
INT_Count += 1
PrList#v(INT_Count)R = IntFlagReg
PrList#v(INT_Count)B = IntFlagBit
PrList#v(INT_Count)Priority = Priority
GetIntInfo IntFlagReg, IntFlagBit
if (Found == YES)
btfss INT_Enable_Reg, INT_Enable_Bit, 0 ; if INT is enabled
goto NoInt
btfss INT_Flag_Reg, INT_Flag_Bit, 0 ; and the Flag set?
goto NoInt
if (Priority == H)
bsf _Serviced_H, 0
else
bsf _Serviced_L, 0
endif
ifdef NO_CLRWDT
if (NO_CLRWDT != 1)
CLRWDT
endif
else
CLRWDT
endif

if (Type == PBP) ; If INT handler is PBP
if (Priority == H)
ifdef ReEnterHPused
GetAddress21 AfterSave, RetAddrH
L?GOTO _SavePBP_H ; Save PBP system Vars in HP INT
else
error "ReEnterPBP-18 must be INCLUDEd to use High Priority PBP interrupts"
endif
else ; Priority = L
ifdef ReEnterLPused
GetAddress21 AfterSave, RetAddrL
L?GOTO _SavePBP_L ; Save PBP system Vars in LP INT
else
error "ReEnterPBP-18LP must be INCLUDEd to use Low Priority PBP interrupts"
endif
endif
endif



I've searched for info on using the watchdog timer and came across Mel's post in 2003 ??? which helps explain how PBP uses the timer... but still a bit confused given the complexity of our code

Darrel Taylor
- 18th December 2010, 22:50
To see where the CLRWDT instructions are inserted, you would have to look at the .LST file because they are part of the library, not the generated .ASM code.

The one's you found are part of DT_INTS, and as you can see they also respond to the NO_CLRWDT define.

The base period of the WDT on a 4580 is 4ms, and with a postscaler of 512 it will timeout after about 2 seconds.

Your main loop will take less than 2 seconds each time so a single CLEARWDT statement at the end of the loop just before GOTO Main should suffice, although it may be advantageous to put it in the area where it updates the heater drives just to be sure that is getting accomplished. But then if you turn off power to all 4 channels, you'll have to clear it somewhere else or it will reset every 2 seconds. You may just have to play with it and see what works best.

IF ChannelPWR(pid_Channel) THEN
HeaterDrives(pid_Channel) = pid_Out
CLEARWDT
ELSE
HeaterDrives(pid_Channel) = 0
ENDIF


HTH,

malc-c
- 19th December 2010, 00:10
The base period of the WDT on a 4580 is 4ms, and with a postscaler of 512 it will timeout after about 2 seconds.

Your main loop will take less than 2 seconds each time so a single CLEARWDT statement at the end of the loop just before GOTO Main should suffice,

Darrel,

I've added DEFINE NO_CLRWDT 1 at the start of the code along with other defines. Then added one CLEARWDT just before the GOTO MAIN -

Just before the main loop the code jumps to a simple section that displays the current version


about:
LCDOUT $FE,2," Multi - Therm "
lcdout $FE, $C0, " Thermostat "

lcdout $FE, $D4, " Firmware Ver 4.60 "
Pause 2000
LCDOUT $FE,1
goto main


No with the CLEARWDT inserted as suggested the code is constantly displaying the about screen. IE its constantly resetting which looking at the pause 2000 line in the about the same as the WDT.

So I commented out the GOTO About and the program ran fine, however if I jump to the main menu it quickly resets before you get chance to make any changes. So I guess I have to go through each section of the code to ensure that I insert enough CLEARWDT statements to allow the program to function, but not defeat the purpose of the WDT ?

Darrel Taylor
- 19th December 2010, 00:40
Yup, that's part of that "strategically" idea. :D

And now you will appreciate the idea of "Event Driven" software, instead of GOSUB somewhere then return when it's finished, whenever that is.

With event driven, everything keeps running, even if you're moving through menu's on an LCD.

Keep in mind that if your light bulb spark is causing a false button press and it's going off into one of those routines ... the WDT won't help unless that routine times out eventually because now you've added CLEARWDT's to it.

malc-c
- 19th December 2010, 10:45
Yup, that's part of that "strategically" idea. :D

And now you will appreciate the idea of "Event Driven" software, instead of GOSUB somewhere then return when it's finished, whenever that is.

With event driven, everything keeps running, even if you're moving through menu's on an LCD.

Keep in mind that if your light bulb spark is causing a false button press and it's going off into one of those routines ... the WDT won't help unless that routine times out eventually because now you've added CLEARWDT's to it.

Yeah, having to work through the sub-routines to add the CLEARWDT so that it doesn't reset, but then to add some form of time out routine so that if nothing is pressed it returns to the main menu.

I must admit my old brain finds it comfortable with the goto / gosub way of coding... the "if button pressed gosub xyz", and I wouldn't know how to re-work the code for event driven routines... still learning ! I'll keep plodding away through the code to insert the CLEARWDT hear and there, at least it will provide some form protection.