 TITLE  "humi.asm"      
 List P=PIC16F684, R=DEC
 INCLUDE "p16f684.inc"
 errorlevel -311

SCL_sensor		EQU		4
SDA_sensor		EQU		5
SCL_disp		EQU 	2			; Clock line setup
SDA_disp		EQU 	1			; Data line setup
CORR			EQU		6832 + 186	; correction const for pressure
D1				EQU		4010		; correction const for TEMP
C2				EQU		5871300		; correction const for HUMI
C1_1			EQU		14939840	;  -"-
C1_2			EQU		15939840	;  -"-

; SHT15 interface						adr	command r/w
#define 	STATUS_REG_W 	0x06 	;	000	0011 	0
#define 	STATUS_REG_R 	0x07 	;	000 0011 	1
#define 	MEASURE_TEMP 	0x03 	;	000 0001 	1
#define 	MEASURE_HUMI 	0x05 	;	000 0010 	1
#define 	RESET 			0x1e 	;	000 1111 	0

; data segment
 CBLOCK 0x20                   
 del, cnt, tmp:4  				; local variables 
 bar:2, temp:2, humi:3			; storage for pressure/temp/humidity	
 humi_lin:4, i, dataIO, ACK	
 ENDC

; code segment
 __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

 ORG 0     
	goto	start

 ORG 4
	retfie

msg1							; first message to display	
	addwf	PCL, f
	dt "mmHg", 0

msg2							; second message to display
	addwf	PCL, f
	dt "C  RH:", 0

start         
 	bcf		STATUS, RP0			; switch to BANK 0
	clrf	PORTA				; initialize PORT A
	clrf	PORTC				; initialize PORT C
	movlw	0x07
	movwf	CMCON0				; comparators OFF
	bsf    	STATUS, RP0     	; switch to BANK 1

 	; ADC and ports configuration
	clrf	TRISA ^ 0x80		; configure PORTA for output
	bsf		TRISA ^ 0x80, SDA_sensor  
	movlw	b'011100'
	movwf	TRISC ^ 0x80
	movlw	b'10000000'
	movwf	ANSEL ^ 0x80		; configure RC3 as analog input
	movlw	b'00100000'			; 250 KHz oscillator
	movwf	OSCCON ^ 0x80
	movlw 	b'01110000' 		; set ADC Frc clock
	movwf 	ADCON1 ^ 0x80
	bcf    	STATUS, RP0			; back to BANK 0
	movlw	b'00011101'
	movwf 	ADCON0	 			; left justify, Vdd Vref, AN7, On	
	
	movlw	100
	movwf	del
	call	delay	
	call	soft_reset_SHT15

	movlw	'P'					; clear display
	call	writeLCD	


loop
	movlw	250
	movwf	del
	call	delay				; 250 msec delay

	call	get_BARO
	call	get_TEMP
	call	get_HUMI
	call	writeMSG
  	goto 	loop				; endless loop


;//////////////////////////////////// procedures
;       _____         ________
; DATA:      |_______|
;           ___     ___
; SCK : ___|   |___|   |______
start_SHT15						; start condition for SHT15
	movlw	0x85				; setup FSR to point to TRISA
	movwf	FSR
	bsf		INDF, SDA_sensor	; start with SDA=1 and SCL=0
	bcf		PORTA, SCL_sensor
	nop							; delay slot
	bsf		PORTA, SCL_sensor	; set clock high
	bcf		PORTA, SDA_sensor
	bcf		INDF, SDA_sensor	; set dala low
	nop	
	bcf		PORTA, SCL_sensor
	goto	$+1					; 5 us delay		
	goto	$+1
	nop
	bsf		PORTA, SCL_sensor	; set clock high
	nop
	bsf		INDF, SDA_sensor	
	nop
	bcf		PORTA, SCL_sensor
	return

;       _____________________________________________________         ________
; DATA:                                                      |_______|
;          _    _    _    _    _    _    _    _    _        ___     ___
; SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______|   |___|   |______
reset_SHT15						; reset sensor interface
	movlw	0x85				; setup FSR to point to TRISA
	movwf	FSR
	movlw	9
	movwf	cnt
	bsf		PORTA, SCL_sensor
	nop
	bcf		PORTA, SCL_sensor
	decfsz	cnt, f
	 goto	$-4
	call	start_SHT15
	return

soft_reset_SHT15
	call	reset_SHT15
	movlw	RESET
	movwf	dataIO
	call	write_SHT15
	return

write_SHT15						; write byte to SHT15
	bsf		INDF, SDA_sensor	; make sure that DATA=1 and CLK=0 
	bcf		PORTA, SCL_sensor	
	movlw	8
	movwf	cnt					; bits counter

w_loop
	bcf		PORTA, SDA_sensor	; preset 0 on data line	
	rlf		dataIO, f			; get the rightmost data bit
	btfsc	STATUS, C
	 bsf	INDF, SDA_sensor
	btfss	STATUS, C
	 bcf	INDF, SDA_sensor	; now the data bit is on the data bus
	bsf		PORTA, SCL_sensor	; raise clock up
	goto	$+1					; short delay
	goto	$+1
	nop
	bcf		PORTA, SCL_sensor	; ... and low
	decfsz	cnt, f
	 goto	w_loop				; repeat this 8 times

	bsf		INDF, SDA_sensor	; release the data line	
	goto	$+1					; let slave respond for ACK
	goto	$+1
	goto	$+1
	bsf		PORTA, SCL_sensor	; clock up
 	goto 	$+1					; slave responds OK (hopefully)
	bcf		PORTA, SCL_sensor	; clock down
	return

read_SHT15						; read byte from SHT15
	bsf		INDF, SDA_sensor	; make sure that DATA=1 and CLK=0 
	bcf		PORTA, SCL_sensor			
	movlw	8
	movwf	cnt					; bits counter	

r_loop
	bsf		PORTA, SCL_sensor	; clock up
	bcf		STATUS, C
	btfsc	PORTA, SDA_sensor
	 bsf	STATUS, C			; C = data bit
	bcf		PORTA, SCL_sensor	; clock down
	rlf		dataIO, f			; insert it into the data byte
	decfsz	cnt, f
	 goto	r_loop				; repeat this 8 times

	movf	ACK, f				; set Z flag
	bcf		PORTA, SDA_sensor
	btfsc	STATUS, Z
	 bcf	INDF, SDA_sensor	; ACK signal
	bsf		PORTA, SCL_sensor	; clock up
	goto	$+1
	goto	$+1
	nop
	bcf		PORTA, SCL_sensor	; clock down
	bsf		INDF, SDA_sensor	; release the data line
	return

get_TEMP						; measure TEMP
	call	start_SHT15	
	movlw	MEASURE_TEMP
	movwf	dataIO
	call	write_SHT15			; send command (dataIO)
	btfsc	PORTA, SDA_sensor	; wait for the end of measurement
	 goto	$-1
	clrf	ACK					; send ACK after reading the first byte
	call	read_SHT15
	movf	dataIO, w			; MSB
	movwf	temp+1
	incf	ACK, f				; ignore the checksum
	call	read_SHT15
	movf	dataIO, w
	movwf	temp				; LSB

	movlw	LOW	D1				; correct temp
	subwf	temp, f
	btfss	STATUS, C
	 decf	temp+1, f
	movlw	HIGH D1
	subwf	temp+1, f
	return

get_HUMI						; measure relative humidity
	call	start_SHT15	
	movlw	MEASURE_HUMI
	movwf	dataIO
	call	write_SHT15			; send command (dataIO)
	btfsc	PORTA, SDA_sensor	; wait for the end of measurement
	 goto	$-1
	clrf	ACK					; send ACK after reading the first byte
	call	read_SHT15
	movf	dataIO, w			; MSB
	movwf	humi+1
	incf	ACK, f				; ignore the checksum
	call	read_SHT15
	movf	dataIO, w
	movwf	humi				; LSB
	call	process_humi
	return

get_BARO						; measure atmospheric pressure
	bsf		ADCON0, GO			; start ADC operation
	btfsc	ADCON0, GO			; and wait for its completion	
	 goto 	$-1
	movf	ADRESH, w			; get high 8 bits of ADC

	movwf	bar					; multiply bar by 6
	clrf	bar+1
	rlf		bar, f				; bar *= 2
	rlf		bar+1, f	
	bcf		bar, 0
	addwf	bar, f				; bar *= 3
	btfsc	STATUS, C
	 incf	bar+1, f
	rlf		bar, f				; bar *= 6
	rlf		bar+1, f
	bcf		bar, 0

	movlw	low	CORR			; add corr constant
	addwf	bar, f
	btfsc	STATUS, C
	 incf	bar+1, f
	movlw	high CORR			; bar = pressure*10
	addwf	bar+1, f			; (max 4 decimal digits)

	movlw	5					; rounding off to 3 digits
	addwf	bar, f
	btfsc	STATUS, C
	 incf	bar+1, f
	return

process_humi
	movlw	0xF0				; STEP1 - linearization (second-order curve)
	andwf	humi, f				; zero 4 lsb's

	clrf	tmp+3
	movlw	LOW	C2				; computing tmp = 5871300 - 4096·SO_RH
	movwf	tmp					; 24-bit subtraction
	movf	humi, w				; however, LSB(subtrahend)=0, so only
	sublw	HIGH C2				; 2 higher-order bytes of C2 are affected
	movwf	tmp+1
	movf	humi+1, w
	btfss	STATUS, C
	 addlw	1					; won't lead to overflow
	sublw	UPPER C2	
	movwf	tmp+2

	movf	humi, w				; adding 16·SO_RH to tmp
	addwf	tmp, f				; 24-bit operation
	movf	humi+1, w			; however humi is only a 16-bit value
	btfsc	STATUS, C
	 addlw	1					; won't lead to overflow since humi+1
	addwf	tmp+1, f			; has 4 leading zeros
	btfsc	STATUS, C
	 incf	tmp+2, f

	movf	humi+1, w 			; humi and humi+2 = SO_RH
	addwf	humi, f
	swapf	humi, w
	movwf	humi
	movwf	humi+2

	clrf	humi_lin			; computing humi_lin = tmp·SO_RH
	clrf	humi_lin+1			; 24-bit x 8-bit multiplication
	clrf	humi_lin+2			; initialize the result with 0
	clrf	humi_lin+3	
	movlw	8					; counter for the # of iterations
	movwf	cnt

mul_tmp							; unsigned multiplication
	rrf		humi, f				; put lsb(multiplier) into C
	btfss	STATUS, C
	 goto 	shift_tmp			; jump to shifting tmp
	movf	tmp, w				; add current tmp to humi_lin
	addwf	humi_lin, f			; 32-bit operation
	movf	tmp+1, w
	btfsc	STATUS, C
	 incfsz tmp+1, w
	  addwf	humi_lin+1, f
	movf	tmp+2, w
	btfsc	STATUS, C
	 incfsz	tmp+2, w
	  addwf	humi_lin+2, f
	movf	tmp+3, w
	btfsc	STATUS, C
	 addlw	1 
	addwf	humi_lin+3, f

shift_tmp
	bcf		STATUS, C
	rlf		tmp, f				; shift tmp to the left for the
	rlf		tmp+1, f			; next iteration
	rlf		tmp+2, f
	rlf		tmp+3, f
	decfsz	cnt, f
	 goto	mul_tmp

	movf	humi+1, w			; special cases
	xorlw	0xA0
	andlw	0x10				; bit Z is set of humi=160 or 176
	clrf	tmp					; in this case tmp=1
	btfss	STATUS, Z			; otherwise,   tmp=0
	 incf 	tmp, f

	movlw	LOW C1_1			; subtract C1 
	btfsc	tmp, 0
	 movlw	LOW C1_2
	subwf	humi_lin, f     	; this is a 32-bit operation but we use
	movlw	HIGH C1_1			; the fact that the MSB of C1 is 0
	btfsc	tmp, 0
	 movlw 	HIGH C1_2	
	btfss	STATUS, C			; none of the bytes of C1 is 0xFF
	 addlw	1					; no overflow here!
	subwf	humi_lin+1, f
	movlw	UPPER C1_1
	btfsc	tmp, 0
	 movlw	UPPER C1_2
	btfss	STATUS, C
	 addlw	1					; no overflow here!
	subwf	humi_lin+2, f
	btfss	STATUS, C
	 decf	humi_lin+3, f

	movlw	LOW 2500			; STEP2: temperature compensation 
	subwf	temp, w				; compute tmp+2:tmp+1 = temp - 2500
	movwf	tmp+1
	movwf	humi				; we do not need humi any more
	movlw	HIGH 2500
	btfss	STATUS, C
 	 addlw	1
	subwf	temp+1, w
	movwf	tmp+2
	movwf	humi+1				; humi+1:humi = temp - 2500
	clrf	tmp
	clrf	tmp+3

	btfss	tmp+2, 7			; temp - 2500 < 0?
	 goto 	proceed2			; no - proceed
	decf	tmp+3, f			; sign extend tmp (tmp+3 was 0 before)
	comf	humi, f				; humi = |temp - 2500|
	comf	humi+1, f
	incf	humi, f
	btfsc	STATUS, Z
	 incf	humi+1, f
	
proceed2
	rlf		tmp+2, w			; C = sign bit of tmp+2
	rrf		tmp+2, f			; tmp = (temp-2500)/2
	rrf		tmp+1, f			; signed division!
	
	movlw	8					; counter for the # of iterations
	movwf	cnt
	addwf	humi+2, f			; temp+3 = SO_RH + 8

mul_tmp2						; updating humi_lin
	rrf		humi+2, f			; put lsb into C
	btfss	STATUS, C
	 goto 	shift_tmp2			; jump to shifting tmp
	movf	tmp+1, w			; add current tmp to humi_lin
	addwf	humi_lin+1, f		; 32-bit operation
	movf	tmp+2, w			; but LSB(tmp)=0 so only 24 high-order
	btfsc	STATUS, C			; are involved into the operaton
	 incfsz tmp+2, w
	  addwf	humi_lin+2, f
	movf	tmp+3, w
	btfsc	STATUS, C
	 addlw	1 
	addwf	humi_lin+3, f

shift_tmp2
	bcf		STATUS, C
	rlf		tmp+1, f			; shift tmp to the left for the
	rlf		tmp+2, f			; next iteration
	rlf		tmp+3, f			; again, only 3 higher-order bytes
	decfsz	cnt, f				; need to be shifted
	 goto	mul_tmp2

	movlw	0xF0				; load 10240 or 61440 into WREG
	andwf	humi+1, w			; bit Z is set if |temp-2500| < 4096
	movlw	HIGH 10240
	btfss	STATUS, Z
	 movlw	HIGH 61440			; w = MSB(correction const) 

	subwf	humi_lin+1, f		; subtract correction const from humi_lin
	clrw						; this is a 32-bit operation
	btfss	STATUS, C			; but the constant is 16-bit only
	 addlw	1					; and LSB(const)=0 
	subwf	humi_lin+2, f
	clrw	
	btfss	STATUS, C
	 addlw	1					; no overflow here!
	subwf	humi_lin+3, f
	return

;////////////////////////////////////
getDigit32
	clrf	cnt
	incf	cnt, f
	movf	tmp, w				; 32-bit subtraction of the value in tmp
	subwf	humi_lin, f			; from humi_lin
	movf	tmp+1, w
	btfss	STATUS, C
	 incfsz tmp+1, w
	  subwf	humi_lin+1, f
	movf	tmp+2, w
	btfss	STATUS, C
	 incfsz	tmp+2, w
	  subwf	humi_lin+2, f
	movf	tmp+3, w
	btfss	STATUS, C
	 incfsz	tmp+3, w
	  subwf	humi_lin+3, f

	btfsc	STATUS, C
	 goto	getDigit32+1

	movf	tmp, w				; 32-bit addition of the value in tmp
	addwf	humi_lin, f			; to humi_lin
	movf	tmp+1, w
	btfsc	STATUS, C
	 incfsz tmp+1, w
	  addwf	humi_lin+1, f
	movf	tmp+2, w
	btfsc	STATUS, C
	 incfsz	tmp+2, w
	  addwf	humi_lin+2, f
	movf	tmp+3, w
	btfsc	STATUS, C
	 incfsz	tmp+3, w
	  addwf	humi_lin+3, f
	decf	cnt, w				; return digit in WREG
	addlw	48					; ASCII conversion
	return

getDigit16
	clrf	cnt
	incf	cnt, f
	movf	tmp+2, w			; 16-bit subtraction of tmp+3:tmp+2
	subwf	tmp, f				; from tmp+1:tmp
	movf	tmp+3, w
	btfss	STATUS, C
	 incfsz tmp+3, w
	  subwf	tmp+1, f

	btfsc	STATUS, C
	 goto	getDigit16+1

	movf	tmp+2, w			; 16-bit addition of tmp+3:tmp+2
	addwf	tmp, f				; to tmp+1:tmp
	movf	tmp+3, w
	btfsc	STATUS, C
	 incfsz tmp+3, w
	  addwf	tmp+1, f
	decf	cnt, w				; return digit in WREG
	addlw	48					; ASCII conversion
	return

;*******************LCD interface
writeMSG
; output of the top row (msg1)
	movlw	b'10000001'			; clear display
	call	writeLCD
	movlw	':'
	call 	writeLCD	

	movf	bar, w				;********** output pressure
	movwf	tmp
	movf	bar+1, w
	movwf	tmp+1

	movlw	LOW	1000			
	movwf	tmp+2
	movlw	HIGH 1000
	movwf	tmp+3
	call	getDigit16
	call	writeLCD			; first digit

	movlw	LOW 100
	movwf	tmp+2
	clrf	tmp+3
	call	getDigit16
	call	writeLCD			; second digit

	movlw	LOW 10
	movwf	tmp+2
	clrf	tmp+3
	call	getDigit16
	call	writeLCD			; third digit

	clrf 	i			
L1	movf	i, w
	call	msg1				; get the next char
	iorlw	0					; end of string?
	btfsc	STATUS, Z
	goto 	L2					; YES - jump out of the loop
	call 	writeLCD			; NO - send it to LCD
	incf	i, f				; update the char index
	goto 	L1

L2	movlw	0xC0
	call 	writeLCD			; move the cursor to the second line
	clrf	i					; clear the index of the second string

; output the bottom row 
	movlw	'T'
	call 	writeLCD
	movlw	':'
	call 	writeLCD
	movlw	'+'					; temp sign
	btfss	temp+1, 7
	 goto	$+7
	comf	temp, f				; compute |temp|
	comf	temp+1, f
	incf	temp, f
	btfsc	STATUS, C
	 incf	temp+1, f
	movlw	'-'
	call	writeLCD

	movf	temp, w				; *******************output TEMP
	movwf	tmp
	movf	temp+1, w
	movwf	tmp+1

	movlw	5					; round-off temp to 1 digit after
	addwf	tmp, f				; the decimal point
	btfsc	STATUS, C
	 incf	tmp+1, f

	movlw 	LOW 1000
	movwf	tmp+2
	movlw	HIGH 1000			
	movwf	tmp+3
	call	getDigit16
	sublw	48					
	btfsc	STATUS, Z
	 goto	$+3					; drop the leading 0
	sublw	48					; restore digit
	call	writeLCD			; first digit

	movlw	100
	movwf	tmp+2
	clrf	tmp+3
	call	getDigit16
	call	writeLCD			; second digit

	movlw	46					
	call	writeLCD			; decimal point
	
	movlw	10
	movwf	tmp+2
	clrf	tmp+3
	call	getDigit16			; third digit
	call	writeLCD

; output msg2
L3 	movf	i, w				
	call	msg2				; get the next char
	iorlw	0					; end of string?
	btfsc	STATUS, Z			
	 goto 	L4					; YES - jump out of the loop
	call 	writeLCD			; NO - send it to LCD
	incf	i, f				; update the char index
	goto 	L3

L4								; *************output humidity
	clrf	tmp					; load 10^8 into tmp
	movlw	LOW 390625			; this is 10^8 / 256
	movwf	tmp+1
	movlw	HIGH 390625
	movwf	tmp+2
	movlw	UPPER 390625
	movwf	tmp+3
	call	getDigit32

	sublw	57					; the first digit is at least 10
	btfsc	STATUS, C			; if humi exceeds 99%
	 goto	$+6					; in this case we just show 99
	movlw	57					; ASCII(9)
	call	writeLCD			; output 99%
	movlw	57
	call	writeLCD
	goto	humi_end

	sublw	57					; restore the first digit	
	btfss	STATUS, Z			; don't show the leading 0
	 call	writeLCD			; first digit

	movlw	128
	movwf	tmp
	movlw	LOW 39062			; this is 10^7 / 256
	movwf	tmp+1
	movlw	HIGH 39062
	movwf	tmp+2
	movlw	UPPER 39062
	movwf	tmp+3
	call	getDigit32
	call	writeLCD			; second digit
humi_end
	movlw	'%'				
	call	writeLCD			; percentage sign
	return

writeLCD						; write byte in W to the LCD line
	movwf	dataIO
	movlw	b'111001'
	andwf	PORTC, f
	movlw	0x87				; setup FSR to point to TRISC
	movwf	FSR
	bsf		INDF, SCL_disp		; make sure that clock is high
	movlw	8
	movwf	cnt					; bits counter

w_LCD_loop
	btfss	PORTC, SCL_disp		; wait for release of the clock line
	 goto	$-1

	bcf		INDF, SCL_disp		; set clock low
	rlf		dataIO, f			; get the rightmost data bit
	btfss	STATUS, C
	 bcf	INDF, SDA_disp
	btfsc	STATUS, C
	 bsf	INDF, SDA_disp		; now the data bit is on the line
	goto	$+1					; short delay
	bsf		INDF, SCL_disp		; raise clock up

	movlw	6					; a 20 usec delay
	addlw	-1
	btfss	STATUS, Z
	 goto	$-2

	decfsz	cnt, f
	 goto	w_LCD_loop			; repeat this 8 times
	return

delay							; a delay for del milliseconds
	movlw 	200
	
	sublw	1					; this loop takes 5us*200 = 1ms
	sublw	0					; for PIC @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del, f
	goto 	delay
	return

 end
