	LIST P=PIC12F683
	INCLUDE "P12F683.INC"
	__CONFIG	_INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOD_ON & _IESO_ON & _FCMEN_ON
; Port usage
; GP0: Fake sensor output
; GP1: LM60 thermal sensor input
; GP2: PWM output
; GP3: 0: Duty control  1:Temparature control
; GP4: Increase target temparature
; GP5: Decrease target temparature

; Note
;  Vcc must be 5.0V and Vdd must be 0V for temparature measurement.

; User registers
DUTY		EQU	20H
DUTYTEMP	EQU	DUTY+1
SLOWBTNSTATE	EQU	DUTYTEMP+1
FASTBTNSTATE	EQU	SLOWBTNSTATE+1
LOOPCOUNT1	EQU	FASTBTNSTATE+1
LOOPCOUNT2	EQU	LOOPCOUNT1+1
LOOPCOUNT3	EQU	LOOPCOUNT1+2
WSTACK		EQU LOOPCOUNT3+1
STATUSSTACK	EQU	WSTACK+1
TARGETTEMP	EQU	STATUSSTACK+1
TEMPSUML	EQU TARGETTEMP+1
TEMPSUMH	EQU TARGETTEMP+2
TEMPSUMCOUNT	EQU TARGETTEMP+3

; Initial full speed loop
STARTBOOST	EQU 5				; Set 5 for 4secs

; EEPROM
EEPDUTY		EQU 00H
EEPTARGETTEMP	EQU	01H

; DUTY
PR2VALUE	EQU D'11'
DUTYMAX		EQU (PR2VALUE+1)*4	; 100%
DUTYMIN		EQU	D'12'			; 25%
DUTYSTEP_DUTY	EQU	D'4'
DUTYSTEP_TEMP	EQU	D'1'
DUTYINIT	EQU	DUTYMAX			; 100%

; TARGET Temparature (ADC)
; LM60 output voltage(mV) = 6.25T + 424
TARGETTEMP_MIN	EQU	D'112'		; 1024*(6.25*20+424)/5000 (20 celcius degree)
TARGETTEMP_MAX	EQU	D'202'		; 1024*(6.25*80+424)/5000 (90 celcius degree)
TARGETTEMP_STEP	EQU	1			; per 0.78125 cecium degree
TEMPAVERAGINGDEPTH	EQU 5
TEMPSUMCOUNTINIT	EQU 1<<TEMPAVERAGINGDEPTH

; Timer0
TM0INITCOUNT	EQU	D'100'

; Slow down button
SLOWBTNINITCOUNT	EQU	0FFH
FASTBTNINITCOUNT	EQU	0FFH
RELEASEBTNINITCOUNT	EQU	0FFH

; Start from reset
	ORG		0
	GOTO	STARTUP

; Interrupt (Timer0)
	ORG		4
	MOVWF	WSTACK				; Backup W and STATUS
	SWAPF	STATUS,W
	MOVWF	STATUSSTACK			; Inverted
	BCF		STATUS,RP0			; Bank 0
TIMER0							; Fake fan sensor output
	BTFSS	INTCON,T0IF
	GOTO	TIMER1
	BCF		INTCON,T0IF			; Clear TM0 overflow flag
	MOVLW	B'00000001'			; Invert GP0 of GPIO
	XORWF	GPIO,F
	MOVLW	TM0INITCOUNT
	MOVWF	TMR0
TIMER1							; Watch temparature and change PWM duty
	BTFSS	PIR1,TMR1IF
	GOTO	EXITINT
	BCF		PIR1,TMR1IF			; Clear TM1 overflow flag
	BSF		ADCON0,ADON			; Enable A/D Converter
	NOP
	NOP
	NOP
	NOP
	NOP
	BSF		ADCON0,GO_DONE		; Start A/D convertion
	BTFSC	ADCON0,GO_DONE
	GOTO	$-1
	BSF		STATUS,RP0			; Bank 1
	MOVF	ADRESL,W
	BCF		STATUS,RP0			; Bank 0
	ADDWF	TEMPSUML			; Cumlate ADC result
	BTFSC	STATUS,C
	INCF	TEMPSUMH,F
	MOVF	ADRESH,W
	ADDWF	TEMPSUMH
	DECFSZ	TEMPSUMCOUNT
	GOTO	TIMER1_END
	MOVLW	TEMPAVERAGINGDEPTH	; 32 times measured, get average
	MOVWF	TEMPSUMCOUNT
	RRF		TEMPSUMH
	RRF		TEMPSUML
	DECFSZ	TEMPSUMCOUNT
	GOTO	$-3
	MOVFW	TEMPSUML
	SUBWF	TARGETTEMP,W		; f-W (W=ADC, f=Target temp)
	BTFSC	STATUS,Z			; Temp is on target.  Do nothing.
	GOTO	TIMER1_END
	BTFSC	STATUS,C
	GOTO	TIMER1_LOWTEMP
TIMER1_HIGHTEMP
	CALL	RESETTEMPSUM
	CALL	SPEEDUP
	GOTO	TIMER1_END
TIMER1_LOWTEMP
	CALL	RESETTEMPSUM
	CALL	SLOWDOWN
TIMER1_END
	BCF		ADCON0,ADON			; Disable A/D Converter
EXITINT
	SWAPF	STATUSSTACK,W
	MOVWF	STATUS
	SWAPF	WSTACK,F
	SWAPF	WSTACK,W
	RETFIE

;INITIALIZE
STARTUP
	BCF		STATUS,RP0			; Bank 0
	BCF		STATUS,RP1			; Bank 0
	CLRF	INTCON				; Disable all interrupts
	CLRF	GPIO
	MOVLW	07H					; Disable comparator
	MOVWF	CMCON0
	MOVLW	04H					; Enable PR2 with prescaler 1
	MOVWF	T2CON
	MOVLW	SLOWBTNINITCOUNT	; Set slow down button state
	MOVWF	SLOWBTNSTATE
	MOVLW	FASTBTNINITCOUNT	; Set fast down button state
	MOVWF	FASTBTNSTATE
	CLRF	CCP1CON				; Disable PWM mode
	MOVLW	b'10000100'			; ADC: Right justified, REF=Vdd, Select AN1, Disable ADC
	MOVWF	ADCON0
	MOVLW	b'00110000'			; 1/8 prescale, Fosc/4 internal clock, disabled
	MOVWF	T1CON
	CALL	RESETTEMPSUM

	BSF		STATUS,RP0			; Bank 1
;	MOVLW	b'00000011'			; Enable GPIO pull up, Prescaler 1/8 for Timer0
	MOVLW	b'00000101'			; Enable GPIO pull up, Prescaler 1/8 for Timer0
	MOVWF	OPTION_REG			; Enable GPIO pull up
	MOVLW	b'01000010'			; AN1 for analog, Fosc/4
	MOVWF	ANSEL
	CLRF	PIE1				; Disable all peripheral interrupts
	MOVLW	b'01000111'			; 1MHz internal clock source
	MOVWF	OSCCON
	MOVLW	b'00111010'			; GPIO0,2 for output, the others for input
	MOVWF	TRISIO
	MOVLW	b'00110000'			; Enable pull up on GP4,5
	MOVWF	WPU
	MOVLW	PR2VALUE			; 20.8kHz PWM @ 1MHz PIC clock, 5.58bits duty resolution
	MOVWF	PR2
	BCF		EECON1,WREN			; EEPROM read only

; Start fake sensor
	BCF		STATUS,RP0			; Bank 0
	BSF		INTCON,T0IE			; Enable Timer0
	BCF		INTCON,T0IF			; Clear Timer0 overflow flag
	MOVLW	TM0INITCOUNT		; Set timer0 counter
	MOVWF	TMR0
	BSF		INTCON,GIE			; Enable interrupt

; Drive fan at duty = 100% for seconds start up
	BCF		STATUS,RP0			; Bank 0
	BSF		GPIO,GP2			; Enable GPIO2
	MOVLW	STARTBOOST			; Outer loop
	MOVWF	LOOPCOUNT3
	BTFSS	GPIO,GP3			; No initial boost for temperature control
	CALL	OUTERWAIT

; Read default target temparature from EEPROM
RESTORE_TARGETTEMP
	BSF		STATUS,RP0				; Bank 1
	MOVLW	EEPTARGETTEMP
	MOVWF	EEADR
	BSF		EECON1,RD
	MOVF	EEDAT,W
	BCF		STATUS,RP0				; Bank 0
	MOVWF	TARGETTEMP
	CALL	VALIDATE_TARGETTEMP

; Read default duty from EEPROM
RESTORE_DUTY
	BTFSS	GPIO,GP3
	GOTO	RESTORE_DUTY_EEPROM
	MOVLW	DUTYINIT
	GOTO	RESTORE_DUTY_SET
RESTORE_DUTY_EEPROM
	BSF		STATUS,RP0				; Bank 1
	MOVLW	EEPDUTY
	MOVWF	EEADR
	BSF		EECON1,RD
	MOVF	EEDAT,W
	BCF		STATUS,RP0				; Bank 0
RESTORE_DUTY_SET
	MOVWF	DUTY
	CALL	VALIDATE_DUTY

; Enable PWM
STARTPWM
;	BCF		STATUS,RP0				; Bank 0
	MOVF	CCP1CON,W				; Enable PWM mode
	ANDLW	b'11110000'
	IORLW	b'00001100'
	MOVWF	CCP1CON

START_TEMPWATCH
	BSF		T1CON,TMR1ON			; Enable timer 0

; Watch button pressed
;	BCF		STATUS,RP0				; Bank 0
WATCHMODE
	CLRWDT
	BTFSS	GPIO,GP3
	GOTO	SETMODE_DUTY
SETMODE_TEMP
	BSF		T1CON,TMR1ON
	GOTO	WATCHHIGHBTN
SETMODE_DUTY
	BCF		T1CON,TMR1ON

WATCHHIGHBTN
	MOVLW	SLOWBTNINITCOUNT
	BTFSC	GPIO,GP4
	MOVWF	SLOWBTNSTATE			; Button is not pressed.  Reset counter
	DECFSZ	SLOWBTNSTATE,F			; Decrement count
	GOTO	WATCHLOWBTN
	CALL	LOWBUTTON_PRESSED		; Button is pressed long time
	GOTO	WATCHRELEASE
WATCHLOWBTN
	MOVLW	FASTBTNINITCOUNT
	BTFSC	GPIO,GP5
	MOVWF	FASTBTNSTATE			; Button is not pressed.  Reset counter
	DECFSZ	FASTBTNSTATE,F			; Decrement count
	GOTO	WATCHMODE
	CALL	HIGHBUTTON_PRESSED		; Button is pressed long time
WATCHRELEASE
	CLRWDT
	MOVF	GPIO,W
	ANDLW	b'00110000'				; Mask other bits than GP4,5
	SUBLW	b'00110000'
	MOVLW	RELEASEBTNINITCOUNT	
	BTFSS	STATUS,Z
	MOVWF	SLOWBTNSTATE			; Button is pressed.  Reset counter
	DECFSZ	SLOWBTNSTATE,F			; Decrement count
	GOTO	WATCHRELEASE
	MOVLW	SLOWBTNINITCOUNT
	MOVWF	SLOWBTNSTATE
	MOVLW	FASTBTNINITCOUNT
	MOVWF	FASTBTNSTATE
	GOTO	WATCHMODE				; Button is released long time

LOWBUTTON_PRESSED
	BTFSC	GPIO,GP3
	GOTO	DEC_TARGETTEMP			; Temparature control mode
	GOTO	SLOWDOWN				; Duty control mode

HIGHBUTTON_PRESSED
	BTFSC	GPIO,GP3
	GOTO	INC_TARGETTEMP			; Temparature control mode
	GOTO	SPEEDUP					; Duty control mode

INC_TARGETTEMP
	MOVLW	TARGETTEMP_STEP
	ADDWF	TARGETTEMP,F			; W=DUTY+DUTYSTEP
	BTFSC	STATUS,C
	GOTO	SETMAXTARGETTEMP		; W>0FFH
	GOTO	VALIDATE_TARGETTEMP
	
DEC_TARGETTEMP
	MOVLW	TARGETTEMP_STEP
	SUBWF	TARGETTEMP,F			; W=TARGETTEMP-TARGETTEMP_STEP
	BTFSS	STATUS,C
	GOTO	SETMINTARGETTEMP		; W<0

VALIDATE_TARGETTEMP
	MOVFW	TARGETTEMP
	SUBLW	TARGETTEMP_MAX
	BTFSS	STATUS,C
	GOTO	SETMAXTARGETTEMP		; W>MAXDUTY
	MOVFW	TARGETTEMP
	SUBLW	TARGETTEMP_MIN
	BTFSC	STATUS,C
	GOTO	SETMINTARGETTEMP		; W<DUTYMIN
	GOTO	SAVE_TARGETTEMP

SETMINTARGETTEMP
	MOVLW	TARGETTEMP_MIN
	MOVWF	TARGETTEMP
	GOTO	SAVE_TARGETTEMP

SETMAXTARGETTEMP
	MOVLW	TARGETTEMP_MAX
	MOVWF	TARGETTEMP
	GOTO	SAVE_TARGETTEMP

SAVE_TARGETTEMP
	MOVFW	TARGETTEMP
	BSF		STATUS,RP0			; Bank 1
	MOVWF	EEDAT
	MOVLW	EEPTARGETTEMP
	MOVWF	EEADR
	BSF		EECON1,WREN
	BCF		INTCON,GIE			; Disable interrupts
	BTFSC	INTCON,GIE
	GOTO	$-2
	MOVLW	55H
	MOVWF	EECON2
	MOVLW	0AAH
	MOVWF	EECON2
	BSF		EECON1,WR
	BCF		EECON1,WREN
	BSF		INTCON,GIE			; Enable interrupts
	BCF		STATUS,RP0			; Bank 0
	RETURN

SLOWDOWN
	MOVLW	DUTYSTEP_DUTY
	BTFSC	GPIO,GP3
	MOVLW	DUTYSTEP_TEMP
	SUBWF	DUTY,F				; W=DUTY-DUTYSTEP
	BTFSS	STATUS,C
	GOTO	SETMINDUTY			; W<0
	GOTO	VALIDATE_DUTY

SPEEDUP
	MOVLW	DUTYSTEP_DUTY
	BTFSC	GPIO,GP3
	MOVLW	DUTYSTEP_TEMP
	ADDWF	DUTY,F				; W=DUTY+DUTYSTEP
	BTFSC	STATUS,C
	GOTO	SETMAXDUTY			; W>0FFH
;	GOTO	VALITATE_DUTY

VALIDATE_DUTY
	MOVFW	DUTY
	SUBLW	DUTYMIN
	BTFSC	STATUS,C
	GOTO	SETMINDUTY			; W<DUTYMIN
	MOVFW	DUTY
	SUBLW	DUTYMAX
	BTFSS	STATUS,C
	GOTO	SETMAXDUTY			; W>MAXDUTY
	GOTO	CHANGEDUTY

SETMINDUTY
	MOVLW	DUTYMIN
	MOVWF	DUTY
	GOTO	CHANGEDUTY
SETMAXDUTY
	MOVLW	DUTYMAX				; Max duty
	MOVWF	DUTY
	GOTO	CHANGEDUTY

CHANGEDUTY
	SWAPF	DUTY,W				; Write lower 2 bits
	ANDLW	b'00110000'
	MOVWF	DUTYTEMP
	MOVFW	CCP1CON
	ANDLW	b'11001111'
	IORWF	DUTYTEMP,W
	MOVWF	CCP1CON
	MOVFW	DUTY				; Write upper 6 bits
	MOVWF	DUTYTEMP
	RRF		DUTYTEMP,F
	RRF		DUTYTEMP,W
	ANDLW	b'00111111'
	MOVWF	CCPR1L
	BTFSC	GPIO,GP3
	RETURN						; Temp control mode.  Do not store DUTY to EEPROM.
	MOVFW	DUTY				; Duty control mode.  Store DUTY to EEPROM.
	BSF		STATUS,RP0			; Bank 1
	MOVWF	EEDAT
	MOVLW	EEPDUTY
	MOVWF	EEADR
	BSF		EECON1,WREN
	BCF		INTCON,GIE			; Disable interrupts
	BTFSC	INTCON,GIE
	GOTO	$-2
	MOVLW	55H
	MOVWF	EECON2
	MOVLW	0AAH
	MOVWF	EECON2
	BSF		EECON1,WR
	BCF		EECON1,WREN
	BSF		INTCON,GIE			; Enable interrupts
	BCF		STATUS,RP0			; Bank 0
	RETURN

OUTERWAIT
	MOVF	LOOPCOUNT3,F
	BTFSC	STATUS,Z
	RETURN
	CALL	MIDWAIT
	DECF	LOOPCOUNT3
	GOTO	OUTERWAIT

MIDWAIT
	MOVLW	0FFH
	MOVWF	LOOPCOUNT2
	CALL	INNERWAIT
	CLRWDT
	DECFSZ	LOOPCOUNT2,F
	GOTO	$-3
	RETURN

INNERWAIT
	MOVLW	0FFH
	MOVWF	LOOPCOUNT1
	DECFSZ	LOOPCOUNT1,F
	GOTO	$-1
	RETURN

RESETTEMPSUM
	CLRF	TEMPSUML
	CLRF	TEMPSUMH
	MOVLW	TEMPSUMCOUNTINIT
	MOVWF	TEMPSUMCOUNT
	RETURN

	END