 TITLE  "ADT75A.asm"   	       		; -55 - 120C thermometer  
 List P=PIC16F628a, R=DEC		; with ADT75A sensor
 INCLUDE "p16f628a.inc"

SCL	EQU	7			; Clock line setup
SDA	EQU 6				; Data line setup


; data segment in BANK 0
 CBLOCK 0x20           
 tempRQ, d, tmp, cnt, tint:2  		; local variables 
 w_save, stat_save, pc_save		; ISR variables 
 point, scale, button, sign		; decimal point flag
 err_i2c, dataIO, ACK_NAK		; I2C interface vars
 hundreds, units, tens, fract		; vars for BCD conversion
 bcd:4					; BCD representation of TEMP
 temp:2				  	; storage for temp measurement
 conv:2, temp2:2, convTime		; storage for C -> F conversion
 divTMP					; used in div5 procedure
 ENDC					

; code segment
 PAGE
  __CONFIG _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTOSC_OSC_NOCLKOUT

  ORG 0
	goto 	main

  ORG 4					; ISR
	movwf	w_save			; save W reg
	swapf	STATUS, w		; save stat reg without
	movwf	stat_save		; changing flags
	movf	PCLATH, w
	movwf	pc_save

	bcf	INTCON, 2		; clear TMR0 INT flag
	call	displayDigit		; <45 mksec

	movf	tint, f			; tint-- (in 16-bit)
	btfsc	STATUS, Z		; lower(tint) == 0 ?
	 decf	tint+1, f
	decf	tint, f

	movf	tint, w			; check if tint == 0 (in 16-bit)
	iorwf	tint+1, w
	btfss	STATUS, Z		; time to read temp ?
	 goto	task2			; NO - get to the next task 

task1					; request new temp conversion
	bsf	tint+1, 2		; restore the temp interval cntr
	call	getTemp1		; request TEMP in one-shot mode
	incf	tempRQ, f
	movlw	15
	movwf	convTime		; resetup 60ms delay for TEMP conversion
	goto	ISR_end

task2
	btfss	tempRQ, 0		; new temp requested?
	 goto 	task3			; NO - get to the next task

	decf	convTime, f
	btfss	STATUS, Z		; is conversion ready?
	 goto	task3			; not yet - continue

	call 	getTemp2		; get TEMP 
	call	bin2bcd			; convert it into BCD (<1 msec)
	goto	ISR_end

task3					; check the C/F button status
	btfss	PORTA, 5				
	 goto	$+3
	clrf	button			; button is not pressed - continue
	goto	ISR_end

	btfsc	button, 0		; button debouncing:
	 goto	ISR_end			; button was pressed and not released

	bsf	button, 0		; button is just pressed
	movf	scale, w		; toggle the C/F status
	sublw	1					
	movwf	scale			; scale = 1 - scale
	addlw	10			; W = 10 or 11 to display C/F
	movwf	bcd			; setup the scale letter code
	call	bin2bcd			; according	to the scale (C/F)	

ISR_end
	movf	pc_save, w		; restore PCLATH
	movwf	PCLATH
	swapf	stat_save, w		; get orig flags in STATUS
	movwf	STATUS
	swapf	w_save, f		; restore the original W		
	swapf	w_save, w
	retfie

encode7seg				; set up 7-seg digit codes
	andlw	0x0F
	addwf	PCL, f
  	retlw	b'00111111' 		; digit 0 pgfedcba
	retlw	b'00000110' 		; digit 1
	retlw	b'01011011'		; digit 2
	retlw	b'01001111'		; digit 3
	retlw	b'01100110'		; digit 4	
	retlw	b'01101101'		; digit 5
	retlw	b'01111101'		; digit 6
	retlw	b'00000111'		; digit 7
	retlw	b'01111111' 		; digit 8
	retlw	b'01101111'		; digit 9
	retlw	b'00111001'		; letter C
	retlw	b'01110001'		; letter F
	retlw	b'01000000'		; the - sign
	retlw	b'00000000'        	 ; blank
    	retlw  	b'00001000'
    	retlw	b'00001000'
  

main       		               	; main code
	bcf	STATUS, RP0		; activate BANK 0
	clrf	PORTA			; initialize PORT A
	clrf	PORTB			; initialize PORT B
	movlw	0x07
	movwf	CMCON			; comparators OFF
	
	bsf    	STATUS, RP0      	; change to BANK 1	
	movlw	b'10000011'		; TMR0 1:16 prescaler and
	movwf	OPTION_REG^0x80		; falling edge INT setup

	movlw	b'00100000'
	movwf	TRISA ^ 0x80		; enable RA<7:4> for input
	clrf	TRISB ^ 0x80		; enable output on PORTB
	bcf    	STATUS, RP0        	; back to BANK 0

	clrf	tint			; INITIALIZATION
	clrf	tint+1			; setup temp reading interval (4sec)
	bsf	tint+1, 2		; set tint = 1024
	clrf	scale			; scale: 0=C  1=F
	clrf	point			; decimal point flag
	clrf 	temp
	clrf	temp+1
	movlw	1
	movwf	d			; d is the digit code (0 - 3)

	movlw	0x0D			; initialize BCD array with blanks
	movwf	bcd+3
	movwf	bcd+2
	movwf	bcd+1
	movlw	0x0A				
	movwf	bcd

	; setup ADT75A one-shot mode
	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'00100000'
	movwf	dataIO				
	call	write_I2C
	call 	stop_I2C

	movlw	b'10100000'		; enable TMR0 interrupts
	movwf	INTCON

	goto 	$			; infinite loop

; procedures
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
	goto	$+1
	bsf	INDF, SCL		; release clock line
	goto	$+1
	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
	goto	$+1
	bcf	INDF, SCL		; ... and low
	decfsz	cnt, f
	 goto	w_loop			; repeat this 8 times

	goto	$+1
	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
	goto	$+1
	bsf	INDF, SCL		; raise clock up
	goto	$+1
	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	4				
	movwf	dataIO			; one-shot register
	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				
;	movlw	4
;	movwf	dataIO			; one-shot 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	temp+1
	incf	ACK_NAK, f		; setup to send NAK signal
	call	read_I2C		; get TEMP low order byte
	movf	dataIO, w
	movwf	temp
	call 	stop_I2C

	movlw	4			; shift temp 4 bits to the right					
	movwf	cnt			; multiply the sign bit
	bcf	STATUS, C
	btfsc	temp+1, 7
	 bsf	STATUS, C
	rrf	temp+1, f
	rrf	temp, f
	decfsz	cnt, f
	 goto 	$-6 
 	return

bin2bcd					; convert tempAve into BCD repres.
	clrf	sign
	clrf	point
	movf	temp, w			; preserve the original temp value
	movwf	conv			; by copying it to conv for
	movf	temp+1, w		; further processing
	movwf	conv+1
	
	btfsc	scale, 0		; scale == C?
	 call	convert			; convert C to F
	
	btfss	conv+1, 7		; temp < 0 ?
	 goto	frac			; NO - proceed
	movlw	0xFF			; convert temp to a positive
	xorwf	conv+1, f		; value in the 2's complement system
	xorwf	conv, f
	movlw	1
	addwf	conv, f
	btfsc	STATUS, C
	 incf	conv+1, f
	
	movlw	12			; the "-" sign code
	movwf	bcd+3
	incf	sign, f			; negative sign flag	

frac
	movf	conv, w
	andlw	0x0F
	movwf	fract			; fraction as int
	bcf	STATUS, C
	rlf	fract, f
	rlf	fract, f
	addwf	fract, f		; tmp = fraction*5
	rrf	fract, f				
	bcf	STATUS, C
	rrf	fract, f	
	bcf	STATUS, C
	rrf	fract, f
	btfsc	STATUS, C
	incf	fract, f		; rounding off

	rrf	conv+1, f		; fetching the integer part
	rrf	conv, f			; it all fits in 8 bits
	rrf	conv+1, f
	rrf	conv, f
	rrf	conv+1, f
	rrf	conv, f
	rrf	conv+1, f
	rrf	conv, w			; the int part of temp is in W
		
	clrf	hundreds
	clrf	tens
	clrf	units

dig2					; processing hundreds
	incf	hundreds, f
	addlw	-100
	btfsc	STATUS, C
	 goto	dig2
	decf	hundreds, f
	addlw	100

dig1					; processing tens
	incf	tens, f
	addlw	-10
	btfsc	STATUS, C
	 goto	dig1
	decf	tens, f
	addlw	10
	movwf	units			; processing units

	; packing the digits in BCD
	btfss	sign, 0			; temp < 0 ?
	 goto	positive		; NO - positive number processing
	movf	tens, f		
	btfss	STATUS, Z		; tens == 0 ?
	 goto	nofrac			; NO - drop the fraction

	movf	units, w		; -10 < temp < 0
	movwf	bcd+2			; display units and fraction	
	movf	fract, w
	movwf	bcd+1
	bsf	point, 7		; raise the decimal point flag
	return

nofrac
	movf	tens, w			; pack tens and units
	movwf	bcd+2
	movf	units, w
	movwf	bcd+1	
	return

positive
	movf	hundreds, f		; hundreds == 0 ?
	btfsc	STATUS, Z
	 goto 	nohundreds	
	
	movf	hundreds, w		; display a 3-digit number
	movwf	bcd+3
	movf	tens, w
	movwf	bcd+2
	movf	units, w
	movwf	bcd+1
	return

nohundreds
	movf	tens, w
	btfsc	STATUS, Z
	 movlw	13			; blank code
	movwf	bcd+3			; display one or two-digit number
	movf	units, w		; and fraction
	movwf	bcd+2
	movf	fract, w
	movwf	bcd+1
	bsf	point, 7
	return

displayDigit   				; display digit at position d
	movlw	0xF0
	andwf	PORTA, f		; shut down all digits
	btfss	d, 0
	 goto 	check2			; leftmost digit
	movf	bcd+3, w
	call	encode7seg		; w = segment code
	goto 	lightIt

check2
	btfss	d, 1			; middle number digit
	 goto	check3
	movf	bcd+2, w
	call	encode7seg
	addwf	point, w
	goto	lightIt

check3
	btfss	d, 2			; rightmost number digit
	 goto	check4
	movf	bcd+1, w
	call	encode7seg
	goto	lightIt

check4
	movf	bcd, w			; C/F LED
	call	encode7seg

lightIt		
	movwf	PORTB
	movf	d, w
	iorwf	PORTA, f

	bcf	STATUS, C
	rlf	d, f			; next d in cyclic order
	movlw	1
	btfsc	d, 4
	 movwf	d
	return

convert					; convert C into F
	movf	temp, w			; input: temp; output: conv
	movwf	temp2			; save original temp in temp2
	movf	temp+1, w
	movwf	temp2+1

	btfss	temp+1, 7
	 goto	absolute
	movlw	0xFF			; complement temp2 if temp < 0
	xorwf	temp2+1, f
	xorwf	temp2, f
	movlw	1
	addwf	temp2, f
	btfsc	STATUS, C
	 incf	temp2+1, f	
	
absolute
	movf	temp2, w
	movwf	tmp
	movf	temp2+1, w		; w:tmp contain the orig. temp2
	bcf	STATUS, C
	rlf	temp2, f
	rlf	temp2+1, f
	rlf	temp2, f
	rlf	temp2+1, f
	rlf	temp2, f
	rlf	temp2+1, f		; now temp2 is 8*temp2
	addwf	temp2+1, f
	movf	tmp, w			; tmp = old temp2
	addwf	temp2, f
	btfsc	STATUS, C
	 incf	temp2+1, f		; now temp2 = temp2*9

	movf	temp2+1, w		; w = first dividend
	call	div5
	movwf	tmp 			; tmp = q1
	movwf	conv+1			; byte conv+1 is ready
	bcf	STATUS, C	
	rlf	tmp, f			; tmp = q1*2
	rlf 	tmp, f			; tmp = q1*4
	addwf	tmp, w			; w = q1*5
	subwf	temp2+1, w		; w = r1
	movwf	tmp
	swapf	tmp, f
	swapf 	temp2, w		; w = left nibble of temp
	andlw	0x0F
	addwf	tmp, w			; w = second dividend
	movwf	temp2+1			; save it

	call	div5			; w = q2
	movwf	tmp
	swapf	tmp, w
	movwf	conv			; left nibble of conv is ready
	swapf	conv, w			; w = q2 again
	bcf	STATUS, C	
	rlf	tmp, f			; tmp = q2*2
	rlf 	tmp, f			; tmp = q2*4
	addwf	tmp, w			; w = q2*5
	subwf	temp2+1, w		; w = r2
	movwf	tmp
	swapf	tmp, f
	movf 	temp2, w
	andlw	0x0F	
	addwf	tmp, w			; w = third dividend
	movwf	temp2			; save it	

	call 	div5
	addwf	conv, f			; conv is ready
	movwf	tmp
	bcf	STATUS, C	
	rlf	tmp, f			; tmp = q3*2
	rlf 	tmp, f			; tmp = q3*4
	addwf	tmp, w			; w = q3*5
	subwf	temp2, f		; temp = r3
	incf	temp2, f
	btfss	temp2, 2
	 goto 	compl
	movlw	1			; rounding off
	addwf	conv, f
	btfsc	STATUS, C		; now conv (2 bytes) equals
  	 incf	conv+1, f		; 9*temp / 5

compl
	btfss	temp+1, 7		; was temp (in C) negative?
	 goto   add32			; NO - proceed adding 32
	movlw	0xFF
	xorwf	conv+1, f		; YES - complement temp2
	xorwf	conv, f
	movlw	1
	addwf	conv, f
	btfsc	STATUS, C
	 incf	conv+1, f

add32
	movlw	2			; adding 32 to the result
	addwf	conv+1, f		; to complete the conversion
	return

div5
	movwf	divTMP			; save W
	movlw	high divTable		
	movwf	PCLATH			; PCLATH = table segment
	movf	divTMP, w		; restore original W
	addlw	low	divTable	; w = table offset
	btfsc	STATUS, C		; correct PCLATH
	 incf	PCLATH, f		;  (if needed)
	movwf	PCL			; jump to the table entry
divTable
	dt 0, 0, 0, 0, 0, 1, 1, 1
	dt 1, 1, 2, 2, 2, 2, 2, 3
	dt 3, 3, 3, 3, 4, 4, 4, 4
	dt 4, 5, 5, 5, 5, 5, 6, 6
	dt 6, 6, 6, 7, 7, 7, 7, 7
	dt 8, 8, 8, 8, 8, 9, 9, 9
	dt 9, 9, 10, 10, 10, 10, 10, 11
	dt 11, 11, 11, 11, 12, 12, 12, 12
	dt 12, 13, 13, 13, 13, 13, 14, 14
	dt 14, 14, 14, 15, 15, 15, 15, 15
	dt 16, 16, 16, 16, 16, 17, 17, 17
	dt 17, 17, 18, 18, 18, 18, 18, 19
	dt 19, 19, 19, 19, 20, 20, 20, 20
	dt 20, 21, 21, 21, 21, 21, 22, 22
	dt 22, 22, 22, 23, 23, 23, 23, 23
	dt 24, 24, 24, 24, 24, 25, 25, 25
	dt 25, 25, 26, 26, 26, 26, 26, 27
	dt 27, 27, 27, 27, 28, 28, 28, 28
	dt 28, 29, 29, 29, 29, 29, 30, 30
	dt 30, 30, 30, 31, 31, 31, 31, 31
	dt 32, 32, 32, 32, 32, 33, 33, 33
	dt 33, 33, 34, 34, 34, 34, 34, 35
	dt 35, 35, 35, 35, 36, 36, 36, 36
	dt 36, 37, 37, 37, 37, 37, 38, 38
	dt 38, 38, 38, 39, 39, 39, 39, 39
	dt 40, 40, 40, 40, 40, 41, 41, 41
	dt 41, 41, 42, 42, 42, 42, 42, 43
	dt 43, 43, 43, 43, 44, 44, 44, 44
	dt 44, 45, 45, 45, 45, 45, 46, 46
	dt 46, 46, 46, 47, 47, 47, 47, 47
	dt 48, 48, 48, 48, 48, 49, 49, 49
	dt 49, 49, 50, 50, 50, 50, 50, 51
 end
