I have been testing out a program I hope to put into use. I have a small hydroponics set up that grows lettuce and strawberries. (yes yes a guy with a hydro set-up and no marijuana being grown... )
I want to use two PIC's to control it. The two don't talk to each other. One PIC to control the water cycle, the other a micro that upon a press of a button, will give the temperature, water level and voltages etc.
So, here's my question. I've got the 'Sensor' PIC working. It does what I want, BUT, intermittently it will not quite do what I want. The code I'm posting has the full intent of the project in the header comments. After which I've listed the progress in getting each part to work. The last of these 'issues' comments will spell out the intermittent problem I'm having. Which is basically, after sending the PIC into Sleep mode for a long period of time and upon waking the temperature should be displayed on the 7-segment-display but isn't, either at all, or for a period of time.
What I'd like is for a second pair of eyes to look over the code and tell me any issues that they can see that would make this problem happen. NOTE: the code is basic and has a few band-aid solutions to get it doing what i want So don't expect genius here
Any help appreciated....
All the best
Chris
Code: Select all
' Name: V11_LM35_to_7_SD_RB0ExtInt_Sleep_IOCB
' PIC: 18F45K22
' OSC: 8MHz Crystal, HS (4xPLL enabled)
' Dev-board: easyPIC V7
' S/W: MikroBasic PRO for PIC V.6.0.0
' Date: 2014-04-24
' Libraries:
' - ADC (Not really needed)
' - Button
' Intent:
' - The control of a hydroponic garden with two PIC's:
' 'Sensor circuitry', which consist of a 12V 7.2Ah battery, a 12V 2.8W solar cell and the load (the Sensor PIC and
' associated sensor-loads) and;
' a 'Pump Control circuitry', which consist of a 12V 65Ah battery, 12V 20W solar cell,
' solar-to-SLA battery charger, it's control PIC and load (pumps).
' - This program tests out the 'Sensor PIC' on the easyPIC V7. The only difference is that the real world
' LM35 temperature sensor will be connected to RE0, easyPIC V7 has it on RE1. An N-channel MOSFET provides the LM35
' power and is controlled by RB3, here just an LED.
' - The temperature is displayed on four 7-segment-displays.
' - A voltmeter circuit is fed four different voltages from the two circuits (Sensor and Pump) to check things like
' the voltage of the battery only, the battery and solar cell, or just the solar cell. This is selected by a
' rotary switch.
' - The N-channels for these relays are represented by RC0, RC1, RC2 and RC3 LEDs. Real world MOSFETs are connected
' to these pins. Also, LEDs RB1 (Solar Voltage Check Indication) and RB2 (Battery Voltage Check Indication) exist.
' - The holding tank for the water/nutrient has two water level sensors on it. The 'Sensor PIC' only controls
' power going to them as the results are displayed elsewhere. The 'Sensor PIC' controls the float switch on RE1,
' for the easyPIC V7 this is moved to RE0. And a reed switch on RE2. Power to these sensors are controlled by
' N-channel MOSFETs and on the easyPIC V7 are represented by LEDs.
' - A wake/sleep button named 'WAKE' is connected to RB0 which will send the PIC into sleep mode or wake it.
' RB0 External Interrupt is used to wake and send to sleep via the Button library.
' - The four position plus 'OFF' position rotary switch is connected to RB4, RB5, RB6 and RB7. It uses IOCB interrupt
' to wake from sleep and when awake change the configuration of the voltage monitor circuit relays, giving the
' battery only, battery plus solar cell etc voltages to the voltmeter for display elsewhere.
' - TMR0 controls the display rate on the 7-segment displays.
' - TMR1 is a 'timeout' timer. If no WAKE button is pressed within 10 seconds, TMR1 will send the PIC into sleep mode.
' - The change in position of the rotary switch will reset the count of TMR1 to zero and restart the count to sleep.
' - Under normal conditions the rotary switch common will be connected to the 5V supply. The position in sleep should
' be switched back to 'OFF'. Any other position, 5V through a 220R resistor, into the PIC.
'
' Notes:
' - V02
' - Named wrong, "LM35_to_7_SD_IOCB_Sleep_V02", should be "LM35_to_7_SD_RB0ExtInt_Sleep_V03"
' - V03
' - Get temperature sensor working.
' - Get RB0 Ext.Int. working (Sleep function)
' - V04
' - Get IOCB Rotary Switch working.
' - V05
' - Issues from V04:
' - Going to sleep works
' - Awaking from sleep sometimes does not work.
' - IOCB changes work
' - IOCB can appear in sleep mode
' - V06
' - issues from V05:
' - Same as V04 Issues.
' - Going to sleep works, awakening mostly works but can seem to get stuck without
' displaying the temp, only the RE0 and RE2 LEDs.
' - If switch is in an "on" position the portb solar or batt leds light and the portc leds
' light with the sleep leds.
' - Sleep leds can turn off after pic goes to sleep, can't tell if that means the pic isn't
' going into sleep mode.
' - Adding "Selection = 0" in Sleep routine seems to have solved these issues.
' - V07
' - Trying a clean up of the code.
' - Add TMR1 for a "timeout". If the rotary switch (voltmeter Selection) is activated from sleep, or,
' the WAKE button is pressed but not re-pressed, or, from initial power-ON and no other interruption
' that would reset TMR1 is activated, TMR1 will count to 10 seconds and put the PIC to sleep.
' - TMR1 is enabled from Power-ON; a WAKE button (RB0 Ext. Int.) press and no re-press; a turn of the
' rotary switch (voltmeter Selection) (RB4:5:6:7 - IOCB interruption).
' - TMR1 count is reset to zero after Rotary Switch Selection change only.
' - Issues:
' - "Timeout" works well, 7-SD has one out of four displays still running.
' - V08
' - Try switching off TMR0 at TMR1 and interrupt and "Goodnight" routine.
' - Try "LATA = 0" and "LATD = 0" at both places as well.
' - Issues:
' - Sleep is okay, no 7-SD lit.
' - Rotary switch change flickers 7-SD upon waking. WAKE button has no issue, needs 1sec press.
' - Sleep LEDs do not turn off upon rotary switch awakening.
' - RE0 and RE2 LEDs do not turn on upon rotary switch awakening.
' - V09
' - Try put "Waking = 0x00" in the 'send pic to sleep' section of the TMR1 interrupt routine.
' - No issues when Waking the PIC with rotary switch.
' - V10
' - Code clean up.
' - Issues:
' - Awaking from sleep, WAKE button does not show 7-SD until late (approx. 5sec)
' - Rotary switch shows 7-SD upon Waking instantly.
' - RB0 Ext. Int. Waking routine, de-bounce waiting lines removed.
' - "Waking = 0xFF" and "StartUp = 0xFF" added after "PIC_Set_Up()" as a pre-caution.
' -V11
' - Code clean up
' - RB3 LED added to represent LM35 N-channel
' - Issues:
' - After time in sleep mode, rotary switch has similar problems to V10 RB0 WAKE button
' in that 7-SD does not show anything.
' - Trying "Waking = 0xFF" and "StartUp = 0xFF" added after "PIC_Set_Up()" as a pre-caution.
' - Trying SelDump as the temp file to hold Selection. Put in all bit tests in IOCB interrupt routine.
' - Issues:
' - Only position one "Pump Battery" check position does not show 7-SD
' - Issue is an intermittent problem. After playing with the rotary switch and WAKE button, all positions
' allow 7-SD to show values.
' - Issues:
' - After a time the WAKE Button awaken press delivered the same no 7-SD showing issue as described above.
' - After a play with the rotary switch, or, pressing WAKE again (to go to sleep) and then re-pressing (to
' awaken) all is good, the 7-SD works.
' - An idea is that TMR0 is not loading at intermittent wakings (WAKE Button / Rotary Switch Position 1), but
' it could also be a problem with PORTA and PORTD and not TMR0 or an issue with all three. (I'm a bit stumped)
' - Test: Leave PIC in sleep mode. Try to re-create the problem. Try to get it re-working by a) turning rotary switch
' one position over and then turning back, and, b) sending the PIC back to sleep (WAKE Button) and then
' re-awakenning with another press of WAKE Button, to see if that clears the issue and 7-SD works.
' - Results: a) can not get issue to happen on demand....dang it ;)
' b) ditto...
'
Program V11_LM35_to_7_SD_RB0ExtInt_Sleep_IOCB
'-------------------------------------------------------------------------------Declarations section
Const VREF as word = 50000 ' ADC Voltage Ref.
Dim temp_res as word ' ADC variable
Dim shifter, portd_index as byte ' 7-Segment-Display variables
digit, number as word '
portd_array, buff_array as byte[4] '
ready as bit '
Dim OldstateWAKE as byte ' WAKE Button variable
Dim Waking as byte ' PIC Sleep/Awake variable
Dim StartUP as byte ' PIC Sleep/Awake variable
Dim Flag as bit ' IOCB variable
Dim Selection as byte ' IOCB variable
Dim SelDump as byte ' IOCB variable
Dim Dump as byte ' IOCB temp variable
Dim TMR1CNT as word ' TMR1 counter
Sub procedure PIC_Set_Up()
ANSELA = 0 ' Configure PORTA pins as digital
ANSELB = 0 ' Configure PORTB pins as digital
ANSELC = 0 ' Configure PORTC pins as digital
ANSELD = 0 ' Configure PORTD pins as digital
ANSELE = 0x02 ' Configure RE1 pin as analog
TRISE1_bit = 1 ' Configure RE1 pin as input
TRISA = 0 ' Configure PORTA as output (7-SD display)
LATA = 0 ' Clear PORTA
TRISB = 0xF1 ' Configure PORTB as mixed I/O (0=WAKE Button/LED/LED/LED/SW-a/SW-b/SW-c/SW-d=7)
LATB = 0x08 ' Clear PORTB, except RB3 (LM35 n-channel, represented as an LED)
TRISC = 0 ' Configure PORTC as output
LATC = 0x0 ' Clear PORTC
TRISD = 0 ' Configure PORTD as output (7-SD segments)
LATD = 0 ' Clear PORTD
TRISE0_bit = 0 ' Configure PORTE.0 bit as output (LED)
LATE0_bit = 1 ' Turn on LED
TRISE2_bit = 0 ' Configure PORTE.2 bit as output (LED)
LATE2_bit = 1 ' Turn on LED
OldstateWAKE = 0x00 ' Clear Button variable
Waking = 0xFF ' PIC awake
StartUp = 0x00 ' No need to come back to this routine
Flag = 0 ' Clear IOCB variable
Selection = SelDump '
For shifter=0 to 3 ' Blank display
portd_array[shifter]=0
Next shifter
ADCON0 = 0x19 ' Enable ADC on AN6
ADCON1 = 0 ' CCP5 special trigger, AVdd and AVss voltage reference
ADCON2 = 0x96 ' Fosc/64, right justified
T0CON = 0xC4 ' Set TMR0 in 8bit mode, assign prescaler to TMR0
TMR0L = 0 ' Clear TMROL
digit = 0 ' Get 7-SD variables ready
portd_index = 0 '
shifter = 1 '
ready = 0 '
T1CON = 0x11 ' Set TMR1 to 1:2 prescale, clock source is instruction clock (Fosc/4)
TMR1IF_bit = 0 ' Clear TMR1 flag
TMR1H = 0x63 ' TMR1 set for 10ms
TMR1L = 0xBF '
TMR1CNT = 0 ' Clear TMR1 counter
GIE_bit = 1 ' Enable interruption flags
PEIE_bit = 1 ' Peripheral interruptions enabled (TMR1)
TMR0IE_bit = 1 ' TMR0 interruption enabled
TMR1IE_bit = 1 ' TMR1 interruption enabled
INTEDG0_bit = 1 ' Ext.Int. 0 Edge select = Int on rising edge
INT0IE_bit = 1 ' PortB Ext.interruption enabled
IOCB = 0xF0 ' All IOC pins enabled
RBIE_bit = 1 ' PortB IOC Interruption enabled
End sub
Sub procedure Goodnight() ' Sleep function
TMR0L = 0 ' Reset TMR0 count
TMR0IE_bit = 0 ' TMR0 interruption disabled
LATA = 0 '
LATD = 0 '
TMR1ON_bit = 0 ' Stop TMR1
TMR1IE_bit = 0 ' Disable TMR1 interruptions
TMR1IF_bit = 0 ' Clear TMR1 flag
TMR1H = 0x63 ' TMR1 set for 10ms
TMR1L = 0xBF '
TMR1CNT = 0 ' TMR1 Counter reset to zero
Selection = 0 ' just in case
LATB1_bit = 0 ' Solar LED OFF, Battery LED OFF
LATB2_bit = 0 '
LATB3_bit = 0 ' LM35 N-channel represented as an LED, turn OFF
LATC = 0xF0 ' Sleep LEDs on
LATE0_bit = 0 ' Turn off LED (Water-Level F/L N-channel)
LATE2_bit = 0 ' Turn off LED (Water-Level R/S N-channel)
Dump = PortB ' Pre-caution for IOCB
ASM
Sleep ' PIC code
NOP ' Buffer
End ASM
End sub
Sub function mask(dim num as word) as word ' 7-SD number conversion
Select case num
case 0
result= $3F
case 1
result= $06
case 2
result= $5B
case 3
result= $4F
case 4
result= $66
case 5
result= $6D
case 6
result= $7D
case 7
result= $07
case 8
result= $7F
case 9
result= $6F
End select
End sub
Sub procedure interrupt
'-------------------------------------------------------------------------------TMR0, for 7-Segment-Display
If TestBit(INTCON,TMR0IF) = 1 then ' TMR0 interrupt
LATA = 0 ' Turn off all 7seg displays
LATD = portd_array[portd_index] ' Bring appropriate value to PORTD
LATA = shifter ' Turn on appropriate 7seg. display
' move shifter to next digit
shifter= shifter << 1 ' Next display please...
If (shifter > 8) then '
shifter = 1 '
End if '
' increment portd_index
Inc(portd_index) '
If (portd_index > 3) then '
portd_index = 0 ' Turn on 1st, turn off 2nd 7seg.
If ready then ' In case (lm35 value to celsius) conversion not done...
portd_array[0] = buff_array[0] ' ...just display the old value.
portd_array[1] = buff_array[1] '
portd_array[2] = buff_array[2] '
portd_array[3] = buff_array[3] '
ready = 0 '
End if '
End if '
TMR0L = 0 ' Reset TIMER0 value
ClearBit(INTCON,TMR0IF) ' Clear TMR0IF
End if
'-------------------------------------------------------------------------------RB0 External Interrupt, for WAKE Button
If TestBit(INTCON,INT0IF) = 1 then ' Ext.Int. 0 interrupt (RB0) (WAKE UP button)
If (Waking = 0x00) then ' PIC in Sleep mode, so go into awake mode
INT0IE_bit = 0 ' Stop more interrupts
TMR0L = 0 ' Reset TMR0 count
TMR0IF_bit = 0 ' TMR0 interruption disabled
LATA = 0x00 ' Turn off 7-SD
LATD = 0x00 '
TMR1ON_bit = 0 ' Stop TMR1
TMR1IE_bit = 0 ' Disable TMR1 interruptions
TMR1IF_bit = 0 ' Clear TMR1 flag
TMR1H = 0x63 ' TMR1 set for 10ms
TMR1L = 0xBF '
TMR1CNT = 0 ' TMR1 Counter reset to zero
INT0IE_bit = 1 ' Allow more interrupts
PIC_Set_Up() ' Set Up PIC, ready for action; starting TMR1.
Waking = 0xFF ' Button released, indicate need to awaken
StartUp = 0xFF '
goto ExtInt0EXIT ' Then get out of here
End if
If (Waking = 0xFF) then ' PIC awake, so go into sleep mode
TMR0L = 0 ' Clear TMR0 count
TMR0IE_bit = 0 ' Stop 7-SD from displaying anything
LATA = 0 '
LATD = 0 '
TMR1ON_bit = 0 ' Stop TMR1
TMR1IE_bit = 0 ' Disable TMR1 interruptions
TMR1IF_bit = 0 ' Clear TMR1 flag
TMR1H = 0x63 ' TMR1 set for 10ms
TMR1L = 0xBF '
TMR1CNT = 0 ' TMR1 Counter reset to zero
If (Button(PORTB, 0, 1, 1)) then ' Detect logical one on RB0 pin
oldstateWAKE = 1 '
End if '
RB0_Awake_Release: ' Debounce pre-caution
Delay_ms(1000) '
If (oldstateWAKE and Button(PORTB, 0, 1, 0)) then ' Detect one-to-zero transition on RB0 pin
Waking = 0x00 ' Indicate Sleep mode
oldstateWAKE = 0 '
Goodnight() ' Goto Sleep routine
else '
goto RB0_Awake_Release ' Allow no action until button press finished.
End if '
goto ExtInt0EXIT ' Get out of here after everything has been done
End if
ExtInt0EXIT: ' Exit routine
ClearBit(INTCON,INT0IF) ' Clear Ext.Int. 0 interrupt (RB0) (WAKE UP button)
End if
'-------------------------------------------------------------------------------RB4:5:6:7 Interrupt On Change, Rotary Switch
If TestBit(INTCON,RBIF) = 1 then ' PortB IOC interrupt (Volt-meter request button)
ClearBit(INTCON,RBIF) ' Reset RB-IOC flag
Flag = 0
TMR1ON_bit = 0 ' Stop TMR1
If (PORTB.B7 = 1) then ' Checks if the RB7 is selected
Selection = 4 ' "Sensor Solar/Battery" position
SelDump = Selection '
Flag = 1
goto IOCB_End
End if
If (PORTB.B6 = 1) then ' Checks if the RB6 is selected
Selection = 3 ' "Sensor Battery" position
SelDump = Selection '
Flag = 1
goto IOCB_End
End if
If (PORTB.B5 = 1) then ' Checks if the RB5 is selected
Selection = 2 ' "Sensor Solar" position
SelDump = Selection '
Flag = 1
goto IOCB_End
End if
If (PORTB.B4 = 1) then ' Checks if the RB4 is selected
Selection = 1 ' "Pump Battery" position
SelDump = Selection '
Flag = 1
goto IOCB_End
End if
If (Flag = 0) then ' Checks if nothing is selected
Selection = 0 ' "OFF" position
SelDump = Selection '
Flag = 0
End if
IOCB_End:
If (Waking = 0x00) then ' PIC in Sleep mode, awaken PIC
SelDump = Selection ' Save Selection
PIC_Set_Up() ' Awken PIC
Waking = 0xFF ' Button released, indicate need to awaken
StartUp = 0xFF '
Selection = SelDump ' Restore Selection
else
TMR1CNT = 0 ' TMR1 Counter reset to zero. Restart TMR1
TMR1H = 0x63 ' TMR1 set for 10ms
TMR1L = 0xBF '
TMR1IF_bit = 0 ' Clear TMR1 Flag
TMR1IE_bit = 1 ' Enable TMR1 interruptions
TMR1ON_bit = 1 ' Start TMR1
End if
End if
'-------------------------------------------------------------------------------TMR1, for timeout of device if WAKE not re-pressed
If TestBit(PIR1,TMR1IF) = 1 then ' TMR1 "timeout" interrupt routine
TMR1H = 0x63 ' Reset the 10ms count in TMR1
TMR1L = 0xBF ' and clear the TMR1 flag
ClearBit(PIR1,TMR1IF) '
If (TMR1CNT = 1000) then ' If TMR1 counter has reached (0.01s*1000=)10seconds
' then a "timeout" has occurred. Put PIC to sleep.
Waking = 0x00 ' Indicate the PIC is going to be asleep
Goodnight() ' Send PIC to sleep.
else '
Inc(TMR1CNT) ' If 10seconds has not been reached, keep counting.
End if '
End if '
End sub
main:
'-------------------------------------------------------------------------------Main program
SelDump = 0 ' Switch On pre-caution
Dump = 0 ' Switch On pre-caution
PIC_Set_Up() ' Set Up PIC (Switched ON)
While TRUE
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() ' Here and in all other "catch" places, just in case.
End if '
GO_NOT_DONE_bit = 1 ' start ADC (ADCON0 bit 1 - GO/notDONE)
While GO_NOT_DONE_bit ' wait for measurement
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
GO_NOT_DONE_bit = 0 ' If awakening stop this part and set up...
End if '
Wend '
Lo(temp_res) = ADRESL ' Gets ADC read into a more usable format
Hi(temp_res) = ADRESH '
number = longword(temp_res * VREF) >> 10 ' Celsius Conversion
While ready ' Just precaution
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
ready = 0 ' If awakening stop this part and set up...
End if '
Wend '
digit = number / 1000 ' Extract thousands digit
buff_array[3] = mask(digit) ' and store it to PORTD array
digit = (number / 100) mod 10 ' Extract hundreds digit
buff_array[2] = mask(digit) + 0x80 ' and store it to PORTD array, then add a decimal point
digit = (number / 10) mod 10 ' Extract tens digit
buff_array[1] = mask(digit) ' and store it to PORTD array
digit = number mod 10 ' Extract ones digit
buff_array[0] = mask(digit) ' and store it to PORTD array
ready = 1 ' Result done
Select Case Selection ' Rotary Switch Selection
Case 0 ' Switch in the "OFF" position
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
End if '
' No Voltmeter Selection
LATC0_bit = 0 ' Relays not to change state.
LATC1_bit = 0 '
LATC2_bit = 0 '
LATC3_bit = 0 '
' Indication LEDs
LATB1_bit = 0 ' Solar LED OFF
LATB2_bit = 0 ' Battery LED OFF
Case 1 ' Switch in the "Pump Battery" position
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
End if '
' Pumps. Solar off and Battery to Test Point.
LATC0_bit = 1 ' Supply N-channel-1 Pump
LATC1_bit = 0 ' Supply N-channel-2 PIC - PWR
LATC2_bit = 0 ' Supply N-channel-3 PIC - SC
LATC3_bit = 0 ' Supply N-channel-4 PIC - SC/BATT
' Indication LEDs
LATB1_bit = 0 ' Solar LED OFF
LATB2_bit = 1 ' Battery LED ON
Case 2 ' Switch in the "Sensor Solar" position
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
End if '
' Sensor. Solar Cell only to Test Point.
LATC0_bit = 0 ' Supply N-channel-1 Pump
LATC1_bit = 1 ' Supply N-channel-2 PIC - PWR
LATC2_bit = 1 ' Supply N-channel-3 PIC - SC
LATC3_bit = 0 ' Supply N-channel-4 PIC - SC/BATT
' Indication LEDs
LATB1_bit = 1 ' Solar LED ON
LATB2_bit = 0 ' Battery LED OFF
Case 3 ' Switch in the "Sensor Battery" position
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
End if '
' Sensor. Solar off and Battery to Test Point.
LATC0_bit = 0 ' Supply N-channel-1 Pump
LATC1_bit = 1 ' Supply N-channel-2 PIC - PWR
LATC2_bit = 1 ' Supply N-channel-3 PIC - SC
LATC3_bit = 1 ' Supply N-channel-4 PIC - SC/BATT
' Indication LEDs
LATB1_bit = 0 ' Solar LED OFF
LATB2_bit = 1 ' Battery LED ON
Case 4 ' Switch in the "Sensor Solar/Battery" position
If (StartUp = 0xFF) then ' Check if PIC has just been awakened
PIC_Set_Up() '
End if '
' Sensor. Solar and Battery to Test Point.
LATC0_bit = 0 ' Supply N-channel-1 Pump
LATC1_bit = 1 ' Supply N-channel-2 PIC - PWR
LATC2_bit = 0 ' Supply N-channel-3 PIC - SC
LATC3_bit = 1 ' Supply N-channel-4 PIC - SC/BATT
' Indication LEDs
LATB1_bit = 1 ' Solar LED ON
LATB2_bit = 1 ' Battery LED ON
End select
Delay_ms(2000) ' Wait a time (could be 200ms, 500ms, 1sec or 2sec)
Wend
End.