PDA

View Full Version : Aquarium Lighting project - PWM - Need some assistance as I'm rusty !



Scampy
- 24th December 2012, 12:18
Hi Guys, been a while since I was here, hope everyone is OK and would firstly like to wish everyone the complements of the season.

Some of you may remember the vivarium project I developed with the help of Darrel, and Henrik. I'm pleased to say the unit is still running and keeping my snakes nice and warm. It hasn't missed a beat in all these years. Anyway, I digress slightly, I'm looking at developing another controller for a marine fish tank, based on the same chip and same PCB as I still have a few left over. Most of the code will be harvested from the thermostat project, but one area that has had my grey hair turning white is the PWM aspect, which is where I'm seeking some assistance.

Basics:
PIC 18F4580 - running with a 20mhz Xtal - programmed with the HS-PPL config fuse (PIC is maxed out at 40 Mhz)
DS1307 RTC
Port B6 and B7 to be PWM

The RTC data has been converted into decimal, so I have two variables TimeH and TimeM for hours and minutes in 24Hr format. These are used to set the case for the lighting period for two channels. Each of the two channels will have a Dawn, Day, Dusk and Night period, so for example Dawn could be 08:00 - 09:15, Day then from 09:15 - 17:00, Dusk from 17:00 - 18:30 and Night from 18:30 - 08:00 the next day. The idea is to fade up the lights whilst in the Dawn period, run them at max in the day, then fade them down in the Dusk period with them being off at night. As I'm not using the hardware PWM and opting for software control, and it's been several years since I last touched PBP I'm somewhat rusty on how to resolve the issue of incrementing the PWM duty variable so that it rises and falls from 0 to 255 (and then 255 - 0) over whatever time period is set for Dawn and Dusk. So in the above example, it would fade up from 0 to 255 over the course of 75 minutes. If the Dawn period was extended say between 08:00 and 09:30, then it would need to fade over 90 minutes. The frequency of the cycles needs to be 500 htz as well.

I've picked up one suggestion from someone who built a similar project, and have modified the code as such


Vb_5 = ((b_fade_in.highbyte * 60) + b_fade_in.lowbyte) *60 'Get total duration time in seconds
Vb_6 = B_Max - B_Min 'Work out the 'distance' the lights go from min to max
B_Fadein_Time = Vb_5 / Vb_6 'Get number of seconds between each PWM change
Vb_7 = Vb_5 // Vb_6 'Get fractions of a second
if Vb_7 >=5 then B_Fadein_Time = B_Fadein_Time + 1 'If its over half a second then round up

Vb_5 = ((B_Fade_Out.highbyte * 60) + B_Fade_Out.lowbyte) *60
B_Fadeout_Time = Vb_5 / Vb_6
Vb_7 = Vb_5 // Vb_6 'Get fractions of a second
if Vb_7 >=5 then B_Fadeout_Time = B_Fadeout_Time + 1 'If its over half a second then round up


Vb_7 holds the variable in seconds which I've tested by changing the period for Dawn and Dusk. But I'm at a loss how to use this to increment the variable (B_PWM) between 0 and 255. I know I need to increment B_PWM by one every Vb_7 seconds, but just can't seem to get my head round it.


Here's the full code



'************************************************* ***************
'* Name : Aquarium LED Lighting controller. *
'* Author : M. Crabbe *
'* Notice : Original Code by M Jervice *
'* : 18F2550 *
'* Date : *
'* Version : 1.0 *
'* Ported to the 18F4580 *
'* By Malcolm Crabbe *
'* 09/12/2012 *
'* Ver 1.1 - B *
'* *
'************************************************* ***************
@ errorlevel -205
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

DEFINE OSC 40 ; config settings 18F4580, 20mhz crystal
ADCON1 = $0F
clear

DEFINE LCD_DREG PORTB ' LCD Data port
DEFINE LCD_DBIT 0 ' starting Data bit (0 or 4)
DEFINE LCD_EREG PORTB ' LCD Enable port
DEFINE LCD_EBIT 5 ' Enable bit (on EasyPIC 5 LCD)
DEFINE LCD_RSREG PORTB ' LCD Register Select port
DEFINE LCD_RSBIT 4 ' Register Select bit (on EasyPIC 5 LCD)
DEFINE LCD_BITS 4 ' LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 4 ' number of lines on LCD
DEFINE LCD_COMMANDUS 2000 ' Command delay time in us
DEFINE LCD_DATAUS 50 ' Data delay time in us

'************************************************* ******************************

CMCON = 7 ' disable Comparators
ADCON1 = %00001011 ' sets up Analogue pins
ADCON2.7 = 1
TRISA = %00001111
TRISB = %00000000
TRISD = %00001111
TRISE = %1111

'************************************************* ******************************
i var word 'Misc counter
iv var word
LCD_Mode var word 'LCD light mode
LCD_Timer var word 'Timer for how long the LCD is on for.
LCD_Time var word 'Counter for the timer
LCD_Tmr var word

'************************************************* ******************************
' RTC Variables
'************************************************* ******************************

SCLpin var PORTC.3 ' RTC pin - clk
SDApin var PORTC.4 ' RTC pin - data

RTC_Msecs var byte
RTC_Secs var byte
RTC_Mins var byte
RTC_Hours var byte
RTC_Day var byte
RTC_WDay var byte
RTC_Month var byte
RTC_Year var byte
RTC_Ctrl var byte
dummy var byte
RTclock var byte[8]
Tick_Tmr var byte

Hours var byte
Minutes var byte

TimeH var byte ' Variable to store current hour
TimeM var Byte ' Variable to store current minutes

CounterA var byte ' General purpose Variable
CounterB var byte ' General purpose Variable
CounterC var byte ' General purpose Variable
CounterD var byte ' General purpose Variable

'************************************************* ******************************
' LED variables
' Whites:
'************************************************* ******************************
W_Min var word 'Min Light value
W_Max var word 'Max Light value
W_Fade_In var word 'Fade in duration
W_Fade_Out var word 'Fade out duration
W_Dawn_On_H var word 'What time to turn the lights on
W_Dawn_On_M var word 'What time to turn the lights on
W_Day_On_H var word 'What time to turn the lights on
W_Day_On_M var word 'What time to turn the lights on
W_Dusk_On_H var word 'What time to turn the lights on
W_Dusk_On_M var word 'What time to turn the lights on
W_On_H var word 'What time to turn the lights on
W_On_M var word 'What time to turn the lights on
W_Off_H var word 'Time to turn lights off
W_Off_M var word 'Time to turn lights off



'************************************************* ******************************
' LED variables
' Blues:
'************************************************* ******************************
B_Min var word 'Min Light value
B_Max var word 'Max Light value
B_Fade_In var word 'Fade in duration
B_Fade_Out var word 'Fade out duration
B_Dawn_On_H var word 'What time to turn the lights on
B_Dawn_On_M var word 'What time to turn the lights on
B_Day_On_H var word 'What time to turn the lights on
B_Day_On_M var word 'What time to turn the lights on
B_Dusk_On_H var word 'What time to turn the lights on
B_Dusk_On_M var word 'What time to turn the lights on
B_On_H var word 'What time to turn the lights on
B_On_M var word 'What time to turn the lights on
B_Off_H var word 'Time to turn lights off
B_Off_M var word 'Time to turn lights off

'Running fade variables
B_FadeIn_Time var word 'LIVE fade delay time seconds
B_FadeOut_Time var word
W_FadeIn_Time var word
W_FadeOut_Time var word
B_Cnt var word 'Seconds counter for the timing of adjusting the PWM
W_Cnt var word
B_Tick var word
W_Tick var word

'PWM Variables where the current output values are stored
W_PWM var word 'Whites
B_PWM var word 'Royal Blues

'************************************************* ******************************
Blue_Day_Cycle var word 'Store for which part of the day cycle we are in
White_Day_Cycle var word

' Constants for the current running mode cycle for the lights
DAWN con 0
DAY con 1
DUSK con 2
NIGHT con 3

'************************************************* ******************************
'Expendable variables used for misc calculations etc

Vw_5 var word
Vw_6 Var word
Vw_7 var word
Vw_8 var word

Vb_5 var word
Vb_6 Var word
Vb_7 var word
Vb_8 var word

'************************************************* ******************************

data @0
data 08 'Whites dawn ON Time HOURS
data 00 'Whites dawn ON Time MINS
Data 11 'Whites day ON time Hours
Data 00 'Whites day ON time Minutes
Data 16 'Whites dusk ON time Hours
Data 00 'Whites dusk On time Mins
data 20 'Whites OFF Time HOURS
data 00 'Whites OFF Time MINS
data 0 'Whites MIN Intensity %
data 75 'Whites MAX Intensity %
data 08 'Blue dawn ON Time HOURS
data 00 'Blue dawn ON Time MINS
Data 08 'Blue day ON time Hours
Data 01 'Blue day ON time Minutes
Data 08 'Blue dusk ON time Hours
Data 02 'Blue dusk On time Mins
data 08 'Blue OFF Time HOURS
data 03 'Blue OFF Time MINS
data 0 'Blue MIN Intensity %
data 100 'Blue MAX Intensity %
Data 08 'Blue On Hours
Data 30 'Blue On Min
Data 08 'White On Hrs
Data 30 'White on Min
data 00 'Blue ON time hours
data 01 'Blue ON time Mins
Data 00 'White ON time hours
Data 01 'White ON time mins

'************************************************* ******************************
'Setup Interrupts
'************************************************* ******************************
INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas"
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, _MyTimer, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
endasm
T1CON = %10000001 ; free-running, 1:1 prescaler
@ INT_ENABLE TMR1_INT ; enable Timer1 interrupts

'************************************************* ******************************
' Start the program
'************************************************* ******************************
code_start:

SetupPreset:
RTC_Msecs=$00 ' Seconds preset to 00
RTC_Secs=$00 ' Seconds preset to 00
RTC_Mins=$59 ' Minutes preset
RTC_Hours=$07 ' Hours preset
RTC_WDay=$01 ' Weekday preset to 01
RTC_Day=$12 ' Day preset 12
RTC_Month=$06 ' Months preset to June
RTC_Year=$02 ' Year preset to 2002
RTC_Ctrl=$10 ' Control preset to output 1 second 'Tick' on SQWpin

I2CWrite SDApin,SCLpin,$D0,$00,[RTC_Secs,rtc_mins,RTC_Hours,RTC_Ctrl]
B_Cnt = 0
W_Cnt = 0

gosub read_eeprom 'Read in the data needed for the lighting periods
LCD_Mode = 3 'LCD Backlight always ON

gosub Calc_Fade


'************************************************* ******************************
'Main program loop
'************************************************* ******************************
main:


gosub Read_Clock_2



if B_Dawn_On_H < TimeH and B_Dawn_On_M <= TimeM Then
Blue_Day_Cycle = NIGHT
endif

if B_Dawn_On_H = TimeH and B_Dawn_On_M = TimeM Then
Blue_Day_Cycle = DAWN
endif

if B_Day_On_H = TimeH and B_Day_On_M = TimeM then
Blue_Day_Cycle = DAY
endif


if B_Dusk_On_H = TimeH and B_Dusk_On_M = TimeM then
Blue_Day_Cycle = Dusk
endif

if B_Off_H = TimeH and B_Off_M = TimeM then
Blue_Day_Cycle = Night
endif




if W_Dawn_On_H < TimeH and W_Dawn_On_M < TimeM Then
White_Day_Cycle = NIGHT
endif

if W_Dawn_On_H = TimeH and W_Dawn_On_M = TimeM Then
White_Day_Cycle = DAWN
endif

if W_Day_On_H = TimeH and W_Day_On_M = TimeM then
White_Day_Cycle = DAY
endif


if W_Dusk_On_H = TimeH and W_Dusk_On_M = TimeM then
White_Day_Cycle = Dusk
endif

if W_Off_H = TimeH and W_Off_M = TimeM then
White_Day_Cycle = Night
endif

;***************************
'*** Do BLUE daily cycle ***
;***************************

select case Blue_Day_Cycle
case DAWN
lcdout $FE,$94,"BLUE: DAWN "
if B_cnt = B_Fadein_time and B_PWM < B_Max then
B_PWM = B_PWM + 1
B_Cnt = 0
endif
if B_PWM >= B_Max then
Blue_Day_Cycle = DAY
endif

case DAY
lcdout $FE,$94,"BLUE: DAY "
B_PWM= B_Max
B_Cnt = 0

CASE DUSK
lcdout $FE,$94,"BLUE: DUSK "
if B_Cnt >= B_Fadeout_time and B_PWM > B_Min then
B_PWM = B_PWM - 1
B_Cnt = 0
endif
if B_PWM = b_min then
Blue_Day_Cycle = NIGHT
endif


case NIGHT
lcdout $FE,$94,"BLUE: NIGHT"
B_Cnt = 0
B_PWM=0

end select

;****************************
'*** Do WHITE daily cycle ***
;****************************

select case White_Day_Cycle
case DAWN
lcdout $FE,$D4+8,"White: DAWN "
if W_Cnt = W_Fadein_time and W_PWM <= W_Max then
W_PWM = W_PWM + 1
W_Cnt = 0
endif
if W_PWM >= W_Max then
White_Day_Cycle = DAY
endif

case DAY
lcdout $FE,$D4+8,"White: DAY "
White_Day_Cycle = DUSK
W_Cnt = 0

CASE DUSK
lcdout $FE,$D4+8,"White: DUSK "
if W_Cnt >= W_Fadeout_time and W_PWM > W_Min then
W_PWM = W_PWM - 1
W_Cnt = 0
endif
if W_PWM = B_Min then
White_Day_Cycle = NIGHT
endif


case NIGHT
lcdout $FE,$D4+8,"White: NIGHT"
W_Cnt = 0

end select

;******************************
;*** Display Results on LCD ***
;******************************
'for test pourpose only

lcdout $FE,$80,#vb_5," ",#vb_6," ",#vb_7
lcdout $FE,$C0,#B_PWM

Pwm PORTD.7,B_PWM,1 'D7 used for EasyPIC5 development board - will be changed to RB7

goto main

Calc_Fade:

';************************************************ *******************************
'' LED Fade calculations
'' This routine takes the fade in/out durations and also the min/max
'' LED power values and calculates what the duration is between
'' changing the PWM value to give us a smooth fade from min to max
';************************************************ *******************************


Vb_5 = ((b_fade_in.highbyte * 60) + b_fade_in.lowbyte) *60 'Get total duration time in seconds
Vb_6 = B_Max - B_Min 'Work out the 'distance' the lights go from min to max
B_Fadein_Time = Vb_5 / Vb_6 'Get number of seconds between each PWM change
Vb_7 = Vb_5 // Vb_6 'Get fractions of a second
if Vb_7 >=5 then B_Fadein_Time = B_Fadein_Time + 1 'If its over half a second then round up

Vb_5 = ((B_Fade_Out.highbyte * 60) + B_Fade_Out.lowbyte) *60
B_Fadeout_Time = Vb_5 / Vb_6
Vb_7 = Vb_5 // Vb_6 'Get fractions of a second
if Vb_7 >=5 then B_Fadeout_Time = B_Fadeout_Time + 1 'If its over half a second then round up

Vw_5 = ((W_Fade_In.highbyte * 60) + W_Fade_In.lowbyte) *60
Vw_6 = W_Max - W_Min
W_Fadein_Time = Vw_5 / Vw_6
Vw_7 = Vw_5 // Vw_6 'Get fractions of a second
if Vw_7 >=5 then W_Fadein_Time = W_Fadein_Time + 1 'If its over half a second then round up

Vw_5 = ((W_Fade_Out.highbyte * 60) + W_Fade_Out.lowbyte) *60
W_Fadeout_Time = Vw_5 / Vw_6
Vw_7 = Vw_5 // Vw_6 'Get fractions of a second
if Vw_7 >=5 then W_Fadeout_Time = W_Fadeout_Time + 1 'If its over half a second then round up
return

'Lets say 2 hours (120 mins) & 191 for the fade amount (ie 0 to 255 or 0% to 100% intensity)
'So we need to do:
' convert hours into minutes, then add minutes and then multiply by 60 to convert to seconds
' so: 2 hours = 120 mins + 0 mins * 60 to get 7200 seconds
' The distance is now based on the converted % into the PWM range of 0-255
' calculate the 'distance' the light goes from min to max (0 to 255 in this eg: 75% = 191 so: 191- 0 = 191)
' now divide total seconds by 'distance' ie: 7200 / 191 = 37.69 seconds between each PWM change
' Now we test the milliseconds and if above .5 we round up to the next second

;************************************************* ******************************
' Read Eeprom data into variables
;************************************************* ******************************

Read_eeprom:
read 0,W_Dawn_On_H 'Whites ON time hours
read 1,W_Dawn_On_M 'Whites ON time minutes
Read 2,W_Day_On_H
Read 3,W_Day_On_M
read 4,W_Dusk_On_H
Read 5,W_Dusk_On_M
Read 6,W_Off_H
Read 7,W_Off_M
Read 8,W_Min
Read 9,W_Max
read 10,B_Dawn_On_H 'Whites ON time hours
read 11,B_Dawn_On_M 'Whites ON time minutes
Read 12,B_Day_On_H
Read 13,B_Day_On_M
read 14,B_Dusk_On_H
Read 15,B_Dusk_On_M
Read 16,B_Off_H
Read 17,B_Off_M
Read 18,B_Min
Read 19,B_Max
read 20,B_On_H
Read 21,B_On_M
read 22,W_On_H
Read 23,W_On_M
read 24,w_fade_in.highbyte 'Whites fade IN duration hours
read 25,w_fade_in.lowbyte 'Whites fade IN duration minutes
read 26,b_fade_out.highbyte 'Whites fade IN duration hours
read 27,b_fade_out.lowbyte 'Whites fade IN duration minutes


W_Min = (W_Min *255)/100 'Convert intensity % to real PWM value (from 0-100% to 0-255)
W_Max = (W_Max *255)/100
B_Min = (B_Min *255)/100
B_Max = (B_Max *255)/100
gosub Read_Clock_2

return
;************************************************* ******************************
MyTimer:
Tick_Tmr = Tick_Tmr + 1
'pause 100
if Tick_Tmr >179 then
Tick_Tmr = 0
B_Cnt = B_Cnt + 1
W_Cnt = W_Cnt + 1
endif
@ INT_RETURN
;************************************************* ******************************
'**** Ds1307 RTC Clock ****
;************************************************* ******************************

Read_Clock_2:


I2CRead SDApin,SCLpin,$D0,$00,[RTC_Secs,rtc_mins,rtc_hours,RTC_Ctrl] ; read DS1307 chip
If RTC_Hours.6=1 then
CounterA=(RTC_Hours>>4)&$01 ' Work-Out 12 or 24 hour Display for Hours
else
CounterA=(RTC_Hours>>4)&$03
endif
CounterA=CounterA*10+(RTC_Hours&$0F) ' Display Hours appropriately for 12 or 24 hour Mode
If RTC_Hours.6=1 then

LCDOut $FE,$D4,#CounterA
else
LCDOut $FE,$D4,#CounterA Dig 1,#CounterA Dig 0
endif

LCDOut ":",#(RTC_Mins>>4)&$0F,#RTC_Mins&$0F

TimeH=(RTC_Hours>>4) 'convert the BCD format of the hours register and store in variable timeH
TimeH=(TimeH &$03)*10
TimeH=TimeH+(RTC_Hours&$0F)

TimeM=(RTC_Mins>>4)
TimeM=(TimeM &$07)*10
TimeM=TimeM+(RTC_Mins&$0F) 'convert the BCD format of the mins register and store in variable timeM
Return

Scampy
- 24th December 2012, 13:01
I found DT's slow PWM (http://www.pbpgroup.com/modules/wfsection/article.php?articleid=6) maybe that would be the answer ??

Scampy
- 24th December 2012, 21:21
OK I've re-written the code to use the multiple spwm routine produced by DT, but still can't get the delay working right.

This is the routine that calculates the fade (or pause duration) for the dutycycle



Fade:
Vb_1 = ((B_Day_On_H - B_Dawn_On_H)*60) ' difference between day start and dawn start hour, converted to min
Vb_2 = (B_Day_On_M - B_Dawn_On_M) ' difference between day start and dawn start mins
Vb_5 = (Vb_1+Vb_2)*60 ' Get total duration time in seconds
Vb_6 = B_Max - B_Min ' Work out the 'distance' the lights go from min to max
B_Fadein_Time = Vb_5 / Vb_6 ' Get number of seconds between each PWM pulse


Setting the dusk start time to 8:00 and the day on time to 8:05 I get the following results for the variables (B_Min = 0 and B_Max = 255) on the LCD

Vb_5 = 600
Vb_6 = 255
B_Fadein_time = 2

I then have the following statement which should increment the duty cycle until it reaches 255, with each cycle paused 2 seconds



case DAWN

lcdout $FE,$94,"BLUE: DAWN "
DutyCycle1 = DutyCycle1 +1
pause B_Fadein_Time
if DutyCycle1=B_Max then
Blue_Day_Cycle = DAY
endif


However when run the duty cycle cycles from 0 to 255 so fast you can hardly notice it. Right, so the pause 2 statement is going to be too low, as pause 1000 equates to 1 second, so I multiplied the result by 1000 - the net result was too slow, it never reached 255 by the time the 5 minutes were up. Doing the maths I need to divide 600 seconds by 255 steps = 2.352941176470588. So I really need to convert this to a manageable number, say 2.353. ( B_Fadein_time is a word variable so should be able to store that result), and then convert this into seconds to use in the delay loop, but in a way that actually works !

Assistance would be appeciated

Scampy
- 26th December 2012, 14:02
Seems this place is not what it used to be... almost a 100 views and not one comment or suggestion. :(

Normnet
- 27th December 2012, 05:13
This may be of little help but the upcoming FineLineIDE detected 3 unused labels:
Ln 213 code_start:
Ln 215 SetupPreset:
Ln 463 MyTimer:

Norm

Scampy
- 27th December 2012, 14:19
This may be of little help but the upcoming FineLineIDE detected 3 unused labels:
Ln 213 code_start:
Ln 215 SetupPreset:
Ln 463 MyTimer:

Norm

Thanks for the comment, but as you say, not a lot of help. The first two simply are place makers... the latter is a routine that exists as a subroutine prior to the clock read routine and is referenced as part of the setup of the interrupts - I'll think I'll stick to Microcode Studio as my IDE ;-)