;************************************************************************
;	Digital Thermostst						*
;************************************************************************

	list      p=16f628     	
	#include <p16f628.inc> 
	errorlevel 0,-302	
	

CBLOCK	0x20		
DS_DAT	
DS_SIGN	
SEND	
COUNT	
NUM1	
NUM2	
NUM3	
DATA1	
HALF
DUMMY0
DUMMY1
DUMMY2
DUMMY5
DUMMY6
BITS
COMPARE	
SET_TEMP
ADDR
	endc			; End of definition

#define	DQ	PORTA,0		; Define text substitution		
#define	E	PORTB,0
#define	RS	PORTB,3
	
	org	0x000		
	goto	Start

;**********************************************************************
;    	 LCD Display                             		
;**********************************************************************
Room_	addwf	PCL,f			; 
	dt	"Room Temp "		
Set_	addwf	PCL,f					
	dt	"Set Temp "		
First_	addwf	PCL,f
	dt	"<Set Temp First> "

;**********************************************************************
;    	Main Program						      *
;**********************************************************************
Start	call	Init_Port	; Set -up port
	call	Init_LCD	; Initial LCD 
	movlw	0x20		; Set temperature data = 00100000b = 16.0C
	movwf	SET_TEMP
	bsf	PORTA,4		; Off relay, first
	movlw	0x80		; 
	movwf	ADDR		; Select LCD's first line address
	call	Set_Addr	

	clrf	COUNT		; Get message from table
Send_Lp2 movf	COUNT,w		; Begin with counter = 0
	call	First_		; Income data position COUNT
	call	Send		; Send data to LCD
	incf	COUNT,f		; Get the next data if counter < 16
	movlw	.16
	subwf	COUNT,w		; Test counter < 16 ?
	btfss	STATUS,Z
	goto	Send_Lp2	; If < 16, still send data

Again	movlw	0xC5		; Select the center of LCD's second line
	movwf	ADDR
	call	Set_Addr

	clrf	HALF		; Save half degree
	bcf	STATUS,C
	rrf	SET_TEMP,w	; Rotate bit data
	rlf	HALF,f	; 
	movwf	DATA1		; Save for conversion
	call	Convert
	call	Send_Temp	; Send temperature value to LCD

	btfsc	PORTA,1		; Check temperature increment switch 
	goto	Check_Run
	call	Delay200		; Delay for switch debouncing
	incf	SET_TEMP,f
Check_Run 
	btfsc	PORTA,3		; Check running switch
	goto	Again		; If not pressed, loop again
	
	
	movlw	0x01		; Send command for clearing display
	movwf	ADDR
	call	Set_Addr
	call	Delay5		; Nescessary delay

	clrf	COUNT		; Get message from table
Send_Lp3 movf	COUNT,w		; Begin with counter = 0
	call	Set_		; Income data position COUNT
	call	Send		; Send data to LCD
	incf	COUNT,f		; Still send next data if counter < 8
	movlw	.9
	subwf	COUNT,w		; Test counter < 8 ?
	btfss	STATUS,Z
	goto	Send_Lp3	; If < 8, try again
	call	Send_Temp

	movlw	0xC0		; Select LCD's second line address 
	movwf	ADDR
	call	Set_Addr

	clrf	COUNT		; Get message from Table
Send_Lp4 movf	COUNT,w		; Begin with counter = 0
	call	Room_		; Income data position COUNT
	call	Send		; Send data to LCD
	incf	COUNT,f		; Still send next Data if counter < 8
	movlw	.9
	subwf	COUNT,w		; Test counter < 8 ?
	btfss	STATUS,Z
	goto	Send_Lp4	; If < 8, try again
	
OFF_	bsf	PORTA,4		; Off relay
	call	RD_Temp
	movf	DS_DAT,w	; Load data	
	clrf	HALF		; Clear register for storing a half temp value
	bcf	STATUS,C
	rrf	DS_DAT,w	; Use 7-bit upper
	rlf	HALF,f		; Store a half degree (.5) or (.0)
	movwf	DATA1		; Data for conversion to decimal
	call	Convert		; Convert data to decimal
	
	movf	DS_DAT,w	; Check room temp > setting temp ?
	subwf	SET_TEMP,w
	btfss	STATUS,C
	goto	ON_		; If more than, goto on relay

	movlw	0xC9		; If not, show room's temp on LCD
	movwf	ADDR
	call	Set_Addr

	call	Send_Temp
	call	Delay200
	goto	OFF_		; Loop again

ON_	bcf	PORTA,4		; On relay
	movlw	0xC9		; Set address for LCD
	movwf	ADDR		; Show room's temp
	call	Set_Addr	
	
	call	Send_Temp	; Send temperature value to LCD
	call	Delay200	
	call	RD_Temp	; Read temperature value again
	movf	DS_DAT,w	; Load data	
	clrf	HALF		; Clear register for storing a half temp
	bcf	STATUS,C
	rrf	DS_DAT,w	; Use 7-bit upper
	rlf	HALF,f		; Store a half degree (.5) or (.0)
	movwf	DATA1		; Data for decimal conversion
	call	Convert		

	movf	DS_DAT,w	; Check temperature again
	addlw	0x01		; Temperarure is lower than [setting Temperature - 1] ?
	subwf	SET_TEMP,w	
	btfsc	STATUS,C	; If yes, off relay
	goto	OFF_		; 
	goto	ON_		; If no, loop again		

;**********************************************************************
;    	SetPort I/O of PIC16F628	                              *
;**********************************************************************
Init_Port clrf	PORTA		; Clear PORTA before initial
	movlw	0x07		; Set PORTA to digital I/O
	movwf	CMCON
	bsf	STATUS,RP0
	movlw	b'11101111'	; Set RA0, RA1 and RA3 as input, RA4 as output
	movwf	PORTA
	movlw	b'00000110'	; Use PORTB for LCD
	movwf	PORTB
	bcf	STATUS,RP0
	return

;**********************************************************************
;    	Read Data From DS1820		                              *
;**********************************************************************
RD_Temp	call	DS_Rx		; Check DS1820 status
	addlw	0x01		; 255=ready, 0= ready 
	btfss	STATUS,Z	; Zero flag set = ready
	return			; W register is not zero = not ready
Get_Temp call	DS_Reset	; Reset chip first
	btfss	STATUS,Z	; Zero flag set = OK
	goto	Error_		; If not response, exit
	movlw	0xcc		; Skip ROM command
	call	DS_Tx		; Send command
	movlw	0xbe		; Read scratch pad command
	call	DS_Tx		; Send command
	call	DS_Rx		; Read 8-bit data
	movwf	DS_DAT		; Save data to register
	call	DS_Rx		; Sign (FF=-VE, 00=+VE)
	movwf	DS_SIGN		; Save 9th bit to register 
	call	DS_Reset	; Restart
	movlw	0xcc		; Skip ROM command
	call	DS_Tx		; Send command
	movlw	0x44		; Start convert command
	call	DS_Tx		; Send command

	movf	DS_SIGN,w	; Check dign 
	btfsc	STATUS,Z	; Check dign = 00 ?
	goto	Pass		; If yes, Temp as positive then return
	addlw	0x01		; 
	btfss	STATUS,Z	; Check sign = FF ?
	goto	Error_		; If not, end with error
Pass	retlw	0x00		; If Yes, (Temp as negstive then return	
Error_	retlw	0x01
	
;**********************************************************************
;    	MACRO For DS1820		                              *
;**********************************************************************
DQLOW	macro
	bcf	DQ		; DQ bit ready
	bsf	STATUS,RP0
	bcf	DQ		; Set DQ to output
	bcf	STATUS,RP0
	endm

DQHIZ	macro
	bsf	STATUS,RP0
	bsf	DQ		; Set DQ to input
	bcf	STATUS,RP0
	endm

PAUSE	macro	DELAY		; Generate delay time
	movlw	DELAY
	movwf	DUMMY0
	call	DelayAL
	endm

;**********************************************************************
;    	DS1820 SubRoutine				   *
;**********************************************************************
DelayAL	nop			; Use for pausing macro
	nop
	decfsz	DUMMY0,f
	goto	DelayAL
	return

;**********************************************************************
;    	Reset DS1820						      *
;**********************************************************************
DS_Reset DQLOW
	PAUSE	0x77		; 600 microsecond delay
	DQHIZ
	PAUSE	0x0c		; Wait 67 microsecond for response bit
	nop
	nop
	movf	PORTA,w
	andlw	0x01		; Use RA0 only
	movwf	DUMMY1
	PAUSE	0x3b		; 300 microsecond delay
	movf	DUMMY1,w	; Response in W register
	return

;**********************************************************************
;    	Send Data To DS1820 (8 Bit)                                   *
;**********************************************************************
DS_Tx	movwf	DUMMY2		; Transmission data 
	movlw	0x08		; Prepare 8-bit counter for sending data
	movwf	DUMMY1		; Define loop counter
Tx_Loop	DQLOW			; Macro of DQ pin to low, This is start bit
	PAUSE	0x01		; 10 microsecond delay
	rrf	DUMMY2,f	; Rotate data to Carry flag
	btfsc	STATUS,C	; Test Carry flag
	bsf	DQ		; If Carry flag = "1" , set DQ to high
	PAUSE	0x0d		; 70 microsecond delay
	DQHIZ
	nop
	decfsz	DUMMY1,f	; 8 times ?
	goto	Tx_Loop		; No, send again
	return	

;**********************************************************************
;    	Recieve Data From DS1820 (8 Bit)		              *
;**********************************************************************
DS_Rx	movlw	0x08		; Recieve 8-bit data
	movwf	DUMMY1
Rx_Loop	DQLOW			; Macro of DQ pin to low, this is start bit
	PAUSE	0x01		; 10 microsecond delay
	DQHIZ			; Back to high for receiving
	nop
	nop
	movf	PORTA,w		; Read data
	andlw	0x01		; Get data bit 0 only
	addlw	0xff		; Move data bit 0 to Carry flag with addition method
	rrf	DUMMY2,f	; Move data bit 0 to DUMMY bit 7
	PAUSE	0x0b		; 60 microsecond delay
	decfsz	DUMMY1,f	; Loop 8 times
	goto	Rx_Loop		; Read again
	movf	DUMMY2,w	; Save data to W register
	return

;**********************************************************************
;    	Subroutine For Send Data To LCD	                              *
;**********************************************************************
Send_Temp movf	NUM3,w		; Read data from  DS1820
	btfsc	STATUS,Z	; Fisrt data = 0 ?
	movlw	0xe0		; If yes, convert to blank 0xE0+0x30 =0x20 (" ")
	addlw	0x30		; Convert number to ASCII		
	call	Send		; Send data to LCD
	movf	NUM2,w		; Read second digit
	addlw	0x30		; Convert to ASCII
	call	Send		;
	movf	NUM1,w
	addlw	0x30
	call	Send
	movlw	'.'		; 
	call	Send
	movlw	'0'
	btfsc	HALF,0		; Test half degree = [0] or [5]
	movlw	'5'
	call	Send
	movlw	'C'
	call	Send
	return

Set_Addr bcf	E		; Fix position on LCD 
	bcf	RS		; Send command
	call	Delay125	
	movf	ADDR,w		; Select address for sending data to LCD
	call	Send	
	bsf	RS
	call	Delay125		
	return

reset_LCD	macro	
	movlw	0x38		; Fisrt, send 8-bit mode command
	movwf	BITS
	call	Flip
	call	Pulse
	call	Delay125
	endm

Init_LCD bcf	E		; Set command mode
	bcf	RS
	call	Delay125
	reset_LCD
	reset_LCD
	reset_LCD
	reset_LCD
	movlw	0x28		; Send 4-bit mode command in MSB only
	movwf	BITS
	call	Flip
	call	Pulse
	call	Delay125
	movlw	0x28		; Set LCD mode to 4 bit mode, 2 line display, 5x7 dot
	call	Send
	movlw	0x0c		; Set LCD on, cursor off, cursor not blink
	call	Send
	movlw	0x01		; Clear display
	call	Send
	call	Delay5
	return

Send	movwf	BITS		; Send 8-bit data to LCD module 
	call	Flip		; 
	call	Pulse		; Send Upper 4-bit first and lower 4-bit following
	swapf	BITS,f	
	call	Flip
	call	Pulse
	call	Delay125
	return
	
Flip	bcf	PORTB,4		; Copy data from BITS register to RB4-RB7
	btfsc	BITS,4		; 
	bsf	PORTB,4		; Unchange other bit of PORTB
	bcf	PORTB,5		; 
	btfsc	BITS,5
	bsf	PORTB,5
	bcf	PORTB,6
	btfsc	BITS,6
	bsf	PORTB,6
	bcf	PORTB,7
	btfsc	BITS,7
	bsf	PORTB,7
	return
	

Pulse	bsf	E		; Generate enable pluse for LCD
	nop
	bcf	E
	call	Delay125
	return

Delay125 movlw	.42		; Delay 125 microsecond
	movwf	DUMMY1
	decfsz	DUMMY1,f
	goto	$-1
	return

Delay5	movlw	.41		; Delay 5 millisecond
	movwf	DUMMY2
D1	call	Delay125
	decfsz	DUMMY2,f
	goto	D1
	return 
 	
Delay200 clrf	DUMMY5		; Delay ~ 200 mSec For Key Bounce
	clrf	DUMMY6
	decfsz	DUMMY6,f
	goto	$-1
	decfsz	DUMMY5,f
	goto	$-3
	return	

;**********************************************************************
;    	Convert Hex to 3 Digit Decimal	                              *
;**********************************************************************
Convert	clrf	NUM2		; Clear register of 10's unit
Check	movlw	0x0A		; Subtract with 10 until the result lower 10
	subwf	DATA1,w		; Fisrt subtraction < 10 ?
	btfss	STATUS,C	
	goto	Less1		; If < 10 then return
	incf	NUM2,f		; If > 10 then increase 10's unit
	movlw	0x0A		; Subtract with 10 
	subwf	DATA1,f		; Test Again. Is it lower 10 ? 
	goto	Check

Less1	movf	DATA1,w		; If < 10, send data to 1's unit 
	movwf	NUM1		; 
	clrf	NUM3		; Clear 100's unit
Check2	movlw	0x0A
	subwf	NUM2,w		; 10's unit > 10 ?
	btfss	STATUS,C
	return			; If < 10 then return
	incf	NUM3,f
	movlw	0x0A		; If > 10 then subtraction again
	subwf	NUM2,f		; and check until the result is lower 10
	goto	Check2

	end
