; Wings.ASM -  Wing Twinkler for Halloween Wings 2011
;              Designed for LEDs so all LEDs are PWM'd ...
;              ... allows them to twinkle like an incandescent lamp
;
; Mark Csele, 2011/09/29
;
; Uses 8MHz XT oscillator crystal frequency
;
; Outputs (Twelve Total):
;   RC0-RC7 = Pwm Channels 0 through 7
;   RB0-RB3 = Pwm Channels 8 through 11
;
; LED Configuration:
;   Channels 0 - 1 - 2 left side perimiter
;   Channels 3 - 4 - 5 right side perimiter
;   Channels 6,7,8,9   twinkle leds in center
;
;;;;;;; Assembler directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        LIST    p=18F252       ;PIC18F252 is the target processor
        INCLUDE "P18F252.INC"  ;Include file with register defines

	;Programming Configuration Information

	CONFIG     CP0=OFF			;code protect disabled
	CONFIG     OSCS=OFF, OSC=HS	;Oscillator switch disabled, HS osc
	CONFIG     BOR=ON, BORV=25	;Brown-Out Reset enabled, BOR Voltage is 2.5v
	CONFIG     WDT=OFF			;Watch Dog Timer disable
	CONFIG     CCP2MUX=OFF		;CCP2 pin Mux disabled
	CONFIG     STVR=ON			;Stack over/underflow Reset enabled
	CONFIG     LVP=OFF
	CONFIG     DEBUG=ON			;Enable debugging via the ICD-2

	;Programming Configuration ENDS

;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        cblock  0x000       ;Beginning of Access RAM
		WRegTemp			;Temp storage for registers during ISR
		StatusTemp
		RampCount			;Value for ramp up/dn counter
		DelayCtr			;Counter for delay loops
		DelayCtr2
		ChaserCount			;Counter for chaser (# times to run sequence)

		PwmCount			;Counter for PWM
		PwmCh0				;PWM channel outputs (0x00=off, 0xFF=on)
		PwmCh1				;MUST be consecutive registers
		PwmCh2
		PwmCh3
		PwmCh4
		PwmCh5
		PwmCh6
		PwmCh7
		PwmCh8
		PwmCh9
		PwmCh10
		PwmCh11
		PwmCh12
		PwmCh13
		PwmCh14
		PwmCh15

        endc

;;;;;;; Vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	org  	0x0000         	;Reset vector
	nop
	nop
	goto	Main			;Jump over the ISR code

	org	0x0018				;Low Priority Interrupt vector
;;;;;;; Interrupt Service Routines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; The ISR is called when Timer0 has completed its count
; It does so by waiting for sixteen-bit Timer0 to count up and roll over
; from 0xFFFF to zero. Calculations:
;   With a clock speed of 8MHz, Fcy=2MHz
;   Prescaled by 1:1, the input to the timer is 2MHz
;   For a 16.7KHz timer we need 125 clocks to elapse
;   So, the counter must be loaded to start at 65536-125 = 65410 each time

PreLoadTimer  equ     .65410

ISR	
	movwf	WRegTemp			;Save W register
	movff	STATUS,StatusTemp	;Save STATUS register

	movlw  	high  PreLoadTimer	;Load high first, loading low will then load all 16 bits
	movwf	TMR0H
	movlw  	low  PreLoadTimer
	movwf	TMR0L

	incfsz	PwmCount,f			;Increment PWM Counter, runs continually
	goto	PwmRunning

;At zero, turn on all channels unless each is zero
	movf	PwmCh0,f
	btfsc	STATUS,Z
	goto	Ch0Zero				;Channel 0 is zero, do not turn on
	bsf		PORTC,0				;Turn ON channel 0
Ch0Zero
	movf	PwmCh1,f
	btfsc	STATUS,Z
	goto	Ch1Zero
	bsf		PORTC,1
Ch1Zero
	movf	PwmCh2,f
	btfsc	STATUS,Z
	goto	Ch2Zero
	bsf		PORTC,2
Ch2Zero
	movf	PwmCh3,f
	btfsc	STATUS,Z
	goto	Ch3Zero
	bsf		PORTC,3
Ch3Zero
	movf	PwmCh4,f
	btfsc	STATUS,Z
	goto	Ch4Zero
	bsf		PORTC,4
Ch4Zero
	movf	PwmCh5,f
	btfsc	STATUS,Z
	goto	Ch5Zero
	bsf		PORTC,5
Ch5Zero
	movf	PwmCh6,f
	btfsc	STATUS,Z
	goto	Ch6Zero
	bsf		PORTC,6
Ch6Zero
	movf	PwmCh7,f
	btfsc	STATUS,Z
	goto	Ch7Zero
	bsf		PORTC,7
Ch7Zero
	movf	PwmCh8,f
	btfsc	STATUS,Z
	goto	Ch8Zero
	bsf		PORTB,0
Ch8Zero
	movf	PwmCh9,f
	btfsc	STATUS,Z
	goto	Ch9Zero
	bsf		PORTB,1
Ch9Zero
	movf	PwmCh10,f
	btfsc	STATUS,Z
	goto	Ch10Zero
	bsf		PORTB,2
Ch10Zero
	movf	PwmCh11,f
	btfsc	STATUS,Z
	goto	Ch11Zero
	bsf		PORTB,3
Ch11Zero
	goto	TurnOnDone

PwmRunning
;When running, if channel counter = PwmCount, turn channel off
	movf	PwmCh0,w			;Check if Channel = PwmCount
	subwf	PwmCount,w	
	btfss	STATUS,C			;Carry is set if equal or GT
	goto	Ch0Off
	bcf		PORTC,0
Ch0Off
	movf	PwmCh1,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch1Off
	bcf		PORTC,1
Ch1Off
	movf	PwmCh2,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch2Off
	bcf		PORTC,2
Ch2Off
	movf	PwmCh3,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch3Off
	bcf		PORTC,3
Ch3Off
	movf	PwmCh4,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch4Off
	bcf		PORTC,4
Ch4Off
	movf	PwmCh5,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch5Off
	bcf		PORTC,5
Ch5Off
	movf	PwmCh6,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch6Off
	bcf		PORTC,6
Ch6Off
	movf	PwmCh7,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch7Off
	bcf		PORTC,7
Ch7Off
	movf	PwmCh8,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch8Off
	bcf		PORTB,0
Ch8Off
	movf	PwmCh9,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch9Off
	bcf		PORTB,1
Ch9Off
	movf	PwmCh10,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch10Off
	bcf		PORTB,2
Ch10Off
	movf	PwmCh11,w
	subwf	PwmCount,w	
	btfss	STATUS,C
	goto	Ch11Off
	bcf		PORTB,3
Ch11Off
TurnOnDone
EndIsr
	bcf  	INTCON,TMR0IF      	;Clear Timer0 flag
	movf	WRegTemp,w			;Do first as it affects STATUS flags
    movff   StatusTemp,STATUS	;  then restore original copy of STATUS flag
    retfie                		;This will re-enable GIE flag as well
;END of ISR routine


;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Main
        rcall  Initialize		;Initialize everything

Loop

;Basic Chaser Display Multi-Direction
		movlw	.10				;Chaser upwards
		movwf	ChaserCount
ChaserLoop
		call	ChaserUp
		decfsz	ChaserCount,f
		goto	ChaserLoop

; 	goto Loop

		movlw	.10				;Reverse direction
		movwf	ChaserCount
ChaserLoop2
		call	ChaserDn
		decfsz	ChaserCount,f
		goto	ChaserLoop2

		movlw	.10				;Chaser Clockwise
		movwf	ChaserCount
ChaserLoop3
		call	ChaserCW
		decfsz	ChaserCount,f
		goto	ChaserLoop3

		movlw	.10				;Chaser Counter Clockwise
		movwf	ChaserCount
ChaserLoop4
		call	ChaserCCW
		decfsz	ChaserCount,f
		goto	ChaserLoop4

;Twinkle for a while
		movlw	.20			;Twinkle Randomly
		movwf	ChaserCount
TwinkleLoop
;		call	Twinkle
		call	TwinkleWithOuter
		decfsz	ChaserCount,f
		goto	TwinkleLoop

        goto  Loop				;Nothing to do since interrupts handle it all!



;**************************
;*  High-Level Functions  *
;**************************
;* Chase perimeter LEDs Upwards (both sides), one cycle
ChaserUp
		movlw	0				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	3				;Channel 4
		call	ChannelOn
		movlw	2	
		call	ChannelOff
		movlw	5	
		call	ChannelOff
		call	ChaserDelay
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
		movlw	0	
		call	ChannelOff
		movlw	3	
		call	ChannelOff
		call	ChaserDelay
		movlw	2
		call	ChannelOn
		movlw	5
		call	ChannelOn
		movlw	1	
		call	ChannelOff
		movlw	4	
		call	ChannelOff
		call	ChaserDelay

		return

ChaserUpOldDuringTwinkle
		movlw	0				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	3				;Channel 4
		call	ChannelOn
		movlw	1				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	4				;Channel 4
		call	ChannelOn
		movlw	2	
		call	ChannelOff
		movlw	5
		call	ChannelOff
		call	ChaserDelay
		movlw	0	
		call	ChannelOff
		movlw	3	
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
		movlw	2
		call	ChannelOn
		movlw	5
		call	ChannelOn
;		movlw	0	
;		call	ChannelOff
;		movlw	3	
;		call	ChannelOff
		call	ChaserDelay
		movlw	1	
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	0
		call	ChannelOn
		movlw	3
		call	ChannelOn
		movlw	2
		call	ChannelOn
		movlw	5
		call	ChannelOn
;		movlw	2
;		call	ChannelOn
;		movlw	5
;		call	ChannelOn
;		call	ChaserDelay
;		movlw	2	
;		call	ChannelOff
;		movlw	5
;		call	ChannelOff

		return

ChaserUpOld
		movlw	0				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	3				;Channel 4
		call	ChannelOn
;		movlw	2	
;		call	ChannelOff
;		movlw	5
;		call	ChannelOff
		call	ChaserDelay
		movlw	0	
		call	ChannelOff
		movlw	3	
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
;		movlw	0	
;		call	ChannelOff
;		movlw	3	
;		call	ChannelOff
		call	ChaserDelay

;		movlw	2
;		call	ChannelOn
;		movlw	5
;		call	ChannelOn
		movlw	1	
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	2
		call	ChannelOn
		movlw	5
		call	ChannelOn
		call	ChaserDelay
		movlw	2	
		call	ChannelOff
		movlw	5
		call	ChannelOff

		return

;* Chase perimeter LEDs Downwards (both sides), one cycle
ChaserDn						;Algorithm #2
		movlw	2				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	5
		call	ChannelOn
		movlw	0	
		call	ChannelOff
		movlw	3	
		call	ChannelOff
		call	ChaserDelay
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
		movlw	2	
		call	ChannelOff
		movlw	5	
		call	ChannelOff
		call	ChaserDelay
		movlw	0
		call	ChannelOn
		movlw	3
		call	ChannelOn
		movlw	1	
		call	ChannelOff
		movlw	4	
		call	ChannelOff
		call	ChaserDelay
		return

ChaserDnOld
		movlw	2				
		call	ChannelOn		
		movlw	5				
		call	ChannelOn
		call	ChaserDelay
		movlw	2	
		call	ChannelOff
		movlw	5	
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
		call	ChaserDelay
		movlw	1	
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	0
		call	ChannelOn
		movlw	3
		call	ChannelOn
		call	ChaserDelay
		movlw	0	
		call	ChannelOff
		movlw	3
		call	ChannelOff
		return

;* Chase perimeter LEDs Clockwise (up one side, down other), one cycle
ChaserCW
		movlw	0				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	5				;Channel 4
		call	ChannelOn
		call	ChaserDelay
		movlw	0	
		call	ChannelOff
		movlw	5	
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
		call	ChaserDelay
		movlw	1	
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	2
		call	ChannelOn
		movlw	3
		call	ChannelOn
		call	ChaserDelay
		movlw	2	
		call	ChannelOff
		movlw	3
		call	ChannelOff
		return

;* Chase perimeter LEDs CounterClockwise (dn one side, up other), one cycle
ChaserCCW
		movlw	2				;Channel 0
		call	ChannelOn		;Ramps intensity of this channel up
		movlw	3				;Channel 4
		call	ChannelOn
		call	ChaserDelay
		movlw	2	
		call	ChannelOff
		movlw	3	
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	4
		call	ChannelOn
		call	ChaserDelay
		movlw	1	
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	0
		call	ChannelOn
		movlw	5
		call	ChannelOn
		call	ChaserDelay
		movlw	0	
		call	ChannelOff
		movlw	5
		call	ChannelOff
		return

Twinkle
		movlw	6
		call	ChannelOn
		call	LongDelay
		movlw	6
		call	ChannelOff
		movlw	7
		call	ChannelOn
		call	LongDelay
		movlw	7
		call	ChannelOff
		movlw	8
		call	ChannelOn
		call	LongDelay
		movlw	8
		call	ChannelOff
		movlw	6
		call	ChannelOn
		movlw	8
		call	ChannelOn
		call	LongDelay
		movlw	6
		call	ChannelOff
		movlw	7
		call	ChannelOff
		call	LongDelay
		movlw	9
		call	ChannelOn
		call	LongDelay
		movlw	8
		call	ChannelOff
		movlw	9
		call	ChannelOff
		movlw	7
		call	ChannelOn
		movlw	9
		call	ChannelOn
		call	LongDelay
		movlw	9
		call	ChannelOff
		movlw	7
		call	ChannelOff
		movlw	7
		call	ChannelOn
		call	LongDelay
		movlw	7
		call	ChannelOff
		movlw	9
		call	ChannelOn
		call	LongDelay
		movlw	9
		call	ChannelOff
		movlw	7
		call	ChannelOn
		movlw	8
		call	ChannelOn
		call	LongDelay
		movlw	7
		call	ChannelOff
		movlw	8
		call	ChannelOff
		return

TwinkleWithOuter
		movlw	6
		call	ChannelOn
		movlw	0
		call	ChannelOn
		movlw	3
		call	ChannelOn
		call	LongDelay
		movlw	3
		call	ChannelOff
		movlw	6
		call	ChannelOff
		movlw	7
		call	ChannelOn
		movlw	0
		call	ChannelOff
		movlw	3
		call	ChannelOn
		call	LongDelay		
		movlw	3
		call	ChannelOff
		movlw	7
		call	ChannelOff
		movlw	8
		call	ChannelOn
		movlw	4
		call	ChannelOn
		movlw	1
		call	ChannelOn
		call	LongDelay
		movlw	1
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	8
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	1
		call	ChannelOff
		movlw	6
		call	ChannelOn
		movlw	8
		call	ChannelOn
		movlw	0
		call	ChannelOn
		movlw	4
		call	ChannelOn
		call	LongDelay
		movlw	6
		call	ChannelOff
		movlw	0
		call	ChannelOff
		movlw	4
		call	ChannelOff
		movlw	7
		call	ChannelOff
		call	LongDelay
		movlw	4
		call	ChannelOn
		movlw	4
		call	ChannelOff
		movlw	5
		call	ChannelOn
		movlw	9
		call	ChannelOn
		movlw	2
		call	ChannelOn
		movlw	6
		call	ChannelOn
		call	LongDelay
		movlw	5
		call	ChannelOff
		movlw	4
		call	ChannelOn
		movlw	6
		call	ChannelOff
		movlw	8
		call	ChannelOff
		movlw	9
		call	ChannelOff
		movlw	2
		call	ChannelOff
		movlw	7
		call	ChannelOn
		movlw	9
		call	ChannelOn
		call	LongDelay
		movlw	4
		call	ChannelOff
		movlw	6
		call	ChannelOn
		movlw	6
		call	ChannelOff
		movlw	1
		call	ChannelOn
		movlw	9
		call	ChannelOff
		movlw	7
		call	ChannelOff
		movlw	1
		call	ChannelOff
		movlw	5
		call	ChannelOn
		movlw	7
		call	ChannelOn
		call	LongDelay
		movlw	2
		call	ChannelOn
		movlw	6
		call	ChannelOn
		movlw	5
		call	ChannelOff
		movlw	7
		call	ChannelOff
		movlw	9
		call	ChannelOn
		call	LongDelay
		movlw	9
		call	ChannelOff
		movlw	7
		call	ChannelOn
		movlw	8
		call	ChannelOn
		movlw	2
		call	ChannelOff
		movlw	6
		call	ChannelOff
		movlw	0
		call	ChannelOn
		movlw	3
		call	ChannelOn
		call	LongDelay
		movlw	7
		call	ChannelOff
		movlw	8
		call	ChannelOff
		movlw	0
		call	ChannelOff
		movlw	3
		call	ChannelOn
		return


;*************************
;*  Low-Level Functions  *
;*************************

;*** Initialize ***
; Initializes  Ports, Timer for ISR, and registers.
Initialize
        movlw  	B'11000000'
		movwf	TRISB  			;Set I/O for PORTB - RB0-5=Output
		clrf	PORTB

        movlw  	B'00000000'
		movwf	TRISC  			;Set I/O for PORTC - RC0-7=Output
		clrf	PORTC

		clrf	PwmCh0			;Clear all Pwm Outputs to OFF
		clrf	PwmCh1
		clrf	PwmCh2
		clrf	PwmCh3	
		clrf	PwmCh4
		clrf	PwmCh5
		clrf	PwmCh6
		clrf	PwmCh7
		clrf	PwmCh8
		clrf	PwmCh9
		clrf	PwmCh10
		clrf	PwmCh11
		clrf	PwmCh12
		clrf	PwmCh13
		clrf	PwmCh14
		clrf	PwmCh15

        movlw  	B'10001000'		;Timer0 Config: ON, 16-bit, IntClk, 1:1 prescaler
		movwf	T0CON

		movlw  	high  PreLoadTimer	;Load high first, loading low will then load all 16 bits
		movwf	TMR0H
		movlw  	low  PreLoadTimer
		movwf	TMR0L

		bsf		INTCON,TMR0IE	;Enable Timer 0 interrupt source
		bsf		INTCON,GIE		;Enable all interrupts (Master Enable)

        return

;*** ChannelOn ***
;Turn channel in w ON using a twinkling effect (ramp up)
;Ramps up intensity from current value to 0xFF
ChannelOn
		addlw	PwmCh0			;Calculate the register # to access
		movwf	FSR0L		

		movf	INDF0,w			;Read the current value
		movwf	RampCount		;And start at that point
RampUpLoop
		incfsz	RampCount,f
		goto	NextRampUp
		return					;Intensity is 0xFF (full), exit
NextRampUp
		movf	RampCount,w
		movwf	INDF0			;Update PWM value			
		call	ShortDelay		;Short delay for ramp rate
		goto	RampUpLoop
		return

;*** ChannelOff ***
;Turn channel in w OFF using a twinkling effect (ramp down)
;Ramps down intensity from current value to 0x00
ChannelOff
		addlw	PwmCh0			;Calculate the register # to access
		movwf	FSR0L		

		movf	INDF0,w			;Read the current value
		movwf	RampCount		;And start at that point
RampDnLoop
		movf	RampCount,f		;Check for zero
		btfsc	STATUS,Z
		return					;Intensity is 0xFF (full), exit
		decf	RampCount,f
NextRampDn
		movf	RampCount,w
		movwf	INDF0			;Update PWM value			
		call	ShortDelay		;Short delay for ramp rate
		goto	RampDnLoop
		return

;*** Short Delay ***
;Used to time Ramp Up/Dn for twinkling effect
ShortDelay
		movlw	0x20
		movwf	DelayCtr
ShortDelayLoop
		nop
		decfsz	DelayCtr,f
		goto	ShortDelayLoop
		return

;*** Long Delay ***
;Used within the program between on/off cycles
LongDelay
		movlw	0x40
		movwf	DelayCtr2
LongDelayLoop
		movlw	0x20
		movwf	DelayCtr
InnerDelayLoop
		nop
		nop
		decfsz	DelayCtr,f
		goto	InnerDelayLoop
		decfsz	DelayCtr2,f
		goto	LongDelayLoop
		return

;*** Long Delay ***
;Used within the program between chaser cycles
ChaserDelay
		movlw	0x10
		movwf	DelayCtr2
ChaserDelayLoop
		movlw	0x20
		movwf	DelayCtr
InnerChaserDelayLoop
		nop
		nop
		decfsz	DelayCtr,f
		goto	InnerChaserDelayLoop
		decfsz	DelayCtr2,f
		goto	ChaserDelayLoop
		return

        end

