.list
.include "tn4313def.inc"		; Definition file for Tiny4313

//  PWM parameters
.equ redMax = 0xC0				; Max PWM width for red
.equ grnMax = 0xC0				; Max PWM width for green
.equ bluMax = 0xC0				; Max PWM width for blue

.equ redMin = 0					; Min PWM width for red
.equ grnMin = 0					; Min PWM width for green
.equ bluMin = 0					; Min PWM width for blue

.equ pwmMax = redMax*3			; 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 ringPtr = GPIOR1			; Bits to track ring
.equ ring1On = GPIOR10
.equ ring2On = GPIOR11
.equ ring3On = GPIOR12
.equ ring4On = GPIOR13
.equ ring5On = GPIOR14
.equ ring6On = GPIOR15
.equ ring7On = GPIOR16
.equ ring8On = GPIOR17

.equ colorDrvPort = PORTB		; LED drivers for the three colors
.equ redDrv = PB0
.equ grnDrv = PB2
.equ bluDrv = PB1

.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)+(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 port A0 as output
	ldi		r16,(1<<ring9)
	out 	DDRA, r16
										; Set port A inputs to pull-ups if PU enabled
	ldi		r16,(1<<ring9)
	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

	sbi		colorPtr, redOn				; Set up the color tracking bits
	cbi		colorPtr, grnOn
	cbi		colorPtr, bluOn

	cbi		colorDrvPort, redDrv		; Enable red drive, turn off others
	sbi		colorDrvPort, grnDrv
	sbi		colorDrvPort, bluDrv

	sbi		portRing1, ring1			; Turn on ring 1 to start
	cbi		portRing2, ring2
	cbi		portRing3, ring3
	cbi		portRing4, ring4
	cbi		portRing5, ring5
	cbi		portRing6, ring6
	cbi		portRing7, ring7
	cbi		portRing8, ring8
	cbi		portRing9, ring9

	cbi		ringPtr, ring1On			; Set up the ring tracking bits
	sbi		ringPtr, ring2On
	cbi		ringPtr, ring3On
	cbi		ringPtr, ring4On
	cbi		ringPtr, ring5On
	cbi		ringPtr, ring6On
	cbi		ringPtr, ring7On
	cbi		ringPtr, ring8On
;
; End of initialization
;
	ldi		r16, (1<<SE)				; Allow SLEEP mode = Idle
	out		MCUCR, r16
	sei									; Enable interrupts

/*
		Core program: (currently does nothing)
*/
Main:
;
;	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
/*
	This test turns on each ring in sequence and then checks each color in that ring
*/
	in		r1, colorPtr				; Check current color setting
	bst		r1, redOn
	brtc	sendGrn
sendRed:
	lds		r16, redPWM					; Put the PWM value in the timer reg
	out		OCR1BH, r0					; set PWM value in Timer 1
	out		OCR1BL, r16
	dec		r16
	sts		redPWM, r16
	brne	skipRed
	ldi		r17, grnMax
	sts		grnPWM, r17
	cbi		colorPtr,redOn
	sbi		colorPtr,grnOn
	sbi		colorDrvPort, redDrv		; Turn off red / turn on green
	cbi		colorDrvPort, grnDrv
	sbi		colorDrvPort, bluDrv
skipRed:
	rjmp	updateComplete
;
sendGrn:
	bst		r1, grnOn
	brtc	sendBlu
	lds		r16, grnPWM					; Put the PWM value in the timer reg
	out		OCR1BH, r0					; set PWM value in Timer 1
	out		OCR1BL, r16
	dec		r16
	sts		grnPWM, r16
	brne	skipGrn
	ldi		r17, bluMax
	sts		bluPWM, r17
	cbi		colorPtr,grnOn
	sbi		colorPtr,bluOn
	sbi		colorDrvPort, redDrv		; Turn off green / turn on blue
	sbi		colorDrvPort, grnDrv
	cbi		colorDrvPort, bluDrv
skipGrn:
	rjmp	updateComplete
;
sendBlu:
	lds		r16, bluPWM					; Put the PWM value in the timer reg
	out		OCR1BH, r0					; set PWM value in Timer 1
	out		OCR1BL, r16
	dec		r16
	sts		bluPWM, r16
	brne	updateComplete
	ldi		r17, redMax
	sts		redPWM, r17
	cbi		colorPtr,bluOn
	sbi		colorPtr,redOn
	sbi		colorDrvPort, bluDrv	; Turn off blue / turn on red
	sbi		colorDrvPort, grnDrv
	cbi		colorDrvPort, redDrv
updateRing:
	cbi		portRing1, ring1		; Turn all rings off
	cbi		portRing2, ring2
	cbi		portRing3, ring3
	cbi		portRing4, ring4
	cbi		portRing5, ring5
	cbi		portRing6, ring6
	cbi		portRing7, ring7
	cbi		portRing8, ring8
	cbi		portRing9, ring9
	in		r19, ringPtr			; Get the current bit mask
	sbrc	r19, ring1On			; Turn ring corresponding to bitthat is set on
	sbi		portRing1, ring1
	sbrc	r19, ring2On
	sbi		portRing2, ring2
	sbrc	r19, ring3On
	sbi		portRing3, ring3
	sbrc	r19, ring4On
	sbi		portRing4, ring4
	sbrc	r19, ring5On
	sbi		portRing5, ring5
	sbrc	r19, ring6On
	sbi		portRing6, ring6
	sbrc	r19, ring7On
	sbi		portRing7, ring7
	sbrc	r19, ring8On
	sbi		portRing8, ring8
	clc								; Rotate ring bits one place
	tst		r19						; This handles 9th ring with no bit
	brne	nextRing
	sbi		portRing9, ring9
	sec								; Set carry to rotate in for ninth bit
nextRing:
	rol		r19
	out		ringPtr, r19
	rjmp	updateComplete
;
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
