;; ;; Motion detecting lamp switch ;; ;; While idle, keeps lamps lit at low power. ;; ;; When motion is detected, first lamp output is turned to ;; high power. If motion ceases, after n minutes, return to ;; idle. If additional motion is detected, second lamp output ;; is turned to high power. If motion continues, both lamps ;; remain at high power, otherwise we return to idle. ;; ;; TODO, cancel button ;; TODO, stay on button ;; proto equ 1 ; we are prototyping on 16F84 ; change to zero for final burn if proto == 1 processor 16f84 list f=inhx8m include "p16f84.inc" __CONFIG _CP_OFF & _WDT_OFF & _HS_OSC else processor 12c509 list f=inhx8m include "p12c509a.inc" __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC endif ; general purpose register allocation ; first common free address as per ; p12 of 12c509 spec ; p13 of 16f84 spec tmp1 equ 0x0c ; delay loop outer counter tmp2 equ 0x0d ; delay loop inner counter tmp3 equ 0x0e ; delay loop inner counter a_left equ 0x12 ; duty cycle accumulators a_right equ 0x13 d_left equ 0x14 ; duty cycle settings d_right equ 0x15 a_heat1 equ 0x16 ; heat accumulators a_heat2 equ 0x17 s_left equ 0x18 ; desired settings s_right equ 0x19 ; heat reset value k_heat equ d'4' k_warm equ d'1' ; warming k_on equ d'255' ; light on ;; note, these must be either both odd or both even ; interface bit definitions if proto == 1 ; which port to use port equ PORTA else port equ GPIO endif b_right equ 0x00 ; gate of mosfet for right lamp b_left equ 0x01 ; gate of mosfet for left lamp b_go equ 0x02 ; motion detector b_led equ 0x03 ; red diagnostic led via 1k res align macro org (($>>2)<<2)+4 ; align to four byte boundary endm org 0x00 ; reset vector if proto != 1 movwf OSCCAL ; store oscillator calibration endif goto main align dt "quozl@us.netrek.org rev 0.4 2006-08-09" ;; ;; adjust, manages the change in brightness by moving d_ toward s_ ;; align adjust movf s_left,w ; W=0 subwf d_left,w ; F=9, W=9-0, result positive, C=1 btfsc STATUS,Z goto adjust_left_skip ; Z=1, is at desired state btfss STATUS,C goto adjust_left_up ; C=0 adjust_left_down ; C=1 decf d_left,f ; F=8 goto adjust_left_skip adjust_left_up incf d_left,f incf d_left,f goto adjust_left_skip adjust_left_skip movf s_right,w ; W=9 subwf d_right,w ; F=0, W=0-9, result negative, C=0 btfsc STATUS,Z goto adjust_right_skip ; Z=1, is at desired state btfss STATUS,C goto adjust_right_up ; C=0 adjust_right_down ; C=1 decf d_right,f goto adjust_right_skip adjust_right_up incf d_right,f ; F=1 incf d_right,f goto adjust_right_skip adjust_right_skip retlw 0 align ;; ;; cool, immediate reset of long duration timer ;; cool ; reset heat counter clrw movwf a_heat1 ; reset LSB movlw k_heat movwf a_heat2 ; reset MSB retlw 0 align ;; ;; heat, decrement the long duration timer until it expires ;; heat ; called slowly movlw k_on subwf d_left,w ; set zero flag if lamp on btfsc STATUS,Z ; skip if not zero goto heat_2 ; jump if it is on movlw k_on subwf d_right,w ; set zero flag if lamp on btfsc STATUS,Z ; check right lamp goto heat_2 ; jump if it is on heat_1 retlw 0 ; neither on, return heat_2 decf a_heat1,f ; decrement the heat counter LSB btfss STATUS,Z ; skip if zero goto heat_1 ; jump if not zero decf a_heat2,f ; decrement the heat counter MSB btfss STATUS,Z ; skip if zero goto heat_1 ; jump if no overflow (Z=0) goto main ; overheat; bug out! align ;; ;; light, manage the isochronous variable duty cycle outputs ;; light ; update lights using duty cycles movf d_left,w addwf a_left,f btfsc STATUS,C goto light_1 bcf port,b_left goto light_2 light_1 bsf port,b_left light_2 movf d_right,w addwf a_right,f btfsc STATUS,C goto light_3 bcf port,b_right goto light_4 light_3 bsf port,b_right light_4 ; timing; 12 to 14 cycles per pass retlw 0 align ;; ;; ms, delay as many milliseconds as the number in register w ;; ms macro delay ; delay "n" number of milliseconds local ms_0, ms_1 movlw delay ; set argument movwf tmp1 ; copy argument ms_0 ; start of 1mS loop movlw d'48' ; (tailored in ax2 for 1ms based on light call) movwf tmp2 ; move to register bank ms_1 call light ; keep lights on decfsz tmp2,f ; decrement inner counter goto ms_1 ; end of loop decfsz tmp1,f ; decrement outer (1mS) counter goto ms_0 ; end of 1mS loop endm ds macro delay ; delay "n" number of deciseconds local ds_0 movlw delay movwf tmp3 ds_0 ms d'10' call adjust decfsz tmp3,f goto ds_0 endm ;; ;; motion, wait for motion to begin? ;; motion ; wait for motion ms d'50' bcf port,b_led ; blink led ms d'150' motion_1 ms d'10' ; heat time base call heat ; abort on overheat call adjust btfss port,b_go ; wait for motion to begin goto motion_1 ms d'10' ; small delay for debounce btfss port,b_go ; motion still active after delay? goto motion_1 bsf port,b_led ; turn led on retlw 0 align ;; ;; main ;; main movf a_left,w ; set lamps 180 degrees out of phase xorlw 0x80 movwf a_right bsf d_left,0 ; make odd bsf d_right,0 loop ; [re-]initialise i/o registers ; repeated in case of EMC movlw b'00100' if proto == 1 ; are we on 16f84? bsf STATUS,RP0 ; select register bank 1 movwf TRISA ; set port a direction register bcf STATUS,RP0 ; select register bank 0 else ; otherwise 12c509 tris GPIO ; set port direction register bcf OPTION,TOCS ; we want GP2 as output (note p15 spec) endif movlw k_warm ; turn lamps down to warming level movwf s_left movwf s_right call cool ; reset heat countdown call motion ; wait for motion movlw k_on ; turn left lamp up movwf s_left ds d'200' ds d'200' ds d'200' ds d'200' ds d'200' ds d'200' call cool ; reset heat countdown call motion ; wait for motion movlw k_on ; turn right lamp up movwf s_right again call cool ; reset heat countdown call motion ; wait for motion goto again ; keep lamps on while motion continues end