;**********************************************************************
; Moodlight.asm
; Simple Moodlight used for illuminating a laser engraved Crystal
; Version 0.5
;
; generates 3 individual PWM-Signals on GP0..GP2 to drive 
; different coloured LEDs
; colour changes are random 
;
; Usage of Pins:
; GP0 / Pin 7: Driver Blue
; GP1 / Pin 6: Driver Green
; GP2 / Pin 5: Driver Red
; GP5 / Pin 2: Mode Switch
;
; Assuming INTOSC f=4MHz
;-------------------------------
;Stephan Asmus, Germany
;(c) stephan.asmus@t-online.de
;
;8/2008
;for Philip
;-------------------------------
; 21.08.2008	Initial Version
; 22.08.2008	Improved Timing
; 23.08.2008	Switch for setting all Drivers full power (non random)
; 24.08.2008	PWM changed to 64 Steps
; 29.12.2009	PWM Changed to 128 Steps, using Pullup on Pin 2 now
;**********************************************************************
	LIST   P=PIC12F683

        #include <p12f683.inc>        ; processor specific variable definitions
        
        __CONFIG _CP_OFF & _WDT_OFF & _BOD_OFF & _PWRTE_ON & _INTOSCIO & _CPD_OFF & _MCLRE_OFF


;***** VARIABLE DEFINITIONS
w_temp          EQU     0x20    ; used for context saving Register "W"
status_temp     EQU     0x21    ; used for context saving Register "Status"
        cblock 0x22             ; define a series of variables.
		LevelB		; Level of Blue Driver (0..128)
		LevelG		; Level of Green Driver (0..128)
		LevelR		; Level of Red Driver (0..128)
		DirB		; Direction: 0:don't change 1: increase Level 
					; 2:decrease Level
		DirG		;
		DirR		;
		Cycle		; Position within PWM-Cycle (0..maxlevel)
		Colchangel	; counter for colour change Lowbyte
		Colchangeh	; and highbyte
		Random		; Random Number Register
		Fullflag	; Flag "fullpower" 0x00=fp off 0xff=fp on
		d1			; Register to hold Delayloop	
		d2			; dto.
		
        endc
; ***********Constants definitions
IOPort		equ	GPIO	;GP-Port Register
Blue		equ	d'0'	;Bits within GP corresponding Color
Green		equ	d'1'	;
Red			equ	d'2'	;
Switch		equ	d'5'	;Bit within GP corresponding Mode-Switch
						; 1=unpressed; 0=pressed
Changeratel	equ	0xFF	;Colourchange all xxx interrupts..
Changerateh	equ 0x07	;
maxlevel	equ	0x7f	;Maximum Level of colors


; **********************************************************************
; **********************************************************************
	ORG     0x000             ; processor reset vector
	goto    init              ; go to beginning of program


	ORG     0x004           ; interrupt vector location
	movwf   w_temp          ; save off current W register contents
	swapf   STATUS,W        ; move status register into W register
	clrf    STATUS          ; select Bank 0
	movwf   status_temp     ; save off contents of STATUS register
							; There's no need to save PCLATH since
							; i'll stick to Bank 0 of program memory.

	btfsc   INTCON,T0IF     ; If Timer 0 caused the interrupt, handle it.
	call    Timer0

	swapf   status_temp,w     ; retrieve copy of STATUS register
	movwf   STATUS            ; restore pre-isr STATUS register contents
	swapf   w_temp,f
	swapf   w_temp,w          ; restore pre-isr W register contents
	retfie                    ; return from interrupt

; *********************************************
;       Timer 0 Interrupt handler.
;       Timer 0 has overflowed
; *********************************************
Timer0
	movf	Cycle,w
	btfss	STATUS,Z	;If Cycle=0 then all colors on (Begin of PWM)
	goto t1
	bsf		IOPort,Blue
	bsf		IOPort,Green
	bsf		IOPort,Red

t1	movf	Cycle,w		;clear color if necessary (this is where PWM happens)
	addlw	0x01		
	subwf	LevelB,w
	btfss	STATUS,C	;C=1 if w<=LevelB
	bcf		IOPort,Blue	
	
	movf	Cycle,w		;clear color if necessary (this is where PWM happens)
	addlw	0x01
	subwf	LevelG,w
	btfss	STATUS,C	;C=1 if w<=LevelB
	bcf		IOPort,Green	

	movf	Cycle,w		;clear color if necessary (this is where PWM happens)
	addlw	0x01
	subwf	LevelR,w
	btfss	STATUS,C	;C=1 if w<=LevelB
	bcf		IOPort,Red	
	;----------------

	incf	Cycle,f		;increment cycle-counter
	movlw	maxlevel	;we want "maxlevel" Steps only
	andwf	Cycle,f		;so mask upper unnecessary bits

	decf	Colchangel,f	;decrease Color-Change counter
	movf	Colchangel,w	
	btfss	STATUS,Z		;if lowbyte =0 then check if highbyte already 0
	goto	t2
	movf	Colchangeh,w
	btfsc	STATUS,Z
	goto	t2
	decf	Colchangeh,f	;if not then decrease it
	movlw	0xff			; and reload lowbyte 
	movwf	Colchangel		; otherwise other routine will stop cyclus
	
t2  

	movlw	0xaf			; Preload TMR0 with 175 to inc. the interrupt rate
							; und thus the PWM frequ. to above 60Hz
	movwf	TMR0
	BCF 	INTCON,T0IF		; Clear Timer0 flag and continue.
    return


; ******************************************************************************
; START OF CODE to initialize the processor (Variables, I/O ...)
; ******************************************************************************
init
	bcf     STATUS,RP0	; Select Bank 0
	bcf     STATUS,RP1	; just to be sure, unimplemented with this Processor

	movlw	0x3f		; initialise Variables
	movwf	LevelB
	movlw	0x02		; decrease Level	
	movwf	DirB

	movlw	0x00
	movwf	LevelG
	movlw	0x01		;increase Level	
	movwf	DirG

	movlw	0x0f
	movwf	LevelR
	movlw	0x01		;increase Level	
	movwf	DirR

	movlw	Changeratel	;Preload Change-Counter
	movwf	Colchangel	
	movlw	Changerateh	;Preload Change-Counter
	movwf	Colchangeh
	clrf	Cycle		;start with 0
	movlw	b'01011101'	;Preload Random Generator with any number
	movwf	Random
	clrf	Fullflag	;we dont want to start fullpower
	;------

    clrf    IOPort		; Initialize I/O-Port by clearing the output latches.

	movlw	0x07		;set GP 0:2 to digital IO
	movwf	CMCON0

	bsf		STATUS, RP0	;Switch to BANK 1
	clrf	ANSEL		;All digital I/O
	clrf	IOC			;No Interrupt on change
	movlw	b'00111000'	;0-2: Output; 3-5:Input ######################
	movwf 	TRISIO
	movlw	b'00100000'	;enable WPU on Port 5	
	movwf	WPU
	bcf	STATUS, RP0		;Back to BANK 0

; ******************************************************************************
; START OF CODE to initialize Timer 0
; Set up Timer 0 to generate interrupts every x us.  
; ******************************************************************************

	clrwdt				;Routine from Datasheet Microchip
	clrf	TMR0	
	;---
	bsf		STATUS, RP0	;Switch to BANK 1
	bsf		OPTION_REG,PSA
	clrwdt
	movlw b'01011000'	;enable Global WPU
	andwf OPTION_REG,w
	IORLW b'00000000'	;1:1
	movwf OPTION_REG

	bcf	STATUS, RP0		;Back to Bank 0
	clrf TMR0

	clrf	INTCON			;initialise Interrupts
	bsf		INTCON,T0IE		;enable Timer0 interrupts
	bsf 	INTCON,GIE		;enable global interrupts
							;now timer 0 fires
        
; ******************************************************************************
; main, here we go...
; Loop until Colorchange happens, then change colors
; ******************************************************************************

mainloop
	btfsc	IOPort,Switch	;Switch pressed? Then skip next
	goto	main1
	call	delay			;Delay for debounce of Switch
	btfss	IOPort,Switch	;Switch released?
	goto	$-1				;No, wait until then
	call	delay			;Delay for debounce
	call	toggle_fullpower	;Mode change from colorchange to fullpower
	goto	mainloop
	
	
main1
	movf	Fullflag,w		;Fullpowr on? (Fullflag<>0)
	btfss	STATUS,Z
	goto	mainloop		;yes, then dont do colorchange
	movf	Colchangel,w
	btfss	STATUS,Z		;Color Change Counter=0?
	goto	mainloop
	movf	Colchangeh,w
	btfsc	STATUS,Z
	call	change_colors	;yes, do color Change

	goto mainloop           ;

; ******************************************************************************
; Change Colors
; increase/decreases Colorlevels and changes directions if max/min
; ******************************************************************************
change_colors

	movlw	Changeratel	;Reload Change-Counter
	movwf	Colchangel	
	movlw	Changerateh	;Reload Change-Counter
	movwf	Colchangeh

	; Do it for Blue...

	movf	DirB,w		;Load direction
	btfsc	STATUS,Z	;Do nothing if 0
	goto 	chcol_B_check
	andlw	b'00000001'	;Mask Bit 1
	btfss	STATUS,Z	;Zero, then Dir=2 (decrease)
	goto	chcol_B_inc
	decf	LevelB,f
	goto 	chcol_B_check
chcol_B_inc
	incf	LevelB,f
chcol_B_check
	movf 	LevelB,w	;Check if 0 or 16
	btfss	STATUS,Z	;if 0 then direktion=1
	goto	chcol_B_Dir
	;we get here if Level was 0
	call 	rand		;this returns 0 or 1
	movwf	DirB	
	goto chcol_B_fin
chcol_B_Dir
	movlw	maxlevel
	xorwf	LevelB,w	;check if maxlevel
	btfss	STATUS,Z	;if zero then maxlevel
	goto	chcol_B_fin
	;we get here if Level was maxlevel
	call 	rand
	andlw	0xff
	btfss	STATUS,Z	;If return value=0 then save it, otherwise
	movlw	0x02		; save 2 instead
	movwf	DirB

chcol_B_fin

	; Do it for Green...

	movf	DirG,w		;Load direction
	btfsc	STATUS,Z	;Do nothing if 0
	goto 	chcol_G_check
	andlw	b'00000001'	;Mask Bit 1
	btfss	STATUS,Z	;Zero, then Dir=2 (decrease)
	goto	chcol_G_inc
	decf	LevelG,f
	goto 	chcol_G_check
chcol_G_inc
	incf	LevelG,f
chcol_G_check
	movf 	LevelG,w	;Check if 0 or 16
	btfss	STATUS,Z	;if 0 then direktion=1
	goto	chcol_G_Dir
	;we get here if Level was 0
	call 	rand		;this returns 0 or 1
	movwf	DirG	
	goto chcol_G_fin
chcol_G_Dir
	movlw	maxlevel
	xorwf	LevelG,w	;check if maxlevel
	btfss	STATUS,Z	;if zero then maxlevel
	goto	chcol_G_fin
	;we get here if Level was maxlevel
	call 	rand
	andlw	0xff
	btfss	STATUS,Z	;If return vaalue=0 then save it, otherwise
	movlw	0x02
	movwf	DirG

chcol_G_fin

	; Do it for Red...

	movf	DirR,w		;Load direction
	btfsc	STATUS,Z	;Do nothing if 0
	goto 	chcol_R_check
	andlw	b'00000001'	;Mask Bit 1
	btfss	STATUS,Z	;Zero, then Dir=2 (decrease)
	goto	chcol_R_inc
	decf	LevelR,f
	goto 	chcol_R_check
chcol_R_inc
	incf	LevelR,f
chcol_R_check
	movf 	LevelR,w	;Check if 0 or 16
	btfss	STATUS,Z	;if 0 then direktion=1
	goto	chcol_R_Dir
	;we get here if Level was 0
	call 	rand		;this returns 0 or 1
	movwf	DirR	
	goto chcol_R_fin
chcol_R_Dir
	movlw	maxlevel
	xorwf	LevelR,w	;check if maxlevel
	btfss	STATUS,Z	;if zero then maxlevel
	goto	chcol_R_fin
	;we get here if Level was maxlevel
	call 	rand
	andlw	0xff
	btfss	STATUS,Z	;If return vaalue=0 then save it, otherwise
	movlw	0x02
	movwf	DirR

chcol_R_fin

	return

; ***********************************************************************
; Rand: calculates 8-bit pseudo random number and uses this to determine 
;	an new "Direction" for the colour change (0= no change or 1=change)
;	Preload variable "RANDOM" with any non-zero value once before 1st 
;	call to this subroutine
; ***********************************************************************

rand

	RLF     Random,W
	RLF     Random,W
	BTFSC   Random,4
	XORLW   1
	BTFSC   Random,5
	XORLW   1
	BTFSC   Random,3
	XORLW   1
	MOVWF   Random		;now we have a new Random number ready to use

	movlw	d'230'		;90% possibility
	subwf	Random,w	
	btfsc	STATUS,C	;branch if w>RANDOM
	goto	rand_1
	retlw	0x00		;do not change colour (90% possibility)
rand_1	
	retlw	0x01		;do change colour (10% possibility)

; **************************************************************
; delay: just a dalay routine used for debouncing of switch
; **************************************************************
delay
	movlw	0xFF
	movwf	d1
	movlw	0x8f
	movwf	d2
DELAY_1
	decfsz d1,f
	goto $+2
	decfsz d2,f
	goto DELAY_1
	return

; **************************************************************
;toggle fullpower: Switch Full-Power Modus on or off, depending
; on previous state
; **************************************************************
toggle_fullpower

	movf	Fullflag,w
	btfss	STATUS,Z		;Was off, so turn it on
	goto	toggle_off
	movlw	0xff
	movwf	Fullflag		;set flag
	movlw	maxlevel		;full Power
	movwf	LevelB
	movwf	LevelG
	movwf	LevelR
	movlw	0x02			;and next direktion is down
	movwf	DirB
	movwf	DirG
	movwf	DirR
	return

toggle_off					;was on, so turn it off
	clrf	Fullflag
	movlw	0x1f			;half Power to indicate Switching to user
	movwf	LevelB
	movwf	LevelG
	movwf	LevelR
	
	return
; *****************************************************************
	END                       ;'end of program''
