.list
.include "tn4313def.inc"		; Definition file for Tiny4313

//  PWM parameters
.equ redMax = 156				; Max PWM width for red
.equ grnMax = 156				; Max PWM width for green
.equ bluMax = 156				; Max PWM width for blue
.equ colorSwath = 156
;.equ redMax = 64				; Max PWM width for red
;.equ grnMax = 64				; Max PWM width for green
;.equ bluMax = 254				; Max PWM width for blue
;.equ colorSwath = 64
.equ redMin = redMax-colorSwath		; Min PWM width for red
.equ grnMin = grnMax-colorSwath		; Min PWM width for green
.equ bluMin = bluMax-colorSwath		; Min PWM width for blue
.equ pwmMax = 0x01FF			; Max count for PWM timer

.equ redDelta = 1				; Increment / decrement rates by color
.equ grnDelta = 1
.equ bluDelta = 1

.equ redTstH = redMax - redDelta	; Test values before hitting max
.equ grnTstH = grnMax - grnDelta
.equ bluTstH = bluMax - bluDelta
.equ redTstL = redMin + redDelta	; Test values before hitting min
.equ grnTstL = grnMin + grnDelta
.equ bluTstL = bluMin + bluDelta

.equ colorPtr = GPIOR0			; Bits to track which color is enabled
.equ redOn = GPIOR00
.equ grnOn = GPIOR01
.equ bluOn = GPIOR02

.equ colorDrvPort = PORTB		; LED drivers for the three colors
.equ redDrv = PB0
.equ bluDrv = PB1
.equ grnDrv = PB2

.equ PWM = PB4					; Enable color drive FETs (OC1B)
.equ PWMPort = PORTB

.equ ring1 = PD4				; Driver pins for rings, starting with inner ring as 1
.equ portRing1 = PORTD
.equ ring2 = PD0
.equ portRing2 = PORTD
.equ ring3 = PD5
.equ portRing3 = PORTD
.equ ring4 = PD1
.equ portRing4 = PORTD
.equ ring5 = PD6
.equ portRing5 = PORTD
.equ ring6 = PD2
.equ portRing6 = PORTD
.equ ring7 = PB3
.equ portRing7 = PORTB
.equ ring8 = PD3
.equ portRing8 = PORTD
.equ ring9 = PA0
.equ portRing9 = PORTA

.cseg
.org 0x0000						; Starts at location 0000
	rjmp	RESETisr			; Reset
	rjmp	INT0isr				; External Interrupt 0
	rjmp	INT1isr				; External Interrupt 1
	rjmp	TIMER1_CAPTisr		; Timer1 Capture Handler
	rjmp	TIMER1_COMPAisr		; Timer/Counter1 Compare Match A
	rjmp	TIMER1_OVFisr		; Timer/Counter1 Overflow
	rjmp	TIMER0_OVFisr		; Timer/Counter0 Overflow
	rjmp	USART0_RXisr		; USART0, Rx complete
	rjmp	USART0_UDREisr		; USART0, Data register empty
	rjmp	USART0_TXisr		; USART0, Tx complete
	rjmp	ANALOG_COMPisr		; Analog comparator
	rjmp	PCINT0isr			; Pin change Interrupt Request 0
	rjmp	TIMER1_COMPBisr		; Timer/Counter1 Compare Match B
	rjmp	TIMER0_COMPAisr		; Timer/Counter0 Compare Match A
	rjmp	TIMER0_COMPBisr		; Timer/Counter0 Compare Match B
	rjmp	USI_STRisr			; USI START
	rjmp	USI_OVFisr			; USI Overflow
	rjmp	EE_RDYisr			; EEPROM Ready
	rjmp	WDT_OVFisr			; Watchdog Timer Overfow
	rjmp	PCINT1isr			; Pin change Interrupt Request 1
	rjmp	PCINT2isr			; Pin change Interrupt Request 2

RESETisr:
/*		
		Initialization Section
*/
	clr		r0							; Set up a register with zero value

/*		Note clock default is 8 MHz, 14*CK+64ms start-up, CK/8
			=> System Clock = 1 MHz
*/
	
	ldi		r16, (1<<CLKPCE)				; Set for 8 MHz
	ldi		r17, (1<<CLKPS0)				; Divide by 2
;	ldi		r17, (1<<CLKPS0)+(1<<CLKPS1)	; Divide by 8
	out		CLKPR, r16
	out		CLKPR, r0
;
	ldi		r16, high(RAMEND)			; Main program start
	out		SPH,r16						; Set Stack Pointer to top of RAM
	ldi		r16, low(RAMEND)
	out		SPL,r16							
;
; Turn off unused features
										; Shut down USART and USI to save power
	ldi		r16,(1<<PRUSART)+(1<<PRUSI)+(1<<PRTIM0)
	out 	PRR, r16
;
; Set up digital outputs
										; Set pin A0 as output
	ldi		r16,(1<<ring9)
	out 	DDRA, r16
										; Set port A inputs to pull-ups if PU enabled
	ldi		r16,0x7
	out		PortA, r16
;
										; Set port B as output pins
	ldi		r16,(1<<redDrv)+(1<<grnDrv)+(1<<bluDrv)+(1<<ring7)+(1<<PWM)
	out 	DDRB, r16
										; Set port B output states
	ldi 	r16, 0x00
	out		PortB, r16
;
										; Set port D as output pins
	ldi		r16,(1<<ring1)+(1<<ring2)+(1<<ring3)+(1<<ring4)+(1<<ring5)+(1<<ring6)+(1<<ring8)
	out 	DDRD, r16
										; Set port D as outputs
	ldi 	r16,0x00
	out		PortD, r16
;
; Setup Timer 0 (8 bit)
;
; No config = timer 0 stopped
;
; Setup Timer 1 (16 bit)
	ldi		r16, (1<<COM1B1)+(1<<WGM11)+(1<<WGM10)	; OC1B on, Fast PWM, TOP in OCR1A
	out		TCCR1A, r16

;	ldi		r16,(1<<WGM12)+(1<<WGM13)+(1<<CS12)	; Fast PWM, TOP in OCR1A, CLK/256
	ldi		r16,(1<<WGM12)+(1<<WGM13)+(1<<CS11)+(1<<CS10)	; Fast PWM, TOP in OCR1A, CLK/64
;	ldi		r16,(1<<WGM12)+(1<<WGM13)+(1<<CS11)	; Fast PWM, TOP in OCR1A, CLK/8
	out		TCCR1B, r16

	ldi		r16, pwmMax/256				; Set T1 compare reg A to 0x0100 (? Hz)
	ldi		r17, pwmMax&0x00FF
	out		OCR1AH, r16
	out		OCR1AL, r17

; Common Timer Settings
	ldi		r16,(1<<TOIE1)				; Enable Timer 1 overflow interrupt
	out		TIMSK, r16

	ldi		r16, (1<<PSR10)				; Reset the prescaler, start timer
	out		GTCCR, r16
;
;	Initialize variables for colors
;

	ldi		r16, redMin					; Start with red at minimum value
	sts		redPWM, r16
	out		OCR1BH, r0					; set first PWM value in Timer 1
	out		OCR1BL, r16
	ldi		r16, redDelta				; and positive slope
	sts		redIncr, r16
	ldi		r16, grnMin					; Start with green at minimum value
	sts		grnPWM, r16
	sts		grnIncr, r0					; and zero slope
	ldi		r16, bluMax					; Start with blue at maximum value
	sts		bluPWM, r16
	ldi		r16, -bluDelta				; and negative slope
	sts		bluIncr, r16

	sbi		colorPtr, redOn				; Enable red drive, turn off others
	cbi		colorPtr, grnOn
	cbi		colorPtr, bluOn
;
; End of initialization
;
	ldi		r16, (1<<SE)				; Allow SLEEP mode = Idle
	out		MCUCR, r16
	sei									; Enable interrupts

/*
		Core program: (currently does nothing)
*/
Main:
;	Turn on all of the rings
	SBI		portRing1, ring1
	SBI		portRing2, ring2
	SBI		portRing3, ring3
	SBI		portRing4, ring4
	SBI		portRing5, ring5
	SBI		portRing6, ring6
	SBI		portRing7, ring7
	SBI		portRing8, ring8
	SBI		portRing9, ring9
;
;	Wait for an interrupt
	sleep								; Main routine would go here
	rjmp 	Main						; Re-run loop
/*
		Interrupt Service Routines
*/
INT0isr:								; External Interrupt 0
	reti
INT1isr:								; External Interrupt 1
	reti
PCINT0isr:								; Pin change Interrupt Request 0
	reti
PCINT1isr:								; Pin change Interrupt Request 1
	reti
PCINT2isr:								; Pin change Interrupt Request 2
	reti
TIMER0_COMPAisr:						; Timer/Counter0 Compare Match A
	reti
TIMER0_COMPBisr:						; Timer/Counter0 Compare Match B
	reti
TIMER0_OVFisr:							; Timer/Counter0 Overflow
	reti
TIMER1_COMPAisr:						; Timer/Counter1 Compare Match A
	reti
TIMER1_COMPBisr:						; Timer/Counter1 Compare Match B
	reti
TIMER1_OVFisr:							; Timer/Counter1 Overflow
; state machine
; -n = color reducing by n
; 0 = color steady
; +n = color increasing by n
; *** Update states
	in		r1, colorPtr				; Check current color setting
	bst		r1, redOn
	brtc	sendGrn
sendRed:
	sbi		colorDrvPort, redDrv		; Load red, but blue is in timer
	sbi		colorDrvPort, grnDrv
	cbi		colorDrvPort, bluDrv
	cbi		colorPtr, redOn				; Green is next color
	sbi		colorPtr, grnOn
	lds		r16, redPWM					; Put the PWM value in the timer reg
	out		OCR1BH, r0					; set PWM value in Timer 1
	out		OCR1BL, r16
	lds		r17, redIncr				; Get increment and test sign
	tst		r17
	brmi	redDown						; If it's negative, check lower bound
redUp:
	cpi		r16, redTstH				; else test higher bound
	brlo	updateRedPWM				; If below test value, process it
	ldi		r17, -redDelta				; else, change direction
	sts		redIncr, r17
	ldi		r17, grnDelta				; and green starts +
	sts		grnIncr, r17
	ldi		r17, grnMin
	sts		grnPWM, r17
	sts		bluIncr, r0					; and blue is flat
	ldi		r17, bluMin
	sts		bluPWM, r17
	ldi		r16, redMax
	rjmp	storeRed
redDown:
	breq	skipRed						; If flat, no update
	cpi		r16, redTstL				; test lower bound
	brsh	updateRedPWM				; If above, process it
	sts		redIncr, r0					; else red is flat at min value
	ldi		r17, -grnDelta				; and green starts -
	sts		grnIncr, r17
	ldi		r17, grnMax
	sts		grnPWM, r17
	ldi		r17, bluDelta
	sts		bluIncr, r17				; and blue starts +
	ldi		r17, bluMin
	sts		bluPWM, r17
	ldi		r16, redMin					; set red to min value
	rjmp	storeRed
updateRedPWM:
	add		r16, r17					; Update the red PWM value
storeRed:
	sts		redPWM, r16
skipRed:
	rjmp	updateComplete
;
sendGrn:
	bst		r1, grnOn
	brtc	sendBlu
	cbi		colorDrvPort, redDrv		; Load green PWM, but red is in timer
	sbi		colorDrvPort, grnDrv
	sbi		colorDrvPort, bluDrv
	cbi		colorPtr, grnOn				; Blue is next color
	sbi		colorPtr, bluOn
	lds		r16, grnPWM					; Put the PWM value in the timer reg
	out		OCR1BH, r0					; set PWM value in Timer 1
	out		OCR1BL, r16
	lds		r17, grnIncr				; Get increment and test sign
	tst		r17
	brmi	grnDown						; If it's negative, check lower bound
grnUp:
	cpi		r16, grnTstH				; else test higher bound
	brlo	updateGrnPWM				; If below test value, process it
	ldi		r17, -grnDelta				; else, change direction
	sts		grnIncr, r17
	ldi		r17, bluDelta				; and blue starts +
	sts		bluIncr, r17
	ldi		r17, bluMin
	sts		bluPWM, r17
	sts		redIncr, r0					; and red is flat
	ldi		r17, redMin
	sts		redPWM, r17
	ldi		r16, grnMax
	rjmp	storeGrn
grnDown:
	breq	skipGrn						; If flat, no update
	cpi		r16, grnTstL				; test lower bound
	brsh	updateGrnPWM				; If above, process it
	sts		grnIncr, r0 				; else green is flat at min value
	ldi		r17, -bluDelta				; and blue starts -
	sts		bluIncr, r17
	ldi		r17, bluMax
	sts		bluPWM, r17
	ldi		r17, redDelta				; and red starts +
	sts		redIncr, r17
	ldi		r17, redMin
	sts		redPWM, r17
	ldi		r16, grnMin					; set green to min value
	rjmp	storeGrn
updateGrnPWM:
	add		r16, r17					; Update the red PWM value
storeGrn:
	sts		grnPWM, r16
skipGrn:
	rjmp	updateComplete
;
sendBlu:
	sbi		colorDrvPort, redDrv		; Load blue PWM, but green is in timer
	cbi		colorDrvPort, grnDrv
	sbi		colorDrvPort, bluDrv		; Turn off red / turn on green driver
	cbi		colorPtr, bluOn				; Red is next color
	sbi		colorPtr, redOn
	lds		r16, bluPWM					; Put the PWM value in the timer reg
	out		OCR1BH, r0					; set PWM value in Timer 1
	out		OCR1BL, r16
	lds		r17, bluIncr				; Get increment and test sign
	tst		r17
	brmi	bluDown						; If it's negative, check lower bound
bluUp:
	cpi		r16, bluTstH				; else test higher bound
	brlo	updateBluPWM				; If below test value, process it
	ldi		r17, -bluDelta				; else, change direction
	sts		bluIncr, r17
	ldi		r17, redDelta				; and red starts +
	sts		redIncr, r17
	ldi		r17, redMin
	sts		redPWM, r17
	sts		grnIncr,r0					; and green is flat
	ldi		r17, grnMin
	sts		grnPWM, r17
	ldi		r16, bluMax
	rjmp	storeBlu
bluDown:
	breq	skipBlu						; If flat, no update
	cpi		r16, bluTstL				; test lower bound
	brsh	updateBluPWM				; If above, process it
	sts		bluIncr,r0					; else blue is flat at min value
	ldi		r17, -redDelta				; and red starts -
	sts		redIncr, r17
	ldi		r17, redMax
	sts		redPWM, r17
	ldi		r17, grnDelta				; and green starts +
	sts		grnIncr, r17
	ldi		r17, grnMin
	sts		grnPWM, r17
	ldi		r16, bluMin					; set blue to min value
	rjmp	storeBlu
updateBluPWM:
	add		r16, r17					; Update the blue PWM value
storeBlu:
	sts		bluPWM, r16
skipBlu:
	rjmp	updateComplete
;
; Get red state
; if red increasing and max reached, change to falling
; else if red falling and min reached, change to steady
; Get green state
; if red increasing, then green steady
; else if red falling, then green increasing
; else if red steady, then green falling
; Get blue state
; if red increasing, then blue falling
; else if red falling, then blue steady
; else if red steady, then blue increasing
; *** Update counter values
; If red on, then turn red off and turn green on
;   calc blue value:
;     load blue value
;     load blue change value
;     add blue change value
;     store blue value
;   and load into timer compare (buffered, blue next)
; else if green on, then turn green off and turn blue on
;   calc red value 
;   and load into timer compare
; else if blue on, then turn blue off and turn red on
;   calc green value 
;   and load into timer compare
; else turn red on.
;
updateComplete:
	reti
TIMER1_CAPTisr:							; Timer/COunter1 Capture
	reti
WDT_OVFisr:								; Watchdog Time-Out
	reti
USI_STRisr:								; USI Start
	reti
USI_OVFisr:								; USI Overflow
	reti
USART0_RXisr:							; USART0, Rx complete
	reti
USART0_TXisr:							; USART0, Tx complete
	reti
USART0_UDREisr:							; USART0, Data register empty
	reti
EE_RDYisr:								; EEPROM Ready
	reti
ANALOG_COMPisr:							; Analog comparator
	reti

.dseg							// Static RAM Area
.org		SRAM_START			; Skip over the memory-mapped registers
redIncr:	.byte 2				; Direction and size of changes
grnIncr:	.byte 2
bluIncr:	.byte 2
redPWM:		.byte 2				; Current PWM values
grnPWM:		.byte 2
bluPWM:		.byte 2

.eseg							// EEPROM Area
