 TITLE  "LCD direct drive"  
 List P=16F916, R=DEC
 INCLUDE "p16F916.inc"

SCL	EQU	7			; Clock line setup
SDA	EQU 6				; Data line setup

; data segment in the shared area
 CBLOCK 0x70             	 	
 tempC:2, tens, units, tempOld
 displayCodes:2, mode, testL 
 err_i2c, dataIO, ACK_NAK, cnt		; I2C interface vars
 ENDC

 PAGE
 __CONFIG _DEBUG_OFF & _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF &  _CP_OFF & _MCLRE_OFF & _PWRTE_ON  & _WDT_ON & _INTOSCIO

  org    0
	btfss	STATUS, NOT_TO
	 goto 	loop	
	goto 	main

codeUr
	addwf 	PCL, f
	retlw 	b'00001111' 		; right part of digit 0
	retlw	b'00001100'		; right part of digit 1
	retlw	b'00001011'		; right part of digit 2
	retlw	b'00001110'		; right part of digit 3
	retlw	b'00001100'		; right part of digit 4
	retlw	b'00000110'		; right part of digit 5
	retlw	b'00000111'		; right part of digit 6
	retlw	b'00001100'		; right part of digit 7
	retlw	b'00001111'		; right part of digit 8
	retlw	b'00001110'		; right part of digit 9
	retlw	b'00000000'		; the "-" symbol

codeUl
	addwf	PCL, f
	retlw	b'01100000'		; left part of digit 0
	retlw	b'00000000'		; left part of digit 1
	retlw	b'01010000'		; left part of digit 2
	retlw	b'01010000'		; left part of digit 3
	retlw	b'00110000'		; left part of digit 4
	retlw	b'01110000'		; left part of digit 5
	retlw	b'01110000'		; left part of digit 6
	retlw	b'01000000'		; left part of digit 7 
	retlw	b'01110000'		; left part of digit 8
	retlw	b'01110000'		; left part of digit 9
	retlw	b'00010000'		; the "- symbol

codeTr
	addwf	PCL, f
	retlw	b'10010000'		; right part of digit 0
	retlw	b'10000000'		; right part of digit 1
	retlw	b'10100000'		; right part of digit 2
	retlw	b'10100000'		; right part of digit 3
	retlw	b'10110000'		; right part of digit 4
	retlw	b'00110000'		; right part of digit 5
	retlw	b'00110000'		; right part of digit 6
	retlw	b'10000000'		; right part of digit 7
	retlw	b'10110000'		; right part of digit 8
	retlw	b'10110000'		; right part of digit 9
	retlw	b'00100000'		; the "-" symbol
	retlw	b'00000000'		; blank

codeTl
	addwf	PCL, f
	retlw	b'10001110'		; left part of digit 0
	retlw	b'00000010'		; left part of digit 1
	retlw	b'10001100'		; left part of digit 2
	retlw	b'10000110'		; left part of digit 3
	retlw	b'00000010'		; left part of digit 4
	retlw	b'10000110'		; left part of digit 5
	retlw	b'10001110'		; left part of digit 6
	retlw	b'10000010'		; left part of digit 7 
	retlw	b'10001110'		; left part of digit 8
	retlw	b'10000110'		; left part of digit 9
	retlw	b'00000000'		; the "-" symbol
	retlw	b'00000000'		; blank

main
  	bsf    	STATUS, RP0     	; change to BANK 1
	movlw	7
	movwf	CMCON0 			; comparators off
	clrf	ANSEL ^ 0x80		; all inputs digital
	clrf	OSCCON ^ 0x80		; oscillator configuration bits

    clrf	TRISA ^ 0x80		; STEP 1
  	clrf    TRISB ^ 0x80           
	clrf	TRISC ^ 0x80
	bcf	STATUS, RP0
  	bsf	STATUS, RP1		; change to BANK 2

	bsf	LCDCON ^ 0x100, VLCDEN	; STEP 2: enable LCD bias voltage pins
	bsf	LCDCON ^ 0x100, CS1	; STEP 3: select clock source (lfintosc)	
	bcf	LCDCON ^ 0x100, CS0
	bcf	LCDCON ^ 0x100, LMUX1	; STEP 4: select multiplex mode (static)
	bcf	LCDCON ^ 0x100, LMUX0

	bcf	LCDPS, 	WFT		; STEP 5: select waveform type (A)
	bcf	LCDPS,	BIASMD		; STEP 6: select Bias mode
	bcf	LCDPS, 	LP3		; STEP 7: select refresh rate
	bc	LCDPS, 	LP2		; 1:3
	bsf	LCDPS,	LP1
	bcf	LCDPS,	LP0

	movlw	b'10111111'		; step 8: enable LCD segment lines
	movwf	LCDSE0
	movlw	0xFF
	movwf	LCDSE1
	clrf	LCDDATA0 ^ 0x100	; step 9: clear segment data registers
	clrf	LCDDATA1 ^ 0x100

	bsf	LCDCON ^ 0x100,	LCDEN	; step 10: turn on the LCD driver module
	bcf	LCDCON ^ 0x100, SLPEN	; enable LCD in sleep mode

	movlw	b'00001010'		
	movwf	WDTCON			; watchdog timer prescaler 1:1024

	bcf	STATUS, RP1		; back to BANK 0


	; setup the temp sensor resolution of 0.0625C and shutdown
	call	start_I2C
	movlw	b'10010000'		; address byte
	movwf	dataIO
	call	write_I2C		; send address byte
	movlw	1				
	movwf	dataIO			; config pointer
	call	write_I2C
	movlw	b'01100001'
	movwf	dataIO			; temp resolution
	call	write_I2C
	call 	stop_I2C
	clrf	tempOld
	clrf	mode				

loop					; MAIN LOOP
	btfss	mode, 0				
	 goto	getTemp
	clrwdt
	call 	getTemp1		; mode 0: request new temp conversion
	incf	mode, f
	sleep
	nop	

getTemp
	clrwdt
	call	getTemp2		; get temp from sensor
	incf	mode, f
	movf	tempOld, w			
	subwf	tempC, w
	btfss	STATUS, Z		; old temp = new one?
	  call	displayTemp		; NO - update display
	sleep
	nop
	goto	loop

displayTemp
	clrf	displayCodes		
	clrf	displayCodes+1

	call 	temp2BCD
	movwf	tens			; extract tens and units
	movwf	units
	swapf	tens, f
	movlw	0x0F
	andwf	tens, f
	andwf	units, f

dispTens
	movf	tens, w			; displaying tens
	call 	codeTr
	iorwf	displayCodes, f
	movf	tens, w
	call	codeTl
	iorwf	displayCodes+1, f

dispUnits
	movf	units, w		; displaying units
	call 	codeUr
	iorwf	displayCodes, f
	movf	units, w
	call	codeUl
	iorwf	displayCodes+1, f

	movf	displayCodes+1, w	; update LCD
	movwf	LCDDATA1 ^ 0x100
	movf	displayCodes, w
	movwf	LCDDATA0 ^ 0x100
	bcf	STATUS, RP1		; back to BANK 0
	return

start_I2C				; start setup for I2C interface
	movlw	0x85			; setup FSR to point to TRISA
	movwf	FSR
	movlw	b'00111111'
	andwf	PORTA, f
	bsf	INDF, SDA		; pull high SDA and SCL
	bsf	INDF, SCL
	goto	$+1			; delay slot
	bcf	INDF, SDA		; set data low
	nop				; wait for slave to detect it
	bcf	INDF, SCL		; set clock low
	return

stop_I2C				; stop setup for I2C interface
	movlw	b'00111111'
	andwf	PORTA, f
	bcf	INDF, SCL		; make sure that clock and data
	bcf	INDF, SDA		;  lines are low
	nop
	bsf	INDF, SCL		; release clock line
	nop
	bsf	INDF, SDA		; release data line
	return

write_I2C				; write byte to I2C line
	movlw	b'00111111'
	andwf	PORTA, f
	bcf	INDF, SCL		; make sure that clock is low
	clrf	err_i2c			; start with no error
	movlw	8
	movwf	cnt			; bits counter

w_loop
	bcf	INDF, SDA		; start w. data bit low
	rlf	dataIO, f		; get the rightmost data bit
	btfss	STATUS, C
	 bcf	INDF, SDA		; now the data bit is on the line
	btfsc	STATUS, C
	 bsf	INDF, SDA
	goto	$+1
	bsf	INDF, SCL		; raise clock up
	nop
	bcf	INDF, SCL		; ... and low
	decfsz	cnt, f
	 goto	w_loop			; repeat this 8 times

	bsf	INDF, SDA		; check ACK from slave		
	goto	$+1			; let slave respond
	bsf	INDF, SCL		; clock up
	btfsc	PORTA, SDA		; check the ACK from slave
	 incf	err_i2c, f		; no ACK (error)
	bcf	INDF, SCL		; clock down
	return

read_I2C				; read byte from I2C line
	movlw	b'00111111'
	andwf	PORTA, f
	bcf	INDF, SCL		; make sure that clock is low			
	movlw	8
	movwf	cnt			; bits counter	

r_loop
	bcf	INDF, SCL		; set clock low for long enough
	goto	$+1
	bsf	INDF, SCL		; set clock high
	bcf	STATUS, C
	btfsc	PORTA, SDA
	 bsf	STATUS, C		; C = data bit
	rlf	dataIO, f		; insert it into the data byte
	decfsz	cnt, f
	 goto	r_loop			; repeat this 8 times

	bcf	INDF, SCL		; sending the ACK/NAK to slave
	bsf	INDF, SDA
	movf	ACK_NAK, f		; the NAK signal
	btfsc	STATUS, Z
	 bcf	INDF, SDA		; the ACK signal
	nop
	bsf	INDF, SCL		; raise clock up
	nop
	bcf	INDF, SCL		; ... and low
	nop
	bsf	INDF, SDA		; release the data line
	return

getTemp1				; request TEMP conversion
	call	start_I2C
	movlw	b'10010000'		; address byte
	movwf	dataIO
	call	write_I2C		; send address byte
	movlw	1				
	movwf	dataIO			; config pointer
	call	write_I2C
	movlw	b'11100001'		; request one-shot temp conv
	movwf	dataIO			; temp resolution
	call	write_I2C
	call 	stop_I2C
	return

getTemp2				; read TEMP from sensor
	call	start_I2C
	movlw	b'10010000'		; address byte/W
	movwf	dataIO
	call	write_I2C		; send address byte				
	clrf	dataIO			; TEMP register pointer
	call	write_I2C

	call	start_I2C
	movlw	b'10010001'		; address byte/R
	movwf	dataIO
	call	write_I2C		; send address byte
	clrf	ACK_NAK
	call	read_I2C		; get TEMP high order byte
	movf	dataIO, w
	movwf	tempC
	incf	ACK_NAK, f		; setup to send NAK signal
	call	read_I2C		; get TEMP low order byte
	movf	dataIO, w
	movwf	tempC+1
	call 	stop_I2C

test
	movlw	0xB0			; round off tempC is the fractional
	addwf	tempC+1, w		; part in tempC+1 is above 5
	btfss	STATUS, C
	 return

rounding
	btfsc	tempC, 7		; tempC < 0 ?	
	 goto 	negative
	incf	tempC, f		; rounding off non-negative temp
	return

negative
	decf	tempC, f		; rounding off negative temp
	return

temp2BCD
	movf	tempC, w
	movwf	tempOld			; update old temp
	bsf	STATUS,	RP1		; switch to BANK 2
	movwf	EEADRL 			; set address (in Wreg) to read
	bsf	STATUS, RP0		; switch to BANK 3
	bcf	EECON1, EEPGD
	bsf	EECON1, RD		; load byte form EEPROM
	bcf	STATUS, RP0		; back to BANK 2
	movf	EEDATL, w		; move it to Wreg
	return

testLCD					; this function is for testing only
	movlw	-9			; it displays on the LCD the numbers
	movwf	testL			; in the range -9 .. 100
					; this way one can test the 7-segment
lcdLoop					; codes and the BCD conversion
	movf	testL, w
	movwf	tempC
	call	displayTemp

	movlw	255			; short delay
	addlw	-1
	goto	$+1
	goto	$+1
	btfss	STATUS, Z
	 goto	$-4
	
	incf	testL, f
	movlw	101
	subwf	testL, w
	btfsc	STATUS, Z
	 goto	testLCD
	goto	lcdLoop		
	return

 ORG 0x2100				; conversion table	
 	dt 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7
	dt 0xB8, 0xB9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
	dt 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23
	dt 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31
	dt 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39
	dt 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47
	dt 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55
	dt 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63
	dt 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71
	dt 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79
	dt 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87
	dt 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
	dt 0x96, 0x97, 0x98, 0x99, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
	dt 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9
	dt 0xA8, 0xA7, 0xA6, 0xA5, 0xA4, 0xA3, 0xA2, 0xA1

  END
