;******************************************************************
;*********** Freeware 12Volt Halogen Lamp Dimmer ****************
;******************************************************************
;
; Revision 4
;
; All text following semicolon ";" are comments.
;
; This code may not be used for commercial purposes.
;
; Assembly code for PIC16F84. Use MPASM or MPLAB to assemble.
; All rights reserved. Jim Robertson, Oct 2000.
; Details and schematic @ http://lightbrain.8m.com
; Default radix = hex decimal numbers are preceded by "." (i.e. ".200")
; Okay to use 4Mhz ceramic resonator with built in capacitors.
;
; Values used are for 12 volt system with 20 watt lamp.
list p=16F84
; list directive to define processor
#include <p16F84.inc> ; processor specific variable definitions
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC ; configuration word
;******************* VARIABLE DEFINITIONS ****************************
; (16F84 ram locations start at 0x0c)
hi_count EQU 0x0c
; delay register
lo_count EQU 0x0d
; delay register
mainloop EQU 0x0e
; pwm loop register
ld_count EQU 0x0f
; long delay register
ontime EQU 0x10
;
offtime EQU 0x11
;
button_timer EQU 0x12 ;
;******************* CONSTANT DEFINITIONS *****************************
lamp EQU 1
; define PORTB bit 1 for pwm output (HEXFET)
button EQU 7
; define PORTB bit 7 for button input
;**********************************************************************
ORG 0x000
; processor reset vector
;**********************************************************************
;
; Program execution starts here after power up or wake up from sleep
;
;**********************************************************************
main
clrf PORTB
; clear PORTB
bsf STATUS, RP0
; select register bank 1
movlw b'10000000' ; set PORTB bit 7 to input all others are outputs
movwf TRISB
; move contents of W to tristate for PORTB
bcf STATUS, RP0
; select register bank 0 (normal)
movlw b'00001000'
movwf INTCON
; enable wake up on PORTB change
movlw .100
; short delay to prevent accidental on
call long_delay
btfss PORTB, button ; is button still being pressed?
goto power_down
; if no - go to sleep
pwr_on
btfsc PORTB, button ; has button been released?
goto pwr_on
; (ensures mode is not entered while button pressed)
;********************** 7 watt mode **************************
seven_watt
; 35% duty cycle is approx 7 watts
movlw .19
; sets how long the button must be held to
movwf button_timer ; turn the lamp off (19 = approx 2 Seconds)
movlw .35
; sets pwm "on time" 3.5 ms
movwf ontime
movlw .65
; sets pwm "off time" to 6.5 ms
movwf offtime
; \ (3.5 + 6.5 = 10 ms or 100Hz pwm rate)
call runbulb
; call pwm subroutine
;********************* 10 watt mode **********************
ten_watt
; 60% duty cycle is approx 10 watts
movlw .19
movwf button_timer
movlw .60
movwf ontime
movlw .40
movwf offtime
call runbulb
;********************** 20 watt mode ************************
twenty_watt
; 100% duty cycle is full 20 watts
movlw .19
movwf button_timer
movlw .100
movwf ontime
movlw .1
; short off time to allow normal button scan
movwf offtime
call runbulb
goto seven_watt
;************************ Subroutines **************************************
;***************************************************************************
; PWM and button scanning subroutine
; Short button press will fall
; thru to next power level. Long press (2sec)
; will go to power down (sleep).
;***************************************************************************
runbulb
movlw .7
; set button scan rate to approx 14 Hz
movwf mainloop ; (7 x 10ms = 70ms)
run_d2
bsf PORTB, lamp ; turn lamp on
movf ontime, 0 ; move ontime to W register
call v_delay
; call variable delay
bcf PORTB, lamp ; turn lamp off
movf offtime, 0 ; move offtime to W register
call v_delay
; call variable delay
decfsz mainloop ; decrement mainloop, if zero then read button
goto run_d2
; if not keep looping
btfsc PORTB, button ; read button
goto timer_1
; if it is being pressed decrement timer_1
movlw .19
subwf button_timer, 0 ; compare value in button_timer register to 19
btfsc STATUS, Z ; does button_timer register still = 19 ?
goto runbulb
; if yes - button has not been touched - continue pwm
return
; if no - button has been pressed and released
; go to next power level
timer_1
; test for long button press
decfsz button_timer ; decrement button_timer, is it = 0 ?
goto runbulb
; if no - continue pwm
goto power_down ; if yes - button was held for 2 seconds so power down
;***********************************************************
power_down
; turn lamp off and go to sleep (low power mode)
bcf PORTB, lamp
movlw .250
; delay 1 second to allow button to be released before
call long_delay ; putting processor to sleep
movlw .250
call long_delay
movlw .250
call long_delay
movlw .250
call long_delay
clrf PORTB
sleep
; wake up (button press) will execute next instruction
goto main
;******************** delay subroutines *************************************
long_delay
; enter here with desired ms in W (max 255ms)
movwf ld_count ; (1ms resolution)
ld_loop
call ms_delay ; call 1ms delay
decfsz ld_count ; decrement ld_count until it reaches zero
goto ld_loop
return
ms_delay
; enter here for fixed 1 ms (.001 second) delay
movlw .10
;(1) load 10 into W register
v_delay
; enter here for delay with desired ms*10 in W (max 25.5ms)
movwf hi_count ;(1) (i.e. for 20 ms delay W = .200)(.1ms resolution)
loop1 movlw .31
;(1)
movwf lo_count ;(1)
loop2 decfsz lo_count ; (30x3)+2 = 92 cycles (4MHz = 1 uS per instruction cycle)
goto loop2 ;/
nop
;(1)
decfsz hi_count ;(1)
goto loop1 ;(2)
return
end