$NOMOD51
$include (C8051F300.inc)      

SDA		EQU	P0.7			; LCD interface
SCL		EQU	P0.6
A0		EQU	P0.5
RST		EQU	P0.4
CS		EQU	P0.2

	DSEG 	AT 0x20			; data segment in direct addressable space
pag:		DS 1				; LCD page #
col: 	 	DS 1				; LCD column #
width: 	DS 1				; LCD bitmap width
height:	DS 1				; LCD bitmap height
showMode:	DS 1				; display mode: 0=erase, 0xFF=display
lx_old:	DS 2				; storage for previous measurement

	ORG	0x30
		DS 1				; aux byte used by BCD conversion
digits:	DS 5				; storage for digits
fraCount:	DS 1				; # of fraction digits

	CSEG 	AT 0				; interrupt vectors
	ljmp 	Main  			; reset

	ORG	0x03				; INT0 vector
	reti

	ORG	0x0B				; Timer0 vector
	reti

	ORG	0x63				; 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

	acall	Ports_Setup
	acall Clock_Setup			; housekeeping tasks 
	acall	Timers_Setup
	acall	I2C_Setup
	acall	INT0_Setup
	acall	Init_LCD
	acall	MAX44007_Setup

loop:						; main loop
	anl	P0MDOUT, #0xDF		; clear bit 5	
	setb	P0.5				; configure P0.5 for input
	setb	EA				; enable interrupts
	mov	OSCICN, #0x08		; switch to external oscillator
	mov	PCON,   #0x01		; set CPU idle mode (sleep for 1 sec)
	mov	PCON, PCON			; dummy 3-cycle instruction
	mov	OSCICN, #0x04		; switch to internal oscillator
	clr	EA				; disable interrupts
	orl	P0MDOUT, #0x20		; configure P0.5 for output

	acall	MAX44007_Read		; get Lux value from sensor in R3:R2

	mov	A, lx_old			; is the previous measurement
	xrl	A, R2				; equal the current one?
	jnz	dis
	mov	A, lx_old+1
	xrl	A, R3
	jz	loop				; YES - wait for the next data

dis:	mov	lx_old, R2			; NO  - display the new value
	mov	lx_old+1, R3
	acall	Display_Number	
	sjmp	loop
	
;-------------------PROCEDURES-------------------------------------------
Ports_Setup:
    	mov  	P0MDIN,  #0xF7		; analog operation on P0.3 (XTAL2)
     	mov  	P0MDOUT, #0xF4		; open drain mode on P0[1:0] and P0.3
   	mov  	XBR0,    #0xFC		; skip P0.0 and P0.1 for SMB
    	mov  	XBR1,    #0x04		; enable SMB I/O
    	mov  	XBR2,    #0x40		; enable Xbar
    	mov	P0,	   #0x0F		; initialize P0
    	ret 

;------------------------------------------------------------------------
Clock_Setup:
	mov  	OSCICN, #0x04		; internal 20 MHz oscillator with 1:8 divider
	mov	OSCXCN, #0x52		; enable external osc. in cap mode w. K=7.7  
    	ret					; for cap=47pF freq=66.6KHz

;------------------------------------------------------------------------
Timers_Setup:
	mov	TMOD,  #0x21		; mode_1 for Timer0, mode_2 for Timer1
	mov	CKCON, #0x12		; use SYSCLK/48 for Timer0 and SYSCLK for Timer1	
	mov	TH1,   #251			; bit rate for I2C
;	mov	IE,    #0x02		; enable Timer0 interrupt
;	setb	TR0				; start Timer0 for 1-sec interrupts	
	ret

;------------------------------------------------------------------------
INT0_Setup:
	setb	IT0				; congigure INT0 as edge-triggered	
	mov	IT01CF, #5			; assign active low INT0 to P0.5
	setb	EX0				; enable INT0	
	ret

;------------------------------------------------------------------------
I2C_Setup:
	mov	SMB0CF, #0x01		; SMBUS clock by Timer 1 overflow
	ret

;------------------------------------------------------------------------
MAX44007_Setup:
	setb 	TR1     			; enable Timer 1
	orl 	SMB0CF, #0x80           ; enable SMBUS

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x05        	; set Upper Threshold
	acall Write_I2C
	mov 	SMB0DAT, #0x00        	; 0x00
	acall	Write_I2C
	acall Stop_I2C

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x06        	; set Lower Threshold
	acall Write_I2C
	mov 	SMB0DAT, #0xFF        	; 0xEF
	acall	Write_I2C
	acall Stop_I2C

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x07        	; set Threshold Timer
	acall Write_I2C
	mov 	SMB0DAT, #0x01        	; 0x01
	acall	Write_I2C
	acall Stop_I2C

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x01        	; enable interrupt
	acall Write_I2C
	mov 	SMB0DAT, #0x01        	; 0x01
	acall	Write_I2C
	acall Stop_I2C

	anl	SMB0CF, #0x7F           ; disable SMBUS
	clr 	TR1         		; disable Timer 1
	ret

;------------------------------------------------------------------------
MAX44007_Read:
	setb 	TR1     			; enable Timer 1
	orl 	SMB0CF, #0x80           ; enable SMBUS

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x00        	; interrupt status
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	SMB0DAT, #0xB5		; chip address + read
	acall Write_I2C
	acall Read_I2C			; get byte
	clr	ACK				; send NAK
	acall Stop_I2C

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x02        	; MSB LUX reading
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	SMB0DAT, #0xB5		; chip address + read
	acall Write_I2C
	acall Read_I2C			; get byte
	clr	ACK				; send NAK
	acall Stop_I2C
	mov	R4, A				; save value in R3

	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x03        	; MSB LUX reading
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	SMB0DAT, #0xB5		; chip address + read
	acall Write_I2C
	acall Read_I2C			; get byte
	clr	ACK				; send NAK
	acall Stop_I2C
	mov	R3, A				; save value in R3
				
	acall Start_I2C
 	mov 	SMB0DAT, #0xB4        	; chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x04        	; LSB LUX reading
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	SMB0DAT, #0xB5		; chip address + read
	acall Write_I2C
	acall Read_I2C			; get byte
	clr	ACK				; send NAK
	acall Stop_I2C
	mov	R2, A				; save value in R2

	anl	SMB0CF, #0x7F           ; disable SMBUS
	clr 	TR1         		; disable Timer 1
	ret

;=========================I2C interface==================================
Start_I2C:
	setb	STA				; Free Smb interupt and signal start bit
	clr	SI
	jnb 	SI, $				; Wait until SMB Interupt is raised
	clr	STA
	ret

Stop_I2C:
	setb 	STO				; Free Smb interupt and signal stop bit
	clr	SI	
	jb 	MASTER, $			; Wait until SMB Interupt is raised	
	ret

Read_I2C:
	clr 	SI				; Free Smb interupt	
	jnb 	SI, $				; Wait until system is done
	mov 	A, SMB0DAT			; Move the recieved data to ACC
	ret	

Write_I2C:
	clr 	SI				; Free Smb interupt	
	jnb 	SI, $				; Wait until system is done
	ret

;------------------------------------------------------------------------
Bin2BCD:				      ; convert a number in R3:R2 into BCD
      mov   A, #0xFF			; convert a number in R3:R2 into BCD
     	mov   digits, A
      mov   digits+1, A
      mov   digits+2, A
      mov   digits+3, A
      mov   digits+4, A

	mov	R0, #digits			; set pointer to digits storage		
	mov	A, R3
	swap	A
	mov	R5, A
	anl	A, #0xF0
	add	A, R2
	mov	R2, A
	mov	A, R5
	anl	A, #0x0F			; order=ACC, mantissa=R2
      cjne  A, #0x0F, bb0
      ajmp  bb7

bb0:	jnz	bb1				; order=0?
	mov	A, R2				; YES - fetch 2 lsb's
	anl	A, #3
	mov	B, #25			; multiply them by 25 to get fraction bits
	mul	AB				; here B=0
	mov	B, #10
	div	AB				; A=2nd fraction digit, B=3rd frac. digit
	mov	@R0, B			; save fraction digits
	inc	R0
	mov	@R0, A
	inc	R0

	mov	A, R2				; extract 1st fraction digit
	clr	C				; and the integer part
	rrc	A
	clr	C
	rrc	A
	mov	B, #10
	div	AB
	mov	@R0, B	         	; B=1st fraction digit, A=int part
	inc	R0
	mov	@R0, A			; conversion is complete here
	mov	fraCount, #3		; number of digits after the decimal point
	ret

bb1:	cjne	A, #1, bb2			; order=1?
	mov	@R0, #5			; YES - preload the 2nd frac digit
      mov  	A, R2      
      jbc	ACC.0, $+5			; in this case the number is either x.x5		
	mov	@R0, #0			; or x.x0
	inc	R0
      
	rr	A				; get rid of fraction digits
	mov	B, #10
	div	AB				; B=1st frac digit, A=int part
	mov	@R0, B
	inc	R0
	mov	fraCount, #2

	mov	B, #10
	div	AB				; A=1st int digit B=2nd int digit
	mov	@R0, B
	jz	bb1a				; don't count the leading 0 in
	inc	R0				; the int part
	mov	@R0, A
bb1a:	ret

bb2:	mov	fraCount, #1		; here the order is at least 2
	add	A, #-2			; adjust the order due to 1/4*1/10 = 0.025
	mov	R3, #0			; extend R2 to R4:R3:R2
	mov	R4, #0
	jz	bb5				; no shift if the order=0

	cjne	A, #8, bb3			; order=8?		
	mov	3, R2				; YES - exchange R3 and R2
	mov	R2, #0
	sjmp	bb5	

bb3:	mov	DPTR, #Powers
	jc	bb4
	add	A, #-8			; here the order is between 9 and 12
	movc	A, @A+DPTR			; get power of 2
      mov  	B, A
      mov	A, R2
	mul	AB
	mov	R4, B
	mov	R3, A
	mov	R2, #0
	sjmp	bb5

bb4:	movc	A, @A+DPTR			; here the order is between 1 and 7
	mov	B, A
      mov  	A, R2         
	mul	AB
	mov	R3, B
	mov	R2, A
	mov	R4, #0

bb5:	clr	C
	mov	A, R2	         		; the number to process is R4:R3:R2 (20 bits)
	subb	A, #0x10			; if the number exceeds 10000=0x2710 add 5 to it
	mov	A, R3
	subb	A, #0x27
	mov	A, R4
	subb	A, #0x00
      jc	bb6

      dec      R0
	mov	A, R2				; add 5 to the number for rounding off
	add	A, #5
	mov	R2, A
	mov	A, R3
	addc	A, #0
	mov	R3, A
	mov	A, R4
	addc	A, #0
	mov	R4, A
	mov	fraCount, #-1

bb6:	clr	C				; replace the number with 99999 if it exceeds it
	mov	A, R2				; 1000000=0xF4240
	subb	A, #0x40
	mov	A, R3
	subb	A, #0x42
	mov	A, R4
	subb	A, #0x0F
	jc	bb8-2

bb7:	mov	fraCount, #0
	mov	A, #9
	mov	digits,   A
	mov	digits+1, A
	mov	digits+2, A
	mov	digits+3, A
	mov	digits+4, A
	ret

	mov	R5, #6			; compute 6 decimal digits of R4:R3:R2
bb8:  mov	A, R3                   ; at each iteration the number /= 10
      anl   A, #0xF0                ; processing first two nibbles: 
      anl   3, #0x0F                ; low-order one in R4 and
	orl	A, R4                   ; high-order one in R3
      swap 	A
	mov	B, #10
	div	AB
	swap	A
      mov   R4, A
      anl   4, #0x0F
      anl   A, #0xF0
      orl   A, R3
      mov   R3, A
      
      anl   A, #0x0F                ; processing low-order nibble in R3
	swap	A
      orl   A, B
      swap  A
	mov	B, #10			
	div	AB	
      anl   3, #0xF0
      orl   3, A
      
      mov   A, R2                   ; processing hign-order nibble in R2
      anl   A, #0xF0
      anl   2, #0x0F
	orl	A, B							
	swap	A				
	mov	B, #10			
	div	AB				
	swap	A	
      orl   A, R2
      mov   R2, A
      
      anl   A, #0x0F                ; processing lower-order nibble in R2
      anl   2, #0xF0
      swap  A
      orl   A, B
      swap 	A     		
	mov	B, #10			
	div	AB
      orl   2, A
	mov	@R0, B			; resulting decimal digit
	inc	R0

      mov   A, R4				; check is the quotient is 0
      orl   A, R3
      orl	A, R2				; if ACC=0 then conversion is over
	jz	bb9	
	djnz	R5, bb8	 
bb9:	ret

;------------------------------------------------------------------------
Display_Number:				; display the number in R3:R2
	orl	OSCICN, #0x03		; switch to 20 MHz clock
	acall	Bin2BCD

	mov	R3, #5
	mov	R4, fraCount
	mov	R0, #digits
	mov	col, #88

	mov	pag, #3
	mov	showMode, #0xFF
	
dn1:	mov	A, @R0			; get next digit
	inc	R0				; update the digit pointer
	jnb	ACC.7, dn2			; ACC = 0xFF?
	mov	showMode, #0x00		; YES - disable displaying the symbol
dn2:	anl	A, showMode
	acall	Set_Font_PTR		; set DPTR pointer to the digit data
	acall	Display_Symbol		; display the digit

	clr	C
	mov	A, col			; adjust column number for the next digit
	subb	A, width
	subb	A, #3
	mov	col, A

	djnz	R4, dn3
	mov	DPTR, #Point
	acall	Display_Bitmap
	clr	C
	mov	A, col			; adjust column number for the next digit
	subb	A, #22
	mov	col, A

dn3:	djnz	R3, dn1
	ret

;------------------------------------------------------------------------
Display_Bitmap:				; display 2-dim bitmap pointed by DPTR
	clr	A				; width offset in bitmap
	movc	A, @A+DPTR			; get bitmap width
	mov	width, A			; save it
	mov	A, #1				; height offset in bitmap (# of pages)
	movc	A, @A+DPTR
	mov	height, A
	mov	R5, #2			; bitmap data offset
	sjmp	$+4

Display_Symbol:				; col, pag, width, height and DPTR must be set
	mov	R5, #0			; symbol bitmap data offset
	push	height
	mov	R7, pag			; initial page number
dbm1:
	mov	A, R7
	orl	A, #0xB0					
	acall	Command_LCD			; set page

	mov	A, col
	swap	A
	anl	A, #0x0F
	orl	A, #0x10			; set column address (MSB)
	acall	Command_LCD
	mov	A, col
	anl	A, #0x0F			; set column address (LSB)
	acall	Command_LCD
			
	mov	R6, width			; loop over bitmap columns
dbm2:
	mov	A, R5
	inc	R5
	movc	A, @A+DPTR
	anl	A, showMode			; display or erase
	acall	Write_LCD		
	djnz	R6, dbm2
	dec	R7
	dec	height
	mov	A, height
	jnz	dbm1
	pop	height
	ret

;-------------------LCD INTERFACE----------------------------------------
Command_LCD:				; write a command in ACC to LCD
	clr	CS				; chip select
	clr	A0				; clear command/data selector
	sjmp	$+6

Write_LCD:
	clr	CS				; chip select
	setb	A0				; set command/data selector
	mov	R1, #8			; loop counter
wl_loop:
	rlc	A				; shift out msb into CY
	mov	SDA, C			; copy ACC bit to port
	setb	SCL				; clock up
	sjmp	$+2				; 2-cycle delay
	clr	SCL				; clock down
	djnz	R1, wl_loop			; loop back
	rlc	A				; (optional) restore ACC
	setb	CS
	clr	A0
	ret

Init_LCD:
	acall	Delay_1ms
	setb	RST				; move LCD out of reset
	acall	Delay_1ms
	clr	SCL				; make sure that clock is down

	mov	A, #0xAC			; sleep mode ON (2-byte command)
	acall	Command_LCD	
	mov	A, #0x00			; set sleep mode
	acall	Command_LCD
	mov	A, #0xAF			; display OFF
	acall	Command_LCD
	mov	A, #0xA5			; display all points
	acall	Command_LCD

	mov	A, #0xA2			; set LCD bias 1/9
	acall	Command_LCD	
	mov	A, #0xA0			; set non-reversed display
	acall	Command_LCD
	mov	A, #0xC0			; set normal direction
	acall	Command_LCD
	mov	A, #0x20			; set V0 resisors Ra/Rb ratio
	acall	Command_LCD
	mov	A, #0x81			; set V0 out voltage
	acall	Command_LCD
	mov	A, #0x37			; second byte of set volume command
	acall	Command_LCD
	mov	A, #0x2F			; set internal power mode (all on)
	acall	Command_LCD

	mov	A, #0xA4			; display all points OFF
	acall	Command_LCD
	mov	A, #0xAC			; sleep mode OFF (2-byte command)
	acall	Command_LCD	
	mov	A, #0x01			; set normal mode
	acall	Command_LCD

	mov	A, #0x40			; reset start line # to 0
	acall	Command_LCD
	mov	A, #0xAF			; display ON
	acall	Command_LCD

	acall	Clear_LCD
	mov	pag, #1
	mov	col, #111
	mov	DPTR, #Lx
	mov	showMode, #0xFF
	acall	Display_Bitmap		; display the "lx" string
	ret

Delay_1ms:
	mov	R5, #100			; 1 msec power-up delay
del_1ms:					
	mov	A, #67
	djnz	ACC, $			; 3*67=201 cycles delay (10 mksec)
	djnz	R5, del_1ms
	ret

;------------------------------------------------------------------------
Clear_LCD:
	mov	R7, #4			; set page 3
next_page:
	mov	A, R7
	dec	A
	orl	A, #0xB0			; set page command
	acall	Command_LCD

	mov	A, #0x10			; set column address (MSN)
	acall	Command_LCD
	mov	A, #0
	acall	Command_LCD	

	mov	R6, #128			; # of columns
next_col:
	mov	A, #0x00
	acall	Write_LCD
	djnz	R6, next_col 
	djnz	R7, next_page
	ret

;------------------------------------------------------------------------
Set_Font_PTR:				; set DPTR to the digit data in Font table
	mov	height, #4			; large font settings
	mov	width,  #19
	mov	pag,	  #3
	mov	B,      #(19*4)		; 19x32 font char size (in bytes)
	mov	DPTR,   #Large_Font	; preset pointer to the 1st font byte

	mul	AB				; adjust the digit pointer by adding to it
	add	A, DPL			;  the value ACC*char_size
	mov	DPL, A
	mov	A, B
	addc	A, DPH
	mov	DPH, A
	ret

$INCLUDE (fonts.asm)

END