$NOMOD51
$include (C8051F98x.inc)     
 
INDEX_CORR		EQU -55		; modify those 2 lines
INDEX_MAX		EQU 158		; if you perform calibration
            
RTC0CN_Local 	EQU 0x80		; default SmaRTClock cfg. value
PMU0CF_Snapshot 	EQU R5
Period_low		EQU 0xFE		; 0x00 for 1 sek
Period_high		EQU 0xFF		; 0x80 for 1 sek

	DSEG 	AT 0x20			; data segment in direct addressable space
Status:	DS 1				; status bits for circular buffer
Counter:	DS 1				; counter for the # of measurements

	ORG	0x30
bufTemp: 	DS 8				; circular buffer for temperature
tempAve_lo:	DS 1				; storage for temperature average sum
tempAve_hi:	DS 1
temp:		DS 1				; temp value 

	ORG 	0x40
bufHumi:	DS 32				; curcular buffer for humidity
humiAve_lo: DS 1				; storage for humidity average sum
humiAve_hi: DS 1

	CSEG 	AT 0				; interrupt vectors
	ljmp 	Main  			; reset

	ORG	0xB3
	USING    0                    ; specify register bank 

main:       
	anl   PCA0MD, #NOT(0x40)      ; disable the WDT
	mov	SP, #0x79			; setup stack

	acall	Ports_Setup
	acall	Clock_Setup
	acall	Debug_Trap			; debug trap !!!

	acall	RTC_Setup
	acall	Comparator_Setup
	acall	ADC_Setup

	mov 	PMU0CF, #CLEAR
	mov	Counter, #0			; initialize conversion counter
	mov	Status, #1			; request circ buffer initialization
	sjmp	measure

loop: 					; MAIN LOOP
	acall	LPM   			; enter sleep until next alarm
	anl 	P0, #NOT(0x06)		; disable latches
	mov	A, PMU0CF_Snapshot
	anl	A, #RTCAWK			; RTC_Alarm ?
	jz	loop				; NO - back to sleep	

measure:					; YES - do measurements
	acall	getTempHumi			; update temp & Humi values on display
;	sjmp	loop				; uncomment this line if no light sensor present

	inc	Counter			; time to measure light?
	mov	A, Counter
	cjne	A, #0, loop			; NO - proceed
	acall getLight			; light ON ?		
	jb	ACC.6, loop			; YES - continue the main loop

	acall	display_OFF			; NO - disable LCD oscillator 
wait4light:					; and check for light periodically
	acall	LPM				; sleep till the next light test
	anl	P0, #NOT(0x06)		; disable LCD latches
	anl	P1, #NOT(0xC0)
	acall	getLight
	jnb	ACC.6, wait4light		; NO light - keep waiting

	mov	Status, #1			; request circ buffer initialization
	setb	P0.5				; turn on LCD oscillator
	sjmp	measure			; back to the main loop

;-------------------PROCEDURES-------------------------------------------
Ports_Setup:
	mov  	P0MDIN,    #0xF7		; analog input on P0.3 	
	mov  	P0MDOUT,   #0x76		; push-pull out on P0[2:1] and P0[6:4]
	mov  	P1MDIN,    #0xFC		; comparator input on P1[1:0]
    	mov  	P1MDOUT,   #0xFC		; push-pull out on P1[7:2]
    	mov  	P0SKIP,    #0xBF		; I/O function on P0[5:0], P0.7
    	mov  	P1SKIP,    #0xFF		; I/O function on P1[7:0]
	mov  	XBR0,      #0x20
    	mov   XBR2,      #0x40		; enable XBar and weak pull-ups	
	mov	P0, 	     #0x29
	mov	P1, 	     #3		; initialize port pins
	ret

;-----------------------------------------------------------------------
Clock_Setup:
	mov	RTC0ADR, #(0x10 OR RTC0XCN)	
	mov	RTC0DAT, #RTC0AEN		; enable LFO
	nop
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; enable SmaRTClock
	mov	RTC0DAT, #(RTC0CN_Local OR RTC0TR)  

	mov	SFRPAGE, #0xF		; switch to SFR page 0xF
	mov	PMU0MD,  #0			; enable POR supply monitor
	mov	SFRPAGE, #0			; switch to default SFT page
	mov 	RSTSRC,  #0x06		; enable MCD and VDD monitor
	anl	OSCICN,  #NOT(0x80)	; disable internal precision oscillator
	mov	OSCXCN,  #0			; disable external oscillator
	mov	REG0CN,  #0			; disable precision oscillator bias
	mov	CLKSEL,  #0x03		; select LFO with 1:1 clock divider
	mov	A, CLKSEL			; wait for the divider setting to be applied
	anl	A,       #0x80		
	jz	$-4		
	ret

;-----------------------------------------------------------------------
Debug_Trap:
	mov	R2, #0xFF
	mov	A,  #0xFF

del_loop:					; nested loops for approx. 10 sec delay
	dec	A
	nop
	nop
	jnz	$-3

	mov	A, R2
	dec	A
	mov	R2, A
	jnz	del_loop	

	jnb	P0.0, $			; additional debug trap !!!

	mov	CLKSEL, #0x04		; select LPO with 1:1 clock divider
	orl	FLSCL, #BYPASS		; set the one-shot bypass bit
	mov	A, CLKSEL			; wait for the divider setting to be applied
	anl	A, #0x80		
	jz	$-4
	mov 	PMU0CF, #CLEAR
	ret

;-----------------------------------------------------------------------
RTC_Setup:
	mov 	RTC0ADR, #(0x10 + CAPTURE0)	; clear RTC timer
	mov 	RTC0DAT, #0				; autoincrement address
	nop						; feature does not work!
	mov 	RTC0ADR, #(0x10 + CAPTURE1)
	mov 	RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 + CAPTURE2)
	mov 	RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 + CAPTURE3)
	mov 	RTC0DAT, #0
	nop
	mov	RTC0ADR, #RTC0CN		; set timer value
	mov	RTC0DAT, #(RTC0CN_Local OR RTC0SET) 
	nop
	mov 	RTC0ADR, #(0x90 OR RTC0CN)
	nop
	nop
	nop
	mov 	A, RTC0DAT			
	anl	A, #RTC0SET			; verify that timer is set
	jnz	$-10

	mov	R6, #Period_low
	mov	R7, #Period_high
ALARM_Setup:
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; disable alarm while
   	mov	RTC0DAT, #(RTC0CN_Local AND NOT(RTC0AEN)) ; updating the regs      
   	nop
	mov 	RTC0ADR, #(0x10 OR ALARM0)	; load alarm regs       
	mov   RTC0DAT, R6
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM1)
	mov	RTC0DAT, R7
	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
	mov	RTC0DAT, #(RTC0CN_Local OR RTC0TR OR RTC0AEN OR ALRM)  
	ret

;-----------------------------------------------------------------------
ADC_Setup:
   	mov	ADC0CN, #0			; ADC0 disabled, Burst Mode disabled 
	mov 	ADC0CF, #0x11 		; SAR clock = 6.6MHz, gain = 1
   	mov	ADC0AC, #0              ; right-justify results
	ret

;-----------------------------------------------------------------------
Comparator_Setup:				; COMPARATOR init
    	mov  	CPT0CN, #0x0F		; set maximum hysteresis
    	mov  	CPT0MX, #0x44		; input MUX: C+ @P.0, C- @P1.1
    	mov  	CPT0MD, #0x03		; loweset power consumption
	ret

getLight:
	orl	CPT0CN, #0x80		; enable comparator	
	mov	R6, #0xA4			; use RTC clock to perform a
	mov	R7, #0x00			; 50 msec delay, which is needed
	acall	ALARM_Setup			; for comparator to set up
	acall	LPM   			; enter sleep mode until next alarm

	push	CPT0CN
	anl	CPT0CN, #NOT(0xB0)	; disable comparator

	mov	R6, #Period_low		; reassign RTC clock to its regular
	mov	R7, #Period_high		; wake-up time
	acall	ALARM_Setup

	pop	ACC				; A.6 = comparator value	
	ret

;-----------------------------------------------------------------------
display_OFF:
	mov	A, #0x0A			; turn off display
	mov	DPTR, #table7seg1		; initialize 7-seg table pointer
	movc	A, @A+DPTR			; get blank code 
	mov	P1, A
	nop
	nop
	nop
	orl	P0, #0x06			; latch it into all digits
	orl	P1, #0xC0
	nop
	nop
	nop
	clr	P0.5				; stop LCD oscillator
	ret

;-----------------------------------------------------------------------
getTempHumi:
   	mov	ADC0MX, #0x1B		; temp sensor as ADC input
	mov	REF0CN, #0x1C           ; enable high-speed ref, enable temp sensor
	mov	ADC0CN, #0x80		; enable ADC

	mov	A, #10			; 10 usec delay		
	dec	A				; 1 cycle
	nop					; 1 cycle
	nop					; 1 cycle
	jnz	$-3				; 2 cycles

	orl	ADC0CN, #0x10		; start conversion
	nop
	nop	
	mov	A, ADC0CN			; poll BUSY flag
	anl	A, #0x10
	jnz	$-5    	

	mov	ADC0CN, #0			; turn off ADC
	mov	REF0CN, #0			; turn off sensor and ref

	mov	A, ADC0L			; get ADC result
	add	A, #INDEX_CORR		; A = temp table index

	jc	temp1				; index >= 0 ? YES - proceed
	mov	A, #0				; NO - set index=0
	sjmp	process_TEMP

temp1:
	mov	B, A				; save ACC
	clr	C
	subb	A, #INDEX_MAX		; index in range ?
	jc	temp2				; YES - proceed
	mov	B, #INDEX_MAX		; NO - set index=MAX
temp2:
	mov	A, B				; restore ACC

process_TEMP:
	jnb	Status.0, average_TEMP	; init circ buffer ?
	mov	R0, #bufTemp		; YES - initialize buffer pointer
	mov	bufTemp, A			; fill the buffer with the
	mov	bufTemp+1, A		; temp reading
	mov	bufTemp+2, A
	mov	bufTemp+3, A
	mov	bufTemp+4, A
	mov	bufTemp+5, A
	mov	bufTemp+6, A
	mov	bufTemp+7, A

	push	ACC				; save ACC
	mov	B, #8				; compose the average sum by
	mul	AB				; multilpying the current temp
	mov	tempAve_lo, A		; by 8
	mov	tempAve_hi, B
	pop	ACC				; restore ACC
	ajmp	display_TEMP

average_TEMP:
	mov	B, A				; save ACC
	mov	A, tempAve_lo		; subtract the oldest temp value
	clr	C				; in circular buffer from the 
	subb	A, @R0			; average temp sum
	mov	tempAve_lo, A
	mov	A, tempAve_hi
	subb	A, #0
	mov	tempAve_hi, A

	mov	@R0, B			; save new temp in circular buffer
	inc	R0				; update circ buffer pointer
	anl	0, #0xF7			; take pointer R0 mod 8

	mov	A, tempAve_lo		; update average temp sum
	add	A, B
	mov	tempAve_lo, A
	mov	A, tempAve_hi
	addc	A, #0
	mov	tempAve_hi, A

	mov	A, tempAve_lo		; average temp sum (div. by 8)
	rlc	A				; for that we multiply it by 2 
	swap	A				; and swap the nibbles
	anl	A, #0x0F
	mov	B, A				; B contains the lower nibble
	mov	A, tempAve_hi
	rlc	A				; C is not midified since the
	swap	A				; last add
	add	A, B				; A = averaged ADC value

display_TEMP:
	mov	DPTR, #tempTable
	movc	A, @A+DPTR			; get temp value from table
	mov	R2, A
	swap	A
	anl	A, #0x0F			; extract first digit
	mov	DPTR, #table7seg1		; initialize 7-seg table pointer
	movc	A, @A+DPTR			; get code of dig1
	mov	P1, A
	nop
	nop
	nop
	setb	P1.6				; latch in the first digit
	nop
	nop
	nop
	clr	P1.6
	
	mov	A, R2
	anl	A, #0x0F			; extract second digit
	mov	DPTR, #table7seg2		; initialize 7-seg table pointer
	movc	A, @A+DPTR			; get code of dig2
	mov	P1, A
	nop
	nop
	nop
	setb	P1.7				; latch in the second digit
	nop
	nop
	nop
	clr	P1.7

	mov	A, R2				; convert temp from BCD to binary
	swap	A
	anl	A, #0x0F			; leave high-order nibble in ACC
	mov	B, #10
	mul	AB				; ACC = h.o. nibble*10
	mov	B, R2
	anl	B, #0x0F
	add	A, B
	mov	temp, A			; ACC = conversion result	

;-----------------------------------------------------------------------
getHumi:
	orl	P0, #0x10			; power-up the sensor
	mov	R6, #0x90			; use RTC clock to perform a
	mov	R7, #0x01			; 10 msec delay, which is needed
	acall	ALARM_Setup			; to warm up the humidity sensor
	acall	LPM   			; enter sleep mode until next alarm
	nop
	mov	R6, #Period_low		; reassign RTC clock to its regular
	mov	R7, #Period_high		; wake-up time
	acall	ALARM_Setup

	mov	ADC0MX, #0x3		; P0.3 as ADC input
	mov	REF0CN, #0x8           	; set reference = VDD
	mov	ADC0CN, #0x80		; enable ADC
	mov	A, #10			; 10 usec delay		
	dec	A				; 1 cycle
	nop					; 1 cycle
	nop					; 1 cycle
	jnz	$-3				; 2 cycles

	orl	ADC0CN, #0x10		; start conversion
	nop
	nop
	mov	A, ADC0CN			; poll BUSY flag
	anl	A, #0x10
	jnz	$-5               	

	anl	P0, #NOT(0x10)		; power-off sensor
	mov	ADC0CN, #0			; turn off ADC

	jnb	Status.0, average_HUMI	; init circ buffer ?
	clr	Status.0			; YES
	mov	R1, #bufHumi		; initialize buffer pointer
	mov	A, ADC0H
	mov	R2, #16			; buffer size
	inc	R1				; R1 points to odd bytes	
gh_loop1:
	mov	@R1, A			; fill the buffer with humi reading
	inc	R1				; move pointer to the next entry
	inc	R1	
	djnz	R2, gh_loop1

	dec	R1
	mov	A, ADC0L
	mov	R2, #16
gh_loop2:					; fill even bytes of the buffer
	dec	R1
	dec	R1
	mov	@R1, A
	djnz	R2, gh_loop2

	mov	B, #16			; compose the average sum by
	mul	AB				; multilpying current humi by 16
	mov	humiAve_lo, A
	mov	R2, B
	mov	B, #16
	mov	A, ADC0H
	mul	AB
	add	A, R2
	mov	humiAve_hi, A
	ajmp	process_HUMI

average_HUMI:
	mov	A, humiAve_lo		; subtract the oldest temp value
	clr	C				; in circular buffer from the 
	subb	A, @R1			; average humi sum
	mov	humiAve_lo, A
	inc	R1				; advance pointer to higher-order byte
	mov	A, humiAve_hi
	subb	A, @R1
	mov	humiAve_hi, A
	
	dec	R1
	mov	@R1, ADC0L			; save new humi in circular buffer
	inc	R1				; update circ buffer pointer
	mov	@R1, ADC0H
	inc	R1
	anl	1, #0xDF			; take pointer R1 mod 32

	mov	A, humiAve_lo		; update average humi sum
	add	A, ADC0L
	mov	humiAve_lo, A
	mov	A, humiAve_hi
	addc	A, ADC0H
	mov	humiAve_hi, A

process_HUMI:
	setb	RS0				; switch to register bank 1
	mov	A, humiAve_lo
	mov	R0, A
	mov	A, humiAve_hi
	mov	R1, A				; R1:R0 = ADC*16

      clr   C                 	; R1:R0 -= 2480
      mov   A, R0
      subb  A, #(2480 MOD 256)
      mov   R0, A
      mov   A, R1
      subb  A, #(2480 / 256)
      mov   R1, A

      mov   A, temp       		; compute A = temp*5
      rl    A
      rl    A
      add   A, temp
      
      add   A, #(2392 MOD 256)  	; R3:R2 = 2392 + R2*5
      mov   R2, A
      mov   A, #(2392 / 256)
      addc  A, #0
      mov   R3, A
 
      mov   A, R0       		; multiply lower-order bytes
      mov   B, R2
      mul   AB                
      mov   R4, A       		; result in R7:R4
      mov   R5, B
      
      mov   A, R1       		; multiply higher-order bytes
      mov   B, R3
      mul   AB
      mov   R6, A
      mov   R7, B
 
      mov   A, R0       		; first cross-product
      mov   B, R3
      mul   AB
      add   A, R5       		; add it to the product
      mov   R5, A
      mov   A, B
      addc  A, R6
      mov   R6, A
      clr   A
      addc  A, R7
      mov   R7, A
      
      mov   A, R1       		; second cross-product
      mov   B, R2
      mul   AB
      add   A, R5       		; add it to the product
      mov   R5, A
      mov   A, B
      addc  A, R6
      mov   R6, A
      clr   A
      addc  A, R7
      mov   R7, A
      
      mov   A, #18      		; add 18 to the result
      add   A, R4       		; 4-byte addition      
      mov   R4, A
      clr   A
      addc  A, R5
      mov   R5, A
      clr   A
      addc  A, R6
      mov   R6, A
      clr   A
      addc  A, R7
      mov   R7, A
      
      clr   C           		; shift R7:R5 1 bit to the left
      mov   A, R7
      rrc   A
      mov   R7, A
      mov   A, R6
      rrc   A
      mov   R6, A
      mov   A, R5
      rrc   A
      mov   R5, A
      clr   C           		; do it again      
      mov   A, R7
      rrc   A
      mov   R7, A
      mov   A, R6
      rrc   A
      mov   R6, A
      mov   A, R5
      rrc   A
      add   A, #128
      mov   R5, A       		; R7:R5 = R7:R4 / 1024
      
      clr   A
      addc  A, R6				; A = humidity
	clr	RS0				; back to register bank 0

display_HUMI:
	mov	B, #10			; convert humi value to BCD
	div	AB				; A = tens, B = units

	anl	A, #0x0F			; extract first digit
	mov	DPTR, #table7seg1		; initialize 7-seg table pointer
	movc	A, @A+DPTR			; get code of dig1
	mov	P1, A
	nop
	nop
	nop
	setb	P0.1				; latch in the first digit
	nop
	nop
	nop
	clr	P0.1
	
	mov	A, B				; extract second digit
	mov	DPTR, #table7seg2		; initialize 7-seg table pointer
	movc	A, @A+DPTR			; get code of dig2
	mov	P1, A
	nop
	nop
	nop
	setb	P0.2				; latch in the second digit	
	nop
	nop
	nop
	ret

;-----------------------------------------------------------------------
LPM:    
	mov	PMU0CF, #(SLEEP OR RSTWK OR RTCAWK) ; wakeup sources
	nop   				; device is now awake
   	nop
	nop
	nop
  
	mov	PMU0CF_Snapshot, PMU0CF	; capture the wake-up source
   	mov 	PMU0CF, #CLEAR		; clear all wake-up source flags

	mov	RTC0ADR, #(0x90 OR RTC0CN); capture RTC events that occured
	nop					; while PMU0CF was being cleared
	nop
	nop	
	mov	A, RTC0DAT			; RTC0CN_snapshot	
	anl	A, #ALRM
	orl	A, PMU0CF_Snapshot	; add it to the old flags
	mov	PMU0CF_Snapshot, A
   
	mov	A, PMU0CF_Snapshot	; reset pin Wakeup ?
	anl	A, #RSTWK
	jnz	debug_delay		
	ret					; NO - exit

debug_delay:
	mov	A, #0x5			; YES - make a 20 usec delay		
	dec	A				; 1 cycle
	nop					; 1 cycle
	jnz	$-2				; 2 cycles    
	ret      

;========================================================================
table7seg1:					; 7-seg LCD table for left digits
	DB	0 + 3				; code for 0
	DB	0x20 + 3			; code for 1
	DB	0x08 + 3			; code for 2
	DB	0x28 + 3			; code for 3
	DB	0x04 + 3			; code for 4
	DB	0x24 + 3			; code for 5
	DB	0x0C + 3			; code for 6
	DB	0x2C + 3			; code for 7
	DB	0x10 + 3			; code for 8
	DB	0x30 + 3			; code for 9
	DB	0x3C + 3			; blank	
							
table7seg2:					; 7-seg LCD table for right digits
	DB	0 + 3				; code for 0
	DB	0x04 + 3			; code for 1
	DB	0x10 + 3			; code for 2
	DB	0x14 + 3			; code for 3
	DB	0x20 + 3			; code for 4
	DB	0x24 + 3			; code for 5
	DB	0x30 + 3			; code for 6
	DB	0x34 + 3			; code for 7
	DB	0x08 + 3			; code for 8
	DB	0x0C + 3			; code for 9
	DB	0x3C + 3			; blank	

tempTable:
	DB 	0xA0, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, 0xA3, 0xA3
	DB 	0xA4, 0xA4, 0xA5, 0xA5, 0xA6, 0xA6, 0xA7, 0xA7
	DB 	0xA8, 0xA8, 0xA9, 0xA9, 0x10, 0x10, 0x11, 0x11
	DB 	0x12, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15
	DB 	0x15, 0x16, 0x16, 0x17, 0x17, 0x18, 0x18, 0x19
	DB 	0x19, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x22
	DB 	0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26
	DB 	0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x30, 0x30
	DB 	0x31, 0x31, 0x32, 0x32, 0x32, 0x33, 0x33, 0x34
	DB 	0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38
	DB 	0x38, 0x39, 0x39, 0x40, 0x40, 0x41, 0x41, 0x42
	DB 	0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x45, 0x45
	DB 	0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49
	DB 	0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x53
	DB 	0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x57
	DB 	0x57, 0x58, 0x58, 0x59, 0x59, 0x60, 0x60, 0x61
	DB 	0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64
	DB 	0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68
	DB 	0x69, 0x69, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72
	DB 	0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75


END