 TITLE  "lm_mega.asm"           ; controller for LitterMaid Mega
 LIST P=16F684, R=DEC
 INCLUDE "p16f684.inc"
 ERRORLEVEL -302         		; no bank warnings

 __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

#define END_sw	PORTA,4			; the END switch
#define HOME_sw PORTC,4			; the HOME switch
#define LED		3				; the status LED
#define IR_sens PORTC,2			; infrared sensor input	
#define FORW	2				; motor forwards pin @ PORTA
#define REW		5				; motor reverse  pin @ PORTA
#define STUCK	PORTC,0			; rake stuck sensor
#define	BATT	PORTC,1			; low battery sensor
#define ATTEMPT 3				; # of attempts to clean the litter box
TIMEOUT EQU		10*60/2			; 10-minutes timeout after pet used the device
	
 CBLOCK 0xF0					; data segment in common area
 w_save, stat_save, del:2
 petIn							; pet presence indicator
 counter:2						; time interval counter
 attempts						; attempts counter to clean the litter box
 debounce						; for buttons debouncing
 ENDC

 ORG 0							; code segment                  
	goto main

 ORG 4							; TMR1 ISR routine
	movwf	w_save				; save Wreg
	swapf	STATUS, w			; save stat reg without
	movwf	stat_save			; changing flags

	call	check4Pet			; check for pet
	incf	counter, f			; increment waiting counter
	btfsc	STATUS, Z			; (16-bit walue)
	 incf	counter+1, f

ISR_end
	bcf		PIR1, TMR1IF		; clear TMR1 interrupt flag
	swapf	stat_save, w		; get original flags in STATUS
	movwf	STATUS
	swapf	w_save, f			; restore Wreg		
	swapf	w_save, w
	retfie

main
;################# STATE 0: initial state after power-up
	clrf	PORTA				; when the PORTA will be set up
	clrf	PORTC	
	movlw	0x07
	movwf	CMCON0				; comparators OFF

  	bsf    	STATUS, RP0      	; switch to BANK 1	
	clrf	ANSEL ^ 0x80		; all inputs digital

	movlw	b'011000'			; motors off
	movwf	TRISA ^ 0x80		; configure PORTA for input
	movlw	b'011111'			; enable IR lED
  	movwf   TRISC ^ 0x80      	; enable RC5 for intput

	movlw	b'01000001'
	movwf	OSCCON ^ 0x80		; oscillator freq = 1 MHZ 
	clrf 	ADCON1 ^ 0x80 		; ADC clock = FOSC/2
	movlw	24
	movwf	PR2 ^ 0x80			; PWM period 10KHz
	bsf		PIE1, TMR1IE		; enable TMR1 interrupt

  	bcf    	STATUS, RP0        	; back to BANK 0
	clrf	INTCON				; enable periferal interrupts
	bsf		INTCON, PEIE		; (needed for TMR1)
	movlw	b'00110101'			; enable TMR1 oscillator with 1:8 prescaler	
	movwf	T1CON				; it takes 2 sec for TMR1 to overload

	movlw   b'10001000' 		; ADC setup: right justify,
	movwf 	ADCON0 				;   Vref=Vdd, AN2, On

	movlw	4					; enable Timer 2 with 1:1 prescaler
	movwf	T2CON			
	movlw	0x20				; disable single output PWM
	movwf	CCP1CON 			; set 2 lower order bits of duty cycle 
	movlw	0x0C
	movwf	CCPR1L				; setup PWM duty period 50%
	movlw	0x85				; load PORTA address
	movwf	FSR

	clrf	attempts
	goto	moveForwards

;################# STATE 1: waiting for pet
wait4Pet						; wait for pet
	clrf	petIn
	bcf		PIR1, TMR1IF		; clear TMR1 interrupt flag
	bsf		INTCON, GIE			; enable interrupts globally	

waitLoop
	btfsc	BATT				; check for battery voltage
	 goto	malfunction	
	movf	petIn, w			; is pet in?
	btfsc	STATUS, Z			
	 goto	waitLoop			; NO - keep waiting

	clrf	counter				; wait for 10 seconds to check for pet
	clrf	counter+1			; presence again
	movlw	5					; this eliminates short IR ray interruptions
	subwf	counter, w			; caused by whatever reasons
	btfss	STATUS, Z
	 goto	$-3

	movf	petIn, w			; is pet still in?
	btfsc	STATUS, Z			
	 goto	waitLoop			; No - ignore the previous check and keep waiting

	clrf	counter				; YES - wait for TIMEOUT letting the pet
	clrf	counter+1			; do its job
wait4Timeout
	btfsc	BATT				; check for battery voltage
	 goto	malfunction	
	movlw	LOW TIMEOUT
	subwf	counter, w
	btfss	STATUS, Z
	 goto	wait4Timeout
	movlw	HIGH TIMEOUT
	subwf	counter+1, w
	btfss	STATUS, Z
	 goto	wait4Timeout

wait4Leave
	movf	petIn, w
	btfsc	STATUS, Z			; is pet still in?
	 goto	startCleaning		; NO - start cleanup  
	movlw	LOW	60000			
	movwf	del					; YES - wait another minute for pet
	movlw	HIGH 60000			; to leave and check again
	movwf	del+1
	call	delay
	goto	wait4Leave

startCleaning
	clrf	attempts			; clear the attempts counter
 	goto	moveForwards		; at the beginning of cleaning

;################# STATE 2: move rake forwards to clean the litter box
moveForwards					; move rake towards the litter can
	bcf		INTCON, GIE			; disable interrupts globally
	incf	attempts, f			; increment attempts counter
	btfsc	END_sw				; is rake at its end position?
	 goto	moveBackwards		; YES - move backwards

	bcf		INDF, REW			; make sure that the rake is not moving back
	bsf		INDF, FORW			; move rake forwards
	clrf	debounce
	decf	debounce, f			; Z=0 is guaranteed
	bcf		debounce, 7
forwLoop
	btfsc 	END_sw				; check if the end position is reached
	 decf	debounce, f
    btfsc	STATUS, Z
	 goto 	rakeAtEnd
	btfsc	BATT				; check for battery voltage
	 goto	malfunction	
	btfss	STUCK				; check if rake is stuck
	 goto	forwLoop			; all OK - keep going
	bcf		INDF, FORW			; stop the rake
	call	checkAttempt		; if the function returns, it was not the last one
	movlw	LOW 1000			; make another attempt after 1 sec delay
	movwf	del
	movlw	HIGH 1000
	movwf	del+1
	call	delay
	goto	moveBackwards

rakeAtEnd
	bcf		INDF, FORW			; stop the rake
	movlw	LOW 3000			; delay at the rake end position
	movwf	del					; to let the stuff drop into the cab
	movlw	HIGH 3000
	movwf	del+1
	call	delay
	clrf	attempts			; no more attempts are needed
	goto	moveBackwards		; move the rake back home

;################# STATE 3: move rake to home position
moveBackwards					; return rake to home position
	bcf		INTCON, GIE			; disable interrupts globally
	bcf		INDF, FORW			; make sure that the rake does not move forwards
	bsf		INDF, REW			; move rake home
	clrf	debounce
	decf	debounce, f			; Z=0 is guaranteed
	bcf		debounce, 7
backLoop
	btfsc	HOME_sw				; chek if home position is reached
	 decf	debounce, f
	btfsc	STATUS, Z
	 goto	rakeAtHome
	btfsc	BATT				; check for battery voltage
	 goto	malfunction	
	btfss	STUCK				; check if rake is stuck
	 goto	backLoop			; all OK - keep going
	goto	malfunction			; stop service if rake is stuck by going backwards

rakeAtHome
	bcf		INDF, REW			; stop the rake
	movf	attempts, w			; attempts counter is non-zero if rake is stuck
	btfsc	STATUS, Z			; in this case we give it another try
	 goto	wait4Pet

	movlw	LOW 1000			; make another attempt after 1 sec delay
	movwf	del
	movlw	HIGH 1000
	movwf	del+1
	call	delay
 	goto	moveForwards

;################# STATE 4: final state - blink the LED and wait for manual reset
malfunction
	bcf		INTCON, GIE			; disable interrupts globally	
	bcf		INDF, REW			; make sure that the rake is not moving
	bcf		INDF, FORW	
	movlw	0x87				; setup for blinking the LED
	movwf	FSR					; FSR points to TRISC

	movlw	1 << LED
	xorwf	INDF, f				; start blinking LED
	movlw	LOW 500
	movwf	del					; THIS IS A FINAL STATE
	movlw	HIGH 500			; EXIT FORM IT IS ONLY POSSIBLE
	movwf	del+1				; BY RESETTING THE DEVICE
	call	delay				; BY TURNING IT OFF AND ON 
	goto	$-7

;;;;;;;;;;;;;;;;;;;PROCEDURES
check4Pet						; check for pet presence and setup petIn variable
	movlw	0x0C
	iorwf	CCP1CON, f			; enable IR transmitter
	movlw	1					; timeout in milliseconds
	movwf	del
	clrf	del+1
	call	delay				; short delay for the sensor to catch up

	clrf	petIn				; petIn = 1 if pet is in
	btfss	IR_sens				; and 0 otherwise
 	 incf 	petIn, f

	movlw	0xF0
	andwf	CCP1CON, f			; disable IR transmitter
	return

checkAttempt					; this function checks if the current attempt
	movf	attempts, w			; number to clear the litter box exceeds
	sublw	ATTEMPT				; the max number of attempts
	btfss	STATUS, C			; if this is not the case, the function returns
	 goto	malfunction			; otherwise, the device does to the final state
	return

delay							; a delay for del milliseconds
	movlw 	50					; delay duration is passed in del (2 bytes)
	
	sublw	1					; this loop takes 20us*50 = 1ms
	sublw	0					; for PIC @ 1 Mhz
	btfss	STATUS, Z
	goto 	$-3

	movf	del, w				; decrement delay counter
	btfsc	STATUS, Z
	 decf	del+1, f
	decf	del, f
	
	movf	del, w				; del=0?
	iorwf	del+1, w
	btfss	STATUS, Z
	 goto 	delay
	return
 END
