;****************************************************************************************
;* PIC16F684 project													Date: 15.07.08	*
;*																		Rev.  22.03.10	*
;* FILE: Two-wire LCD.asm			2x16-Characters 					Name: GC		*
;*  																					*
;* 					 			+ADC	+BinDec-Converter								*
;*																						*
;*																						*
;* Uses Two-Wire LCD interface															*
;* 																						*
;* ADC-value is displayed as 2 digit decimal (Temperatur:)								*
;* 																						*
;****************************************************************************************

;		H'<HEX>'		D'<Decimal>'		B'<Binary>'			A'<ASCII>'

; 	Registers and constants are defined here:
INDF	equ H'00'	; Indirect Register (Uses content of FSR)
PCL		equ H'02'	; Program Counter Low byte address
STATUS	equ	H'03'	; Status-register
FSR		equ H'04'	; Indirect Data Memory Address Pointer
PORTA	equ	H'05'	; Input/output-register A
TRISA	equ	H'05'	; Bank-1, input/output configuration A
PORTC	equ	H'07'	; Input/output-register C
TRISC	equ	H'07'	; Bank-1, input/output configuration C
PIR1	equ	H'0C'	; EE-prom interuptflag EEIF = PIR1,7
ANSEL	equ H'11'	; Bank-1, Analog select register (A/D conversion Clock)
CMCON0	equ H'19'	; Comparator control register
EEDAT	equ	H'1A'	; Bank-1, EE-Prom data-register
EEADR	equ	H'1B'	; Bank-1, EE-Prom address-register
EECON1	equ H'1C'	; Bank-1, 
EECON2	equ	H'1D'	; Bank-1,
ADRESH	equ H'1E'	; ADC result
ADRESL	equ H'1E'	; Bank-1, ADC result
ADCON0	equ H'1F'	; ADC Control register 
ADCON1	equ H'1F'	; Bank-1, ADC Control register
	

; 	RAM-registers used by routines:
Ux		equ	H'20'	; Ux? (A/D Converter)
Low_	equ	H'21'	; Low_?
High_	equ H'22'	; High_?
Dly_1	equ H'23'	; Delay
Dly_2	equ H'24'	; Delay2
Tmp_1	equ H'25'	; Wr_Ch, Wr_Cmd, TxtOut, Dis_Pos
Tmp_2	equ H'26'	; Wr_Ch, Wr_Cmd, D_Tx
Tmp_3	equ H'27'	; D_Tx, Clr_Buf (Number of Buffer-bits)
Deg_Ref	equ	H'28'	; Deg_C (Offset to subtract from ADC-value)
Nr		equ	H'29'	; BinDec
x100	equ H'2A'	; BinDec
x10		equ H'2B'	; BinDec
x1		equ	H'2C'	; BinDec

Txt_Loc	equ H'2F'	; Txt_Out (Start location of Text)	Set by Txt_#1 and Txt_#2

;	 Definitions used:
#define	Max_	D'121'		; Maximum for Temperature-display
#define	Min_	D'20'		; Minimum for Temperature-display
#define	D_D		PORTA,4		; RA4: Data-input for Two-wire LCD-interface
#define	D_C		PORTA,5		; RA5: Clock-input for Two-wire LCD-interfacs

		list p=16F684

		org		H'05'		; Start location of code  (05)

;*************************************** Chip setup ***************************************


;Bank-1	
		bsf		STATUS,5	; Switch to bank-1 for setup

;PORTA
		movlw	B'11001111'	; RA0 is ADC-input
		movwf	TRISA		; RA5 and RA4 are digital outputs
							; RA2 and RA1 are digital inputs (Tristate)

;PORTC
		clrf	TRISC		; PORTC= Outputs (TRISC<5:0>= 000000)
							; Bit5=RS, Bit4=Strobe, Bit<3:0>=Data (Nibble)

;ADC-In
		movlw	B'00000001'	; RA0: ADC-Input, All other pins = digital I/O
		movwf	ANSEL		; 
		movlw	B'00010000' ; Bit<6:4> A/D Clock=Fosc/8
		movwf	ADCON1		; 

;Bank-0
		bcf		STATUS,5	; Switch back to bank-0

;Comperators off
		movlw	H'07'		; CM<2:0>=111	=>			!!! IMPORTANT !!!
		movwf	CMCON0		; Disable comparator, 		General purpose I/O

;ADC-Out
		movlw	B'00000000'	; Bit7=0:	0-------, Makes A/D Result Left justified (8 Bits used)
							; Bit6=0:	-0------, Vref for A/D =VDD
		movwf	ADCON0		; Bit<4:2>	---000--, Channel 0 selected: RA0/AN0= ADC-input
							; Bit1:		------1-, A/D start and Busy test bit
							; Bit0=1:	-------1, Turns on A/D Converter

;Ref.
		movlw	D'25'		; Low_	:	Temperature Low limit
		movwf	Low_		; 
		movlw	D'27'		; High_	:	Temperature High limit
		movwf	High_		; 
		movlw	D'21'		; Set Reference temperature (Used by Deg_C)
		movwf	Deg_Ref		; Offset-value

		clrf	PORTA		; PORTA Outputs are cleared 
		clrf	PORTC		; PORTC Outputs are cleared

		goto	Start

;************************************    Subroutines   ***********************************


; 		NB!		Text can also be written in EEPROM-Buffer using the ASCII-Field (View -> EEPROM -> )
;				and can be programed here directly.


		;	Characters used by Txt-Routines are loaded into EE-prom 
EE_Ld	bsf		STATUS,5	; Switch to Bank-1
		bsf		EECON1,2	; Write enable of EE-Prom
		movlw	H'00'		; Startaddress In EE-Prom
		movwf	EEADR

Txt_1	movlw	A'T'		; 		ASCII-Code !	Display-Characters are loaded into EE-Prom.
		call	EE_In
		movlw	A'w'		; 
		call	EE_In
		movlw	A'o'		; 
		call	EE_In
		movlw	A'-'		; 
		call	EE_In
		movlw	A'W'		; 	
		call	EE_In			
		movlw	A'i'		;
		call	EE_In
		movlw	A'r'		;
		call	EE_In
		movlw	A'e'		; 
		call	EE_In
		movlw	A' '		; 
		call	EE_In
		movlw	A'L'		; 
		call	EE_In
		movlw	A'C'		; 
		call	EE_In
		movlw	A'D'		; 
		call	EE_In
		movlw	A'*'		; *		<- Star: ends read-out (Se: Txt_Out)
		call	EE_In
		movlw	A' '		; 
		call	EE_In
		movlw	A' '		; 
		call	EE_In
		movlw	A' '		; 
		call	EE_In
		movlw	A' '		; 
		call	EE_In

Txt_2	movlw	A'T'
		call	EE_In
		movlw	A'e'
		call	EE_In
		movlw	A'm'
		call	EE_In
		movlw	A'p'
		call	EE_In
		movlw	A'e'
		call	EE_In
		movlw	A'r'
		call	EE_In
		movlw	A'a'
		call	EE_In
		movlw	A't'
		call	EE_In
		movlw	A'u'
		call	EE_In
		movlw	A'r'
		call	EE_In
		movlw	A':'
		call	EE_In
		movlw	A'*'
		call	EE_In
		bcf		EECON1,2	; Disable EE-Write
		bcf		STATUS,5	; Switch back to Bank-0
		return

EE_In	movwf	EEDAT		; Character in W-register is stored EEDAT-register
		movlw	H'55'		; Unlock write EE-Prom sequense
		movwf	EECON2		; -
		movlw	H'AA'		; -
		movwf	EECON2		; -
		bsf		EECON1,1	; Start the write
		bcf		STATUS,5	; Switch to Bank-0 where PIR1 is
WR_Lp	btfss	PIR1,7		; Wait here until Write is complete
		goto	WR_Lp		
		bcf		PIR1,7		; Clear EEIF (Bit7: Write EE-Prom interuptflag)
		bsf		STATUS,5	; Switch back to Bank-1
		incf	EEADR,f		; Point to next character
		return



;WWWWWWWWWWWWWWWW Routines for writing and controling the Two-wire display WWWWWWWWWWWWWW

		;	Short delays used by:	Delay2, Ux?, Strobe, Various Display-routines
Delay	movwf	Dly_1		; Store W in Dly_1 (23H)
lp_d	decfsz	Dly_1,f		;   
		goto	lp_d		; 
		return

		; 	Long delays used by:	Various Display-routines
Delay2	movwf	Dly_2		; Store W in Dly_2 (24H)
		movlw	H'00'		
lp_d2	call	Delay		; Dly_2 * 772uS
		decfsz	Dly_2,f
		goto	lp_d2
		return

		;	Writes a command to the Display-unit
Wr_Cmd	movwf	Tmp_1		; Save a copy
		swapf	Tmp_1,w		; High nibble first
		andlw	H'0F'		; Preparation of Enable-bit 
		addlw	H'20'		; Command-mode (RS=0)
		movwf	Tmp_2		; Modified copy to Working registe
		call	D_Tx		; Send first part
Wr_Cmd2	movf	Tmp_1,w		; Get data for second part
		andlw	H'0F'		; Preparation of Enable-bit 
		addlw	H'20'		; Command-mode (RS=0)
		movwf	Tmp_2		; Modified copy to Working register 
		call	D_Tx		; Send second part
		movlw	H'05'		; Wait (50 uS)
		call	Delay
		return

		;	Writes a character to the Display-unit
Wr_Ch	movwf	Tmp_1		; Save a copy
		swapf	Tmp_1,w		; High nibble first
		andlw	H'0F'		; Preparation of Enable-bit 
		addlw	H'30'		; Charactermode (RS=1)
		movwf	Tmp_2		; Modified copy to Working registe
		call	D_Tx		; Send first part
		movf	Tmp_1,w		; Get data for second part
		andlw	H'0F'		; Preparation of Enable-bit 
		addlw	H'30'		; Charactermode (RS=1)
		movwf	Tmp_2		; Modified copy to Working register 
		call	D_Tx		; Send second part
		movlw	H'05'		; Wait (50 uS)
		call	Delay
		return

		;	Loads prepared data (6 bits) into serial-interface display-buffer, used by: Wr_Ch
D_Tx	call	Clr_Buf		; Clear the Display-buffer
		movlw	H'06'		; Number of buffered bits 
		movwf	Tmp_3		; 
H_L_?	btfsc	Tmp_2,5		; IF:		Most significant bit is not low
		goto	D_H			; THEN:		Go to D_H
D_L		bcf		D_D			; ELSE:		Set display data-line Low
		goto	D_X			; Go - transmit current bit
D_H		bsf		D_D			; Set display data-line High
D_X		call	D_Ck		; Clock data into display-buffer
		rlf		Tmp_2,f		; Rotate left for next bit
		decfsz	Tmp_3,f		; All done ?
		goto	H_L_?		; Get status of next bit 
		bcf		D_D			; Set display data-line low
		call	D_Ck		; Clock Data one more step forward
		
E_P		bsf		D_D			; Pulse E-line to transfer data to display-unit	(målt: 15us)
		movlw	H'03'
		call	Delay
		bcf		D_D
		return

		;	Generates a clock-pulse to Two-wire LCD. Used by: D_Tx
D_Ck	bcf		D_C			; Set Display_Clock-line low
		movlw	H'03'		; 
		call	Delay		; Wait (x9 uS)
		bsf		D_C			; Set Display_Clock-line high
		movlw	H'06'		; Wait (x9 uS)
		call	Delay
		bcf		D_C			; Set Display_Clock-line low
		movlw	H'03'		; Wait (x9 uS)
		call	Delay
		return

		;	Clears buffer for Two-wire LCD
Clr_Buf	bcf		D_D			; Set Display Data-line low, 
		movlw	H'08'		; All 8 bits are cleared
		movwf	Tmp_3
CB_lp	call	D_Ck
		decfsz	Tmp_3,f
		goto	CB_lp
		return


;IIIIIIIIIIIIIIIIIIIIII Routines for initialising and Display set-up IIIIIIIIIIIIIIIIIIII

		;	Setup-routine for Display-unit.
Dis_Ini	call	Clr_Buf		; Clear display-buffer

		movlw	H'43'		; Delay after power on (50mS)
		call	Delay2		; Display "wakes up"
		movlw	B'0010'		; 4-bit mode 0,0,1,DL
		movwf	Tmp_1
		call	Wr_Cmd2		; NB!	Only the first 4 bits is needed to select 4-bit mode
		movlw	H'06'		; Wait (4,6mS)
		call	Delay2

Fnc_Set	movlw	B'00101000'	; Switch display to 4-bit mode, 2 display-lines in use
		call	Wr_Cmd		; 4-bit mode 0,0,1,DL	N,F,-,-
		movlw	H'10'		; Wait (50uS)
		call	Delay

Disp_On	movlw	B'00001100'	; Turning ON the Display 1,D,C,B, (1=ON, 0=OFF)
		call	Wr_Cmd		; D: Display ON,	C: Cursor OFF,	B: Blink OFF
		movlw	H'10'		; Wait (50uS)
		call	Delay

Ent_Mod	movlw	B'00000110'	; Entry mode
		call	Wr_Cmd		; 0,1,I/D,S		I/D: Increment/Decrement,	S: Shift display
		movlw	H'10'		; Wait (50 uS)
		call	Delay
		return


Dis_Off	movlw	B'00001000'	; Blanks Display!	RS=0 & Data=0	1,D,C,B,	(1=ON, 0=OFF)
		call	Wr_Cmd		; D: Display OFF,	C: Cursor OFF,	B: Blink OFF
		movlw	H'10'		; Wait (50uS)
		call	Delay
		return

Dis_Clr	movlw	B'00000001'	; Display is cleared, cursor home at start (00)
		call	Wr_Cmd		; RS=0 & Data=0
		movlw	H'06'		; Wait (4,6mS)
		call	Delay2
		return

Cur_Hom	movlw	B'00000010'	; Cursor home at start (00), text remains on display
		call	Wr_Cmd		; RS=0 & Data=0
		movlw	H'06'		; Wait (4,6mS)
		call	Delay2
		return


;wwwwwwwwwwwwwwwwwwwwwwwwwwwwww Text-handeling routines wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww

		;	Value in W holds cursor position for text start on display-unit
Dis_Pos	addlw	H'80'		; W holds the position. Command-mode (Bit7 must be set)
		movwf	Tmp_1		; Save a copy in Tmp_1
		call	Wr_Cmd
		return

		;	Writes text-string i EE-Prom to Display-unit. Trailing * ends read-out.
Txt_Out	movf	Txt_Loc,w	; Get text location address in EE-Prom
		bsf		STATUS,5	; Switch to Bank-1 to read EEPROM-data
		movwf	EEADR		; 
Next_C	bsf		EECON1,0	; Initiate Reading of a character in EE-Prom
		movf	EEDAT,w		; Get the character
		sublw	A'*'		; Test for last character = *
		btfsc	STATUS,2	; IF:	Character = * (Z=1)
		goto	Txt_End		; THEN:	End read-out
		movf	EEDAT,w		; Get EE-Data for display
		bcf		STATUS,5	; Bank-0
		call	Wr_Ch		; ELSE:	Write Character to Display-unit
		bsf		STATUS,5	; Bank-1
		incf	EEADR,f		; Move pointer to next character
		goto	Next_C		; Continue
Txt_End	bcf		STATUS,5	; Bank-0
		return

Txt_#1	movlw	H'00'		; Text_1 start-position on display
		call	Dis_Pos
		movlw	H'00'		; Address in EE-Prom where Text_1 is Located
		movwf	Txt_Loc
		call	Txt_Out
		return

Txt_#2	movlw	H'40'		; Text_2 start-position on display
		call	Dis_Pos
		movlw	H'11'		; Address in EE-Prom where Text_2 is Located
		movwf	Txt_Loc
		call	Txt_Out
		return


;ddddddddddddddddddddddddddddddd Data-handeling routines dddddddddddddddddddddddddddddddd


		;	Reads ADC-value
Ux?		bsf		ADCON0,0	; Turn on A/D Converter
		movlw	D'4'		; W contains delay-value for Dly_1 (23h)
		call	Delay		; Delay
		bsf		ADCON0,1	; GO, Start A/D conversion
Ux_lp	btfsc	ADCON0,1	; Done ?	(Bit1: A/D Start and Busy test-bit)
		goto	Ux_lp		; No, try again
		movf	ADRESH,w	; Yes, 
		movwf	Ux			; Store result in Ux
		bcf		ADCON0,0	; Turn off A/D Converter
		return				; Go back to Main program

	
DisTemp		;	Checks if Ux is below Min_ value
Dis_L	movf	Ux,w		; Get Ux to W
		sublw	Min_		; Subtract Ux from Min_		(Min_ = D'21')
		btfss	STATUS,0	; IF:		Ux < Min_	w<f =>	C-bit=1
		goto	Dis_H		; THEN:		Check if high limit is exeded
		goto	Dis_E		; ELSE:		Display -- for error
			;	Checks if Ux exceeds Max_ value
Dis_H	movlw	Max_		; Get Max_ to W
		subwf	Ux,w		; Subtract Max_ from Ux		(Max_ = D'121')
		btfss	STATUS,0	; IF:		Ux > Max_	w<f	=>	C-bit=1
		goto	Dis_Tp		; THEN:		Go display the temperature
		goto	Dis_E		; ELSE:		Display -- for error
			;	Displays temperature as 2 Decimal characters
Dis_Tp	call 	Deg_C		; Deg_C converts 8-bits to a 3-digit Decimal number.
		movlw	H'4C'		; -> Position 4C 	(At the end of Display-line 2)
		call	Dis_Pos		; 
		movf	x10,w
		call	Wr_Ch
		movf	x1,w
		call	Wr_Ch
		movlw	H'DF'		; DF= Degree character in ASCII table
		call	Wr_Ch
		movlw	A'C'		; C: for Celcius
		call	Wr_Ch
		return
Dis_E   movlw	H'4C'		; Displays -- ,		meaning ouside range from Min_ to Max_
		call	Dis_Pos		; -> Position 4C 	(End of Display-line 2)
		movlw	A'-'
		call	Wr_Ch
		movlw	A'-'
		call	Wr_Ch
		movlw	H'DF'		; DF= Degree character
		call	Wr_Ch
		movlw	A'C'		; C: for Celcius
		call	Wr_Ch
		return

		;	This routine calculates a binary value for temperature and convert it to decimal.
Deg_C	Call	Ux?	
		movf	Deg_Ref,w	; Get Reference temperature (Deg_Ref=Offset-value)
		subwf	Ux,w		; Subtract Deg_Ref from w 
Nr_Out	movwf	Nr
		call	BinDec		; Convert it to decimal.
		return


		;	Converts one byte to a 3-digit Decimal number.
BinDec	clrf	x100		; Reset Registers.
		clrf	x10			; The value to convert must be in register: Nr
		clrf	x1			;
Dig_3	movlw	D'100'		;
		subwf	Nr,w		; Subtract 100 from Nr 
		btfss	STATUS,0	;
		goto	Dig_2		;
		movwf	Nr			; Remainder in w to Nr	 	W -> Nr
		incf	x100,f		; Number of hundred's
		goto	Dig_3		;

Dig_2	movlw	D'10'		;
		subwf	Nr,w		; Subtract 10 from Nr
		btfss	STATUS,0	;
		goto	Dig_1		;
		movwf	Nr			; Remainder in w to Nr		W -> Nr
		incf	x10,f		; Number of ten's
		goto	Dig_2		;

Dig_1	movf	Nr,w		; 
		movwf	x1			; Remainder goes in register: x1 

Prep_N	movf	x100,w		; Preparing characters for display
		andlw	H'0F'		; (Numbers starting with 3 	Eks: D'5' => H'35')
		iorlw	H'30'		;
		movwf	x100		;
		movf	x10,w		;
		andlw	H'0F'		; And w with 00001111					H'0F'
		iorlw	H'30'		; Or w with  00110000					H'30'
		movwf	x10			; To make it a Number					H'3X'
		movf	x1,w		;
		andlw	H'0F'		;
		iorlw	H'30'		;
		movwf	x1			;
		return

;************************************* Main program *************************************

Start						; Make shure Text is in EE-Prom !!!
		call	EE_Ld		; Writes text to EE-Prom
							; Text can also be written in the ASCII field -
							; in the EE-Prom window prior to programming.
		call	Dis_Ini		; Display-unit setup
		call	Txt_#1		; Write Text_1
		call	Txt_#2		; Write Text_2
New		call	Ux?			; Read ADC!
		call	DisTemp		; Displays temperature
		goto	New			; New measurement

		end					; Program ends here


;*********************************** Programming a Device *******************************

;	  Select:
;
;	1.Configure		Select Device...	16f684
;	2.Configure		Configuration Bits	Clock:								Internal-RC,-
;																			No Clock
;
;										WatchDogTimer:						Off
; 										Master Clear Enable:				Internal
; 										Internal External Switch Over Mode:	Disabled
; 										Monitor Clock Fail-safe: 			Disabled
;
;	3.Project		Quickbuild 	*.asm
;	4.Programmer	Select Programmer	(PICkit 2)
;	5.Programmer	Program Device 		(Or Yellow Icon)



; 	Temperature sensor: 10k NTC in series vith 10k trim (temp. adjust).

