 TITLE  "LCD direct driving"  
 List P=16F913, R=DEC
 INCLUDE "p16F913.inc"
SCL	EQU	1			; Clock line setup
SDA	EQU 0				; Data line setup

; data segment in the shared area of all banks
 CBLOCK 0x70             	 	
 tempC:2, tens, units, tempOld
 displayCodes:2, mode
 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		; WDT reset?
	 goto 	loop			; YES - keep running
	goto 	main			; NO  - start initialization

 ; the next fours procedures are for the LCD 7-segment encoding
codeUr
	addwf 	PCL, f
	retlw 	b'00000111' 		; right part of digit 0
	retlw	b'00000001'		; right part of digit 1
	retlw	b'00001110'		; right part of digit 2
	retlw	b'00001011'		; right part of digit 3
	retlw	b'00001001'		; right part of digit 4
	retlw	b'00001011'		; right part of digit 5
	retlw	b'00001111'		; right part of digit 6
	retlw	b'00000001'		; right part of digit 7
	retlw	b'00001111'		; right part of digit 8
	retlw	b'00001011'		; right part of digit 9
	retlw	b'00001101'		; right part of digit A
	retlw	b'00001111'		; right part of digit B
	retlw	b'00000111'		; right part of digit C
	retlw	b'00001111'		; right part of digit D
	retlw	b'00001110'		; right part of digit E
	retlw	b'00001100'		; right part of digit F

codeUl
	addwf	PCL, f
	retlw	b'01000011'		; left part of digit 0
	retlw	b'00000001'		; left part of digit 1
	retlw	b'00000011'		; left part of digit 2
	retlw	b'00000011'		; left part of digit 3
	retlw	b'01000001'		; left part of digit 4
	retlw	b'01000010'		; left part of digit 5
	retlw	b'01000010'		; left part of digit 6
	retlw	b'00000011'		; left part of digit 7 
	retlw	b'01000011'		; left part of digit 8
	retlw	b'01000011'		; left part of digit 9
	retlw	b'01000011'		; right part of digit A
	retlw	b'01000000'		; right part of digit B
	retlw	b'01000010'		; right part of digit C
	retlw	b'00000001'		; right part of digit D
	retlw	b'01000010'		; right part of digit E
	retlw	b'01000010'		; right part of digit F

codeTr
	addwf	PCL, f
	retlw	b'11110000'		; right part of digit 0
	retlw	b'01000000'		; right part of digit 1
	retlw	b'11100000'		; right part of digit 2
	retlw	b'01100000'		; right part of digit 3
	retlw	b'01010000'		; right part of digit 4
	retlw	b'00110000'		; right part of digit 5
	retlw	b'10110000'		; right part of digit 6
	retlw	b'01100000'		; right part of digit 7
	retlw	b'11110000'		; right part of digit 8
	retlw	b'01110000'		; right part of digit 9
 	retlw	b'11110000'		; right part of digit A
	retlw	b'10010000'		; right part of digit B
	retlw	b'10110000'		; right part of digit C
	retlw	b'11000000'		; right part of digit D
	retlw	b'10110000'		; right part of digit E
	retlw	b'10110000'		; right part of digit F

codeTl
	addwf	PCL, f
	retlw	b'00011000'		; left part of digit 0
	retlw	b'00001000'		; left part of digit 1
	retlw	b'10010000'		; left part of digit 2
	retlw	b'10011000'		; left part of digit 3
	retlw	b'10001000'		; left part of digit 4
	retlw	b'10011000'		; left part of digit 5
	retlw	b'10011000'		; left part of digit 6
	retlw	b'00001000'		; left part of digit 7 
	retlw	b'10011000'		; left part of digit 8
	retlw	b'10011000'		; left part of digit 9
	retlw	b'10001000'		; right part of digit A
	retlw	b'10011000'		; right part of digit B
	retlw	b'00010000'		; right part of digit C
	retlw	b'10011000'		; right part of digit D
	retlw	b'10010000'		; right part of digit E
	retlw	b'10000000'		; right part of digit F

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

	; LCD module configuration
    	clrf	TRISA ^ 0x80			; STEP 1
  	clrf    TRISB ^ 0x80           
	movlw 	b'00000100'
	movwf	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
	bcf	LCDPS, 	LP2	
	bsf	LCDPS,	LP1
	bcf	LCDPS,	LP0
	movlw	0xFF				; step 8: enable LCD segment lines
	movwf	LCDSE0
	movlw	b'11111011'
	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 TCN75A temp 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		; mode 1: 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

	movlw	150
	subwf	tempC, w
	btfsc	STATUS, C
	 bsf	displayCodes+1, 5	; display hundreds

	call 	temp2BCD
	movwf	tens
	movwf	units
	swapf	tens, f
	movlw	0x0F
	andwf	tens, f
	andwf	units, f

	btfsc	displayCodes+1, 5
	 goto	dispTens		; display tens for temp >= 100F
	movf	tens, w
	btfsc	STATUS, Z		; do not display tens if it is 0
	 goto 	dispUnits		; and temp < 100

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	0x87			; setup FSR to point to TRISC
	movwf	FSR
	movlw	b'11111100'
	andwf	PORTC, 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'11111100'
	andwf	PORTC, 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'11111100'
	andwf	PORTC, 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	PORTC, 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'11111100'
	andwf	PORTC, 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	PORTC, 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

	rlf	tempC+1, f		; shift a bit from the low order byte
	rlf	tempC, f   		; into the high one
	btfsc	STATUS, C		; negative temp ?
	 goto 	negative
	rlf	tempC+1, f
	rlf	tempC, f		; too high hemp (> 63.75C) ?
	btfsc	STATUS, C
	 goto	tooHot
 	return

negative
	clrf	tempC			; ignore negative temp, show it as 0
	return

tooHot
 	movlw	0xFF			; do not show temperatures higher
	movwf	tempC			; than 63.75C
	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

 ORG 0x2100			; Celcius to Fahrenheit conversion table	
	dt 0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35
	dt 0x36, 0x36, 0x37, 0x37, 0x37, 0x38, 0x38, 0x39
	dt 0x39, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42
	dt 0x43, 0x43, 0x44, 0x44, 0x45, 0x45, 0x46, 0x46
	dt 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, 0x50
	dt 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x53, 0x53
	dt 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x57
	dt 0x57, 0x58, 0x58, 0x59, 0x59, 0x59, 0x60, 0x60
	dt 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64
	dt 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68
	dt 0x68, 0x68, 0x69, 0x69, 0x70, 0x70, 0x71, 0x71
	dt 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75
	dt 0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78
	dt 0x79, 0x79, 0x80, 0x80, 0x81, 0x81, 0x82, 0x82
	dt 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x86
	dt 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89
	dt 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93
	dt 0x93, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96
	dt 0x97, 0x97, 0x98, 0x98, 0x99, 0x99, 0x00, 0x00
	dt 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04
	dt 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07
	dt 0x08, 0x08, 0x09, 0x09, 0x09, 0x10, 0x10, 0x11
	dt 0x11, 0x12, 0x12, 0x13, 0x13, 0x13, 0x14, 0x14
	dt 0x15, 0x15, 0x16, 0x16, 0x17, 0x17, 0x18, 0x18
	dt 0x18, 0x19, 0x19, 0x20, 0x20, 0x21, 0x21, 0x22
	dt 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25
	dt 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29
	dt 0x29, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32
	dt 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36
	dt 0x36, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x40
	dt 0x40, 0x40, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43
	dt 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46, 0x47

 end
