; WRITTEN BY  PETER BIRNIE
; COPYRIGHT  P. B. MICRO DESIGNS - 01494 531709
; DATE   11.8.95
; ITERATION  1.0
; FILE SAVED AS  DOTLCD.ASM
; FOR   PIC16C74-04/P  WDT=off CP=off
; CLOCK   4.00 MHz RESONATOR
; INSTRUCTION CLOCK 1.00 MHz T= 1us




 TITLE "DOTLCD.asm - LCD dot matrix driver and string handler"


 LIST   P=16C74



;----------------------------------------------------------------------------


; Program handle all aspects of setup and display on a dot matrix lcd
; display. Routines are provided to allow display of strings or
; characters, place the cursor and clear the display.



;----------------------------------------------------------------------------

;   Register File Assignment

;----------------------------------------------------------------------------



 INCLUDE "P16Cxx.INC"




;----------------------------------------------------------------------------

;     Physical Port Assignment

;----------------------------------------------------------------------------



LCDPORT	EQU	RA	; Port A - LCD




;----------------------------------------------------------------------------

;     Physical Bit Assignment

;----------------------------------------------------------------------------





 ; A port assignment

LCDRS	EQU	4	; Lcd register select output

LCDEN	EQU	5	; Lcd enable output





;----------------------------------------------------------------------------

;     Constant Assignment

;----------------------------------------------------------------------------




MEMBAS	EQU	20h	; Base address of user file registers

CLKR	EQU	100h	; Roll over value for rtc - subtract count
    ; value from this to preset count down time.


DIV256	EQU	%00000111	; 256us prescale setting


  ; LCD commands

LCDCLER	EQU	%00000001	; Clears display, resets curcor
LCDCM	EQU	%10000000	; Sets cursor using bits 0 - 6
    ; Line 1 range - 0 to .15
    ; Line 2 range - .40 to .55

    ; NOTE that subroutine LCDCUR2 take the
    ; value in W in range 0 - 15 and adjusts for
    ; line 2 offset.



;----------------------------------------------------------------------------

;   Variable Assignment

;----------------------------------------------------------------------------



GP1	EQU	MEMBAS	; General purpose register


; LCD variables here

STRNUM	EQU	GP1+1

CHPT	EQU	STRNUM+1	; Character string pointer in string.

LCDCH	EQU	CHPT+1	; Saves byte to be sent to lcd while
    ; it is processed.

TABOFF	EQU	LCDCH+1	; Table ofset pointer in lcd string lookup






;--------------------------------------------------------------------------

;    Macros

;--------------------------------------------------------------------------




;******
; CLKLCD clocks data/command to the lcd by making the EN line
; high then low.

CLKLCD MACRO
	SETB	LCDPORT.LCDEN	; Lcd enable LOW
	CLRB	LCDPORT.LCDEN	; Lcd enable HIGH
 ENDM


;******
; TABSET sets up the lcd table offset pointer before string output
; starts

TABSET MACRO
	MOV	W, #%11111111	; Offset is incremented on each call to
    ; table - first call must generate zero value
    ; offset.
	MOV	TABOFF, W
 ENDM


;******
; POINT increments the string pointer offset and adds it to the
; PLC ready for string lookup

POINT MACRO
	INC	TABOFF
	MOV	W, TABOFF
	ADD	PC, W
 ENDM



;******
; POINT8 increments the string pointer offset and adds it to the
; PLC ready for string lookup. Sets PCLATH to 8

POINT8 MACRO
	MOV	W, #8
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
; MOVWF PCLATH
	MOV	PCLATH, W
	INC	TABOFF
	MOV	W, TABOFF
	ADD	PC, W
 ENDM


;******
; POINT9 increments the string pointer offset and adds it to the
; PLC ready for string lookup. Sets PCLATH to 9

POINT9 MACRO
	MOV	W, #9
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
; MOVWF PCLATH
	MOV	PCLATH, W
	INC	TABOFF
	MOV	W, TABOFF
	ADD	PC, W
 ENDM



;******
; TSTRTC moves TIMER0 to W reg and sets ZERO status

TSTRTC MACRO
	MOV	W, RTCC	; Test for timeout
 ENDM


;******
; BANK0 selects register file bank 0

BANK0 MACRO
	CLRB	STATUS.RP0	; Select BANK 0
 ENDM


;******
; BANK1 selects register file bank 1

BANK1 MACRO
	SETB	STATUS.RP0	; Select BANK 1
 ENDM


;******
; PAGE0 selects rom page 0

PAGE0 MACRO
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;        BCF     PCLATH,3      ; Select rom page 0
	CLRB	PCLATH.3	; Select rom page 0
 ENDM


;******
; PAGE1 selects rom page 1

PAGE1 MACRO
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;        BSF     PCLATH,3      ; Select rom page 1
	SETB	PCLATH.3	; Select rom page 1
 ENDM



;--------------------------------------------------------------------------

;    Vectors

;--------------------------------------------------------------------------


;******
; Reset vector

	ORG	000

	JMP	INIT	; Do a cold start on power up


;******
; Interrupt vector - timer, keypad

	ORG	004

	JMP	INIT	; No interrupt so reset



;--------------------------------------------------------------------------

;       Subroutines

;--------------------------------------------------------------------------



;******
; STRING sends the string with number in W register - to the lcd

STRING
	MOV	STRNUM, W
 TABSET   ; Xero the offset
 PAGE1
	CALL	DOSTR	; Character from string
 PAGE0   ; Restore rom page

	RET


;******
; BCDLCD sends the bcd character in W to lcd

BCDLCD	OR	W, #'00110000'	; Convert to ascii

;******
; PUTLCD sends the ASCII character in W to lcd

PUTLCD
	MOV	LCDCH, W
 PAGE1
	CALL	CHALCD	; Character to lcd
 PAGE0   ; Restore rom page

	RET



;******
; CUR1 and CUR2 are indirect subroutine calls to LCDCUR1/2
; Enter with character placement in W in range 0 - 15

CUR1
 PAGE1
	CALL	LCDCUR1	; Line 1 please
 PAGE0

	RET

CUR2
 PAGE1
	CALL	LCDCUR2	; Line 2 please
 PAGE0

	RET

;******
; LCDCLR is an indirect call to LCDCLR1 on page 1

LCDCLR
 PAGE1
	CALL	LCDCLR1
 PAGE0

	RET


;******
; WAITGP waits for the number of ms in GP1

WAITGP	MOV	W, #CLKR-4	; 1ms timeout in TIMER0
	MOV	RTCC, W

WAITGPA TSTRTC
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
 BNZ WAITGPA  ; Wait for rtc to time out
	DEC	GP1
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
 BNZ WAITGP  ; Loop until delay is complete
	RET	; Exit from WAITGP




;--------------------------------------------------------------------------

;       Setup

;--------------------------------------------------------------------------




;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;INIT CLRF PCLATH
INIT	CLR	PCLATH

 ;******
 ;
 ; Set up peripheral control registers.


 ; Set port data directions: unused port pins set to outputs.


 ; A port setup

 ; Outputs
 ;  RA0 - 3  lcd data
 ;  RA4  lcd register select
 ;  RA5  lcd enable

	CLR	LCDPORT

 BANK1
	MOV	W, #%00000111	; Select port A as digital
	MOV	ADCON1, W
	MOV	W, #%00000000	; Set port data directions
;*** WARNING: TRIS registers are accessed by MOV !Rx, W (M = $0F or $1F). 
; MOVWF TRISA
	MOV	TRISA, W
 BANK0




 ;******
 ; Set up timer options

 ; Timer0 is set for internal, 1/4 ms pre scale

	MOV	W, #DIV256	; Set RTC0 for 1/4 ms tick
 BANK1
;*** WARNING: OPTION register is accessed only by MOV !OPTION, W. 
; MOVWF OPTION_REG
	MOV	OPTION_REG, W
 BANK0


 ;******
 ; Reset the lcd

INITA	MOV	W, #20	; Delay for 20 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000011	; Reset

	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #6	; Delay to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000011	; Reset

	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000011	; Reset

	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000010	; 4 bit interface
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000010	; 4 bit interface
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #%00001000	; 2 LINES, 5*7
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000000	; Display OFF
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #%00001000	;
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000000	; Display clear
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #%00000001	;
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000000	; Entry mode set
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #%00000110	;
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd

	MOV	W, #%00000000	; Display on
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #%00001100	;
	MOV	LCDPORT, W	; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

	MOV	W, #5	; Delay for 5 ms to let lcd settle
	MOV	GP1, W
	CALL	WAITGP	; Wait for lcd



;--------------------------------------------------------------------------

;       Program start

;--------------------------------------------------------------------------



  ; Display demonstration banner


IDLE
	CALL	LCDCLR	; Not really required here as display has
    ; just been set up!

	MOV	W, #3
	CALL	CUR1	; Position text on line 1

	MOV	W, #1	; 'Microchip' - NOTE that the string is on
    ; second page of ROM at 0X800.
	CALL	STRING

  ; Move cursor for line 2 of banner

	MOV	W, #0
	CALL	CUR2	; Position text on line 2

	MOV	W, #3	; '16C74 drives lcd' - NOTE that the string
    ; is on second page of ROM at 0x900.

	CALL	STRING



IDLE1	JMP	IDLE1	; Just loop here please




;--------------------------------------------------------------------------

;   Second page of ROM

;--------------------------------------------------------------------------




; Text strings and control subroutines must be stored on the correct
; page or this pcogram WILL NOT FUNCTION CORRECTLY.

; Only move code around if you are absoluteley clear as to what you
; are doing!



	ORG	$800


LAST	EQU	%10000000	; Value to add to last character in a string


;******
; DOSTR uses CALLSTR table to direct flow to the correct lookup table
; for lcd strings. Gets string characters and outputs to lcd

DOSTR

	MOV	W, #8
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
; MOVWF PCLATH  ; Keep the pageing ok
	MOV	PCLATH, W	; Keep the pageing ok
	MOV	W, STRNUM	; Get the string number
	CALL	CALLSTR

	MOV	LCDCH, W
	CALL	CHALCD	; Character to lcd
	SB	LCDCH.7	; Test if end of string flagged - bit 7 set
	JMP	DOSTR	; Loop until all sent

	RET

;******
; CALLSTR is the dispatch table for lcd string lookup

CALLSTR	ADD	PC, W
	NOP
	JMP	MSG1	; 'Microchip'
	JMP	MSG2	; 'On page 9'

 ; more jumps can be added here

	JMP	MSG3	; '16C74 drives lcd'


;******
;   THE TEXT STRINGS!


MSG1 POINT8  ; Use macro POINT8 when the string is in 0x800
   ; address area

	RETW	#'M'
	RETW	#'i'
	RETW	#'c'
	RETW	#'r'
	RETW	#'o'
	RETW	#'c'
	RETW	#'h'
	RETW	#'i'
	RETW	#'p'+LAST



MSG2 POINT8

	RETW	#'O'
	RETW	#'n'
	RETW	#' '
	RETW	#'P'
	RETW	#'a'
	RETW	#'g'
	RETW	#'e'
	RETW	#' '
	RETW	#'8'+LAST


	ORG	$900


MSG3 POINT9  ; Use macro POINT9 when the string is in 0x900
   ; address area

	RETW	#'1'
	RETW	#'6'
	RETW	#'C'
	RETW	#'7'
	RETW	#'4'
	RETW	#' '
	RETW	#'d'
	RETW	#'r'
	RETW	#'i'
	RETW	#'v'
	RETW	#'e'
	RETW	#'s'
	RETW	#' '
	RETW	#'l'
	RETW	#'c'
	RETW	#'+LAST



;--------------------------------------------------------------------------

;   Subroutines to handle the lcd

;--------------------------------------------------------------------------


;  NOTE: these must be on page 1 of rom
;  ------------------------------------

;*****
; CHALCD writes the character in W register to the lcd.
; On entry, the display character is in LCDCH.


CHALCD
 ; Get the upper nibble and load to the lcd port

	MOV	W, <>LCDCH	; Get the ms nibble to ls position
	AND	W, #07H	; Strip to ms nibble in ls position,
    ; and mask top bit in case this is the lset
    ; character in a string
	MOV	LCDPORT, W	; Data to lcd port
	SETB	LCDPORT.LCDRS	; Select lcd data

  ; Clock the ms nibble to the lcd

 CLKLCD

 ; Get the lower nibble and load to the lcd port

	MOV	W, LCDCH	; Get back the character
	AND	W, #0FH	; Strip to ms nibble in ls position
	MOV	LCDPORT, W	; Data to lcd port
	SETB	LCDPORT.LCDRS	; Select lcd data

  ; Clock the ls nibble to the lcd.

 CLKLCD

	CLRB	LCDPORT.LCDRS

	JMP	LCD64	; Delay then exit from CHALCD


;*****
; COMLCD writes the command in W register to the lcd
; On entry, the command character is in LCDCH.


COMLCD
 ; Get the upper nibble and load to the lcd port

	MOV	W, <>LCDCH	; Get the ms nibble to ls position
	AND	W, #0FH	; Strip to ms nibble in ls position
	MOV	LCDPORT, W	; Command to lcd port

  ; Clock the ms nibble to the lcd

 CLKLCD

 ; Get the lower nibble and load to the lcd port

	MOV	W, LCDCH	; Get back the character
	AND	W, #0FH	; Strip to ms nibble in ls position
	MOV	LCDPORT, W	; Command to lcd port

  ; Clock the ls nibble to the lcd.

 CLKLCD

	JMP	LCD2	; Delay then exit from COMLCD


;******
; LCDCL1R clears the display and resets the cursor

LCDCLR1	MOV	W, #LCDCLER	; LCD clear command
	MOV	LCDCH, W
	JMP	COMLCD


;******
; LCDCUR1 sets cursor to position on line 1 using value in W register

LCDCUR1	OR	W, #LCDCM	; Cursor move command
	MOV	LCDCH, W
	JMP	COMLCD


;******
; LCDCUR2 sets cursor to position on line 2 using value in W register

LCDCUR2	OR	W, #LCDCM	; Cursor move command
	MOV	LCDCH, W
	SETB	LCDCH.6	; This bit set for line 2
	JMP	COMLCD


;******
; LCD64/2 gives a delay of 64us or 1.6ms while the lcd accepts the
; data or command.
; Uses TMR0 - prescale is set to 16us in OPTION.

LCD64	MOV	W, #CLKR-3	; Gives 48/64 us delay
	JMP	LCDEL

LCD2	MOV	W, #CLKR-100	; Gives 1.6ms delay

LCDEL	MOV	RTCC, W	; Set TMR0 prescalar

LCDELA TSTRTC
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
 BNZ LCDELA  ; Wait for TMR0 to time out

	RETW	#0	; Exit from CHA/COMLCD routines



 END