$NOMOD51
$include (C8051F912.inc)      
 
SUN_THRESH	EQU 0xBB			; sun/shade threschold (~16220 lx)

SCL 		EQU P0.2			; MAX44009 I2C interface
SDA		EQU P0.3

	DSEG 	AT 0x20			; data segment in directly addressable space
cnt:		DS 2				; counter for the number of wake-ups
cnt5:		DS 1				; counter for 12-sec intervals
sun5:		DS 1				; # of sunny periods per minute
sunCnt:	DS 2				; total # of sunny minutes
minCnt:	DS 1				; minutes counter for 12 hours cycle
hrsCnt:	DS 1				; hours counter for 12 hours cycle
state:	DS 1				; 0=measurement  1=display result

	ORG 	0x30
den:		DS 3				; variables for tuning up the
num:		DS 3				; SmaRTClock period
res:		DS 2
			
	CSEG 	AT 0				; interrupt vectors
	ljmp 	Main  			; reset

	ORG	0x2B				; Timer2 ISR
	anl	TMR2CN, #0x3F		; clear Timer2 interrupt flags
	reti

	ORG	0xB3				; code section right after the int. vectors
	USING    0                    ; specify register bank 

main:       
	anl   PCA0MD, #NOT(0x40)      ; disable the WDT
	mov	SP, #0x7F			; setup stack in indirect storage area

	mov	DC0CN, #0xE7
	acall	Ports_Setup
	acall	Clock_Setup
	acall	Debug_Trap			; debug trap !!!
	acall	RTC_Setup			; initialize oscillators
	acall	DCDC_Setup
	acall	MAX44009_Setup	
	acall	Timer_Setup
	acall	Tune_RTC

;	call  MAX44009_Read
;	sjmp	$-2

	setb	P1.2
loop: mov 	PMU0CF, #CLEAR		; MAIN LOOP
	mov	PMU0CF, #(SLEEP OR RTCAWK) ; set wakeup sources
	nop   				; device is now awake
  	nop
	nop
	nop
	mov	A, PMU0CF
	mov 	PMU0CF, #CLEAR

	mov	R0, #100			; short delay to boost up the voltage		
	djnz	R0, $

	jnb	ACC.4, $+8			; RESET pin wake-up?
	mov	A, #100			; YES - make a 20 usec delay
	djnz	ACC, $

	mov	A, #0x1F			; toggle LCD pins
	xrl	P1, A
	mov	A, #0xE0			
	xrl	P0, A

	jb	state.0, loop		; skip the rest of the loop in state 1

	mov	A, cnt			; flip the seconds tick segment on LCD
	anl	A, #0x1F
	jnz	$+5
	xrl	P1, #0x04

	inc	cnt				; update the wake-up counter 
	mov	A, cnt			; (16-bit increment)
	jnz	$+4
	inc	cnt+1

	mov	C, (cnt+1).0		; 384 periods correspond to 12 sec
	rrc	A				; 384 = 0x180
	cjne	A, #0xC0, loop		; 384/2 = 0xC0 
	acall	Check_Light			; compute the light conditions
	mov	cnt, #0			; reset the counter
	mov	cnt+1, #0
	sjmp	loop			

;-------------------PROCEDURES-------------------------------------------
Ports_Setup:
	mov  	P0MDIN,  #0xFF		; digital I/O on P0
	mov  	P0MDOUT, #0xF5		; open drain on P0.3 & P0.1
	mov  	P1MDIN,  #0xFF		; digital I/O on P1
    	mov  	P1MDOUT, #0xDF		; push-pull  on P1. open drain on P1.5
	mov  	P0SKIP,  #0xFF		; I/O function on P0
    	mov  	P1SKIP,  #0xFF		; I/O function on P1
    	mov   XBR2,    #0x40		; enable XBar and weak pull-ups
	mov	P0, 	   #0x1E		; initialize port pins
	mov	P1,      #0x20			
	mov	SFRPAGE, #0x0F
	mov	P0DRV,   #0xF0
	mov	P1DRV,   #0x1F
	mov	SFRPAGE, #0
	ret

;-----------------------------------------------------------------------
Clock_Setup:
	mov	RTC0KEY, #0xA5		; unlock RTC interface
	mov	RTC0KEY, #0xF1
	mov	RTC0ADR, #(0x10 OR RTC0XCN)	
	mov	RTC0DAT, #LFOEN		; enable LFO
	nop
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; enable SmaRTClock
	mov	RTC0DAT, #(RTC0EN OR RTC0TR)  
	nop
	mov 	RTC0ADR, #(0x90 OR RTC0PIN)
	nop
	nop
	nop
	mov 	A, RTC0DAT	
	orl	A, #0x80			; short cut XTAL pins
	mov 	RTC0ADR, #(0x90 OR RTC0PIN)			
	mov	RTC0DAT, A
	nop

	mov 	RSTSRC,  #0x00		; disable MCD and VDD monitor
	anl	OSCICN,  #0x7F		; disable internal precision oscillator
	mov	OSCXCN,  #0			; disable external oscillator
	mov	REG0CN,  #0			; disable precision oscillator bias
	mov	VDM0CN,  #0			; disable VDD/DC+ supply monitor

	mov	CLKSEL,  #0x03		; select SmaRTClock with 1:1 clock divider
	ret

;-----------------------------------------------------------------------
Debug_Trap:
	mov	R0, #0x00
	mov	R1, #0x00
	djnz	R0, $				; ~10 sec delay
	djnz	R1, $-2
	jnb	P1.5, $			; debug trap
	mov	SFRPAGE, #0x0F
	mov	PMU0MD,  #0x20		; disable VBAT supply monitor
	mov	SFRPAGE, #0	 
	ret

;-----------------------------------------------------------------------
RTC_Setup:
	mov 	RTC0ADR, #(0x10 OR CAPTURE0)	; clear RTC timer
	mov 	RTC0DAT, #0				; autoincrement address
	nop	
	mov 	RTC0ADR, #(0x10 OR CAPTURE1)
	mov 	RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 OR CAPTURE2)
	mov 	RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 OR CAPTURE3)
	mov 	RTC0DAT, #0
	nop
	mov	RTC0ADR, #(0x10 OR RTC0CN)	; set timer value
	mov	RTC0DAT, #(RTC0EN OR RTC0SET OR RTC0TR) 
	nop
	mov 	RTC0ADR, #(0x90 OR RTC0CN)
	nop
	nop
	nop
	mov 	A, RTC0DAT			
	jb	ACC.1, $-8				; verify that timer is set

	mov	res,     #0xFE			; setup for 32 msec wakeup
	mov	res+1,   #0x03	
Alarm_Setup:
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; disable alarm while
  	mov	RTC0DAT, #(RTC0EN OR RTC0TR) 	; updating the regs      
   	nop
	mov 	RTC0ADR, #(0x10 OR ALARM0)	; load alarm regs       
	mov   RTC0DAT, res
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM1)
	mov	RTC0DAT, res+1
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM2)
	mov	RTC0DAT, #0 
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM3)
	mov	RTC0DAT, #0
	nop
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; enable SmaRTClock alarm
	mov	RTC0DAT, #(RTC0EN OR RTC0TR OR RTC0AEN OR ALRM) 
	ret

;-----------------------------------------------------------------------
DCDC_Setup:
	mov	CLKSEL, #0x04		; select LPO with 1:1 clock divider
	orl	FLSCL,  #BYPASS		; set the one-shot bypass bit
	mov	FLWR,	  #0x01		; write dummy value
	mov	A, CLKSEL			; wait for the divider setting to be applied
	jnb	ACC.7, $-2	

	mov	R0, #0x00
	mov	R1, #25
	djnz	R0, $				; ~1 sec delay
	djnz	R1, $-2
	mov	DC0CF, #0x63		; float in sleep, SYSCLK:8 as clock source
	mov	DC0CN, #0xE7		; set 3.3 V output
;	mov	DC0CF, #0x63		; float in sleep, SYSCLK:8 as clock source
	mov	R0, #0x00
	mov	R1, #25
	djnz	R0, $				; ~1 sec delay
	djnz	R1, $-2
	ret

;-----------------------------------------------------------------------
Timer_Setup:
	mov	CKCON,  #0x10		; Timer2 clock is SYSCLK		
	mov	TMR2CN, #0x00		; capture SmaRTClock/8
	ret

;-----------------------------------------------------------------------
Tune_RTC:
	orl	REG0CN, #0x10		; enable precision oscillator bias
	mov	R0, #50			; wait 5 usec
	djnz	R0, $
	orl	OSCICN, #0x80		; enable 24.5 MHz precision oscillator
	mov	A, OSCICN
	jnb	ACC.6, $-2			; wait for oscillator ready
	mov	CLKSEL, #0			; switch to precision oscillator

	mov	TMR2L,  #0x00		; clear Timer2 counter
	mov	TMR2H,  #0x00
	mov	TMR2CN, #0x14		; start capturing SmaRTClock/8

	clr	TF2H				; clear Timer2 interrupt flag
	jnb	TF2H, $			; wait for the 1st capture
	mov	den+1, TMR2RLL		; save the 1st capture
	mov	den+2, TMR2RLH

	clr	TF2H				; clear Timer2 interrupt flag
	jnb	TF2H, $			; wait for the 2nd capture
	mov	TMR2CN, #0x00		; disable capture and stop timer

	mov	CLKSEL, #0x04		; switch to 20 MHz LPO
	anl	OSCICN, #0x7F		; disable precision oscillator
	mov	REG0CN, #0			; disable precision oscillator bias

	clr	C				; subtract 1st capture from the
	mov	A, TMR2RLL			; 2nd one
	subb	A, den+1
	mov	den+1, A	
	mov	A, TMR2RLH
	subb	A, den+2
	mov	den+2, A
	mov	den, 	 #0			; LSB of the divisor
	mov	res,   #0			; clear the ratio bits
	mov	res+1, #0
	mov	num,   #0x90		; settings for 24.5 MHz tune-up
	mov	num+1, #0xEB
	mov	num+2, #0xBA

	mov	R7, #9			; the bits counter				
cr0:	mov	A, den+2			; prepare divider den by
	jb	ACC.7, cr1			; aligning its msb with the
	clr	C				; one of num (bit 22 counted from 0)
	mov	A, den+1
	rlc	A
	mov	den+1, A
	mov	A, den+2
	rlc	A
	mov	den+2, A		
	inc	R7
	sjmp	cr0

cr1:	clr	C				; perform a 24-bit subtraction
	mov	A, num			; num = num - den
	subb	A, den
	mov	num, A
	mov	A, num+1
	subb	A, den+1
	mov	num+1, A
	mov	A, num+2
	subb	A, den+2
	mov	num+2, A

	mov   F1, C
	cpl	C
	mov	A, res
	rlc	A
	mov	res, A
	mov	A, res+1
	rlc	A
	mov	res+1, A

	jnb   F1, cr2			; if C=0 proceed
	mov	A, num			; otherwise, restore the devidend
	add	A, den
	mov	num, A
	mov	A, num+1
	addc	A, den+1
	mov	num+1, A
	mov	A, num+2
	addc	A, den+2
	mov	num+2, A

cr2:	clr	C				; shift the divisor
	mov	A, den+2
	rrc	A
	mov	den+2, A
	mov	A, den+1
	rrc	A
	mov	den+1, A
	mov	A, den
	rrc	A
	mov	den, A
	djnz	R7, cr1

      anl   res, #0xFE  
	ajmp	Alarm_Setup

;-----------------------------------------------------------------------
Check_Light:
	acall	MAX44009_Read		; get light reading in R3:R2

	clr	C
	mov	A, R3
	subb	A, #SUN_THRESH		; FULL SUN ?
	jc	$+4				; NO  - proceed
	inc	sun5				; YES - increment the sunny counter

	mov	A, #0x04			; string to display
	addc	A, #0x00
	push	ACC

	inc	cnt5				; update the period counter
	mov	A, cnt5
	cjne	A, #5, display		; one minute passed ?
			
	mov	A, sun5			; YES - check the # of sunny periods 
	subb	A, #3				; more that 3 sun periods per min?
	jc	$+10				; NO  - proceed
	inc	sunCnt			; YES - update the sunny min counter
	mov	A, sunCnt			; increment the sunny minutes counter 
	jnz	$+4				; (2 bytes)
	inc	sunCnt+1
	mov	sun5, #0			; clear both counters
	mov	cnt5, #0

	inc	minCnt			; update the total minutes counter
	mov	A, minCnt 
	cjne	A, #60, display		; one hour passed ?
	inc	hrsCnt			; YES - increment the hrs counter
	mov	minCnt, #0			; reset the min counter

	mov	A, hrsCnt
	cjne	A, #12, display		; 12 hours passed?
	mov	state, #1			; YES - display the result
	mov	P0MDOUT, #0xFF		; prepare to power off the sensor
	mov	R0, SP

FSU:	clr	C
	mov	A, sunCnt
	subb	A, #LOW(360)
	mov	A, sunCnt+1
	subb	A, #HIGH(360)		; more than 6 hours of sun?
	jc	HSU				; NO - do the next check 
	mov	@R0, #0			; YES - display the FSU message
	sjmp	display

HSU:	clr	C
	mov	A, sunCnt
	subb	A, #LOW(270)
	mov	A, sunCnt+1
	subb	A, #HIGH(270)		; more than 4.5 hours of sun?
	jc	HSH				; NO - do the next check 
	mov	@R0, #1			; YES - display the HSU message
	sjmp	display

HSH:	clr	C
	mov	A, sunCnt
	subb	A, #LOW(180)
	mov	A, sunCnt+1
	subb	A, #HIGH(180)		; more than 3 hours of sun?
	jc	FSH				; NO - do the next check 
	mov	@R0, #2			; YES - display the HSH message
	sjmp	display

FSH:	mov	@R0, #3			; less than 3 hours sun
						; display the FSH message
;-----------------------------------------------------------------------
display:					; display message # in ACC
	pop	ACC
	add	A, ACC			; ACC *= 2
	mov	R5, A
	mov	DPTR, #table7seg		; initialize 7-seg table pointer
	movc	A, @A+DPTR			; get code of dig2
	jnb	P1.4, $+5			; COM=1 ?
	xrl	A, #0x1F			; YES - invert data
	orl	A, #0x20			; add the default port value
	jb	state.0, $+7		; terminal state?
	mov	C, P1.2			; NO - keep the blinking segment state
	mov	ACC.2, C
	mov	P1, A				; send it to the LCD
	
	inc	R5				; update table pointer
	mov	A, R5
	movc	A, @A+DPTR			; get table value
	jnb	P1.4, $+5
	xrl	A, #0xE0
	jb	state.0, $+5		; terminal state?
	orl	A, #0x1E
	mov	P0, A				; send it to the LCD

	jb	minCnt.0, $+5		; tune SmaRTClock LFO
	ajmp	Tune_RTC			; every other minute
	ret

;=====I2C INTERFACE====================================================
;       ___        
; DATA:    |_______
;       ______    
; SCK :       |___
;
Start_I2C:					; I2C start condition
	acall	delay
	setb	SDA				; start with SDA=1 and SCL=1	
	acall	delay
	setb	SCL
	acall delay
    	clr	SDA	        		; set dala low
	acall	delay
    	clr  	SCL	       		; set clock low
	acall	delay
	ret
        
;---------------------------------------------------------------------------
;              ___        
; DATA: ______|   
;           ______    
; SCK : ___|
;
Stop_I2C:					; I2C stop condition
	acall	delay
	clr	SDA				; start with data low
	acall	delay
	setb	SCL 	        		; set clock high
	acall	delay
	setb	SDA	        		; set data high
	acall	delay
	ret

;---------------------------------------------------------------------------
Write_I2C:					; send a byte in ACC into I2C
	clr	SCL				; make sure that SCL=0
	mov   R5, #8 			; initialize bit counter

w_loop:  
	rlc  	A				; get the leftmost data bit
	mov	SDA, C			; set SDA line
	acall	delay
	setb	SCL	        		; raise clock up
	acall	delay
	clr	SCL	        		; ... and down
	djnz 	R5, w_loop			; repeat this 8 times
		        
	setb 	SDA	        		; release the data line
	rlc	A				; restore ACC
	acall	delay
	setb	SCL	        		; clock up
	acall	delay
	clr	SCL	        		; clock down
	ret

;---------------------------------------------------------------------------
Read_I2C:					; read a byte into ACC, F0=ACK/NAK 
	clr	SCL				; make sure that SCL=0
	setb	SDA
	acall	delay
	mov   R5, #8 			; initialize bit counter

r_loop:  
	setb	SCL	        		; raise clock up
	acall	delay
	mov	C, SDA			; get data bit
	clr	SCL	        		; ... and down
	rlc  	A				; and shift it into ACC
	djnz 	R5, r_loop			; repeat this 8 times
		        
	mov	C, F0
	mov 	SDA, C	        	; set ACK/NAK
	acall	delay
	setb	SCL	        		; clock up
	acall	delay
	clr	SCL	        		; clock down
	ret

;------------------------------------------------------------------------
delay:
	mov	R7, #20
	djnz	R7, $
	ret

;=====SENSOR INTERFACE===================================================
MAX44009_Setup:
	mov	R1, #0
	mov	R0, #0			; power-up delay
	djnz	R0, $
	djnz	R1, $-2

	clr	A
	mov	cnt,      A			; clear all counters
	mov	cnt+1,    A
	mov	cnt5,     A
	mov	sun5,     A
	mov	sunCnt,   A
	mov	sunCnt+1, A
	mov	minCnt,   A
	mov 	hrsCnt,   A
	mov	state,    A
	ret

;------------------------------------------------------------------------
MAX44009_Read:
	acall Start_I2C
 	mov 	A, #0x94        		; chip address + write
	acall	Write_I2C
	mov 	A, #0x03        		; MSB LUX reading
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	A, #0x95			; chip address + read
	acall Write_I2C
	setb	F0				; send NAK
	acall Read_I2C			; get byte
	mov	R3, A				; save value in R3
	acall Stop_I2C
	ret					; read just Order+MSB
				
	acall Start_I2C			; restart interface
 	mov 	A, #0x94        		; chip address + write
	acall	Write_I2C
	mov 	A, #0x04        		; LSB LUX reading
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	A, #0x95			; chip address + read
	acall Write_I2C
	setb	F0				; send NAK
	acall Read_I2C			; get byte
	acall Stop_I2C
	mov	R2, A				; save value in R2
	ret

;========================================================================
table7seg: 	; P1    P0			; 7-seg LCD table for LCD messages
	DB	0x0D, 0x60			; FSU
	DB	0x0B, 0x60			; HSU
	DB	0x0B, 0xC0			; HSH
	DB	0x0D, 0xC0			; FSH
	DB	0x01, 0x60			; _SU
	DB	0x01, 0xC0			; _SH
	

END