; 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 PORTA  ; 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 b'00000111' ; 256us prescale setting


  ; LCD commands

LCDCLER EQU b'00000001' ; Clears display, resets curcor
LCDCM EQU b'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
 BSF     LCDPORT,LCDEN ; Lcd enable LOW
 BCF     LCDPORT,LCDEN ; Lcd enable HIGH
 ENDM


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

TABSET MACRO
 MOVLW b'11111111' ; Offset is incremented on each call to
    ; table - first call must generate zero value
    ; offset.
 MOVWF TABOFF
 ENDM


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

POINT MACRO
 INCF TABOFF
 MOVFW TABOFF
 ADDWF PCL
 ENDM



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

POINT8 MACRO
 MOVLW .8
 MOVWF PCLATH
 INCF TABOFF
 MOVFW TABOFF
 ADDWF PCL
 ENDM


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

POINT9 MACRO
 MOVLW .9
 MOVWF PCLATH
 INCF TABOFF
 MOVFW TABOFF
 ADDWF PCL
 ENDM



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

TSTRTC MACRO
 MOVFW TMR0  ; Test for timeout
 ENDM


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

BANK0 MACRO
        BCF     STATUS,RP0 ; Select BANK 0
 ENDM


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

BANK1 MACRO
        BSF     STATUS,RP0 ; Select BANK 1
 ENDM


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

PAGE0 MACRO
        BCF     PCLATH,3      ; Select rom page 0
 ENDM


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

PAGE1 MACRO
        BSF     PCLATH,3      ; Select rom page 1
 ENDM



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

;    Vectors

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


;******
; Reset vector

 ORG 000

 GOTO INIT  ; Do a cold start on power up


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

 ORG 004

 GOTO INIT  ; No interrupt so reset



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

;       Subroutines

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



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

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

 RETURN


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

BCDLCD IORLW '00110000' ; Convert to ascii

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

PUTLCD
 MOVWF LCDCH
 PAGE1
 CALL CHALCD  ; Character to lcd
 PAGE0   ; Restore rom page

 RETURN



;******
; 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

 RETURN

CUR2
 PAGE1
 CALL LCDCUR2  ; Line 2 please
 PAGE0

 RETURN

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

LCDCLR
 PAGE1
 CALL LCDCLR1
 PAGE0

 RETURN


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

WAITGP MOVLW CLKR-4  ; 1ms timeout in TIMER0
 MOVWF TMR0

WAITGPA TSTRTC
 BNZ WAITGPA  ; Wait for rtc to time out
 DECF GP1
 BNZ WAITGP  ; Loop until delay is complete
 RETURN   ; Exit from WAITGP




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

;       Setup

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




INIT CLRF 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

 CLRF LCDPORT

 BANK1
 MOVLW b'00000111' ; Select port A as digital
 MOVWF ADCON1
 MOVLW b'00000000' ; Set port data directions
 MOVWF TRISA
 BANK0




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

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

 MOVLW DIV256  ; Set RTC0 for 1/4 ms tick
 BANK1
 MOVWF OPTION_REG
 BANK0


 ;******
 ; Reset the lcd

INITA MOVLW .20  ; Delay for 20 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000011' ; Reset

 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .6  ; Delay to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000011' ; Reset

 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000011' ; Reset

 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000010' ; 4 bit interface
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000010' ; 4 bit interface
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW b'00001000' ; 2 LINES, 5*7
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000000' ; Display OFF
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW b'00001000' ;
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000000' ; Display clear
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW b'00000001' ;
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000000' ; Entry mode set
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW b'00000110' ;
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd

 MOVLW b'00000000' ; Display on
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW b'00001100' ;
 MOVWF   LCDPORT  ; Command to lcd port
 CLKLCD   ; Clock the nibble to the lcd

 MOVLW .5  ; Delay for 5 ms to let lcd settle
 MOVWF GP1
 CALL WAITGP  ; Wait for lcd



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

;       Program start

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



  ; Display demonstration banner


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

 MOVLW .3
 CALL CUR1  ; Position text on line 1

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

  ; Move cursor for line 2 of banner

 MOVLW .0
 CALL CUR2  ; Position text on line 2

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

 CALL STRING



IDLE1 GOTO 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 0x800


LAST EQU b'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

 MOVLW .8
 MOVWF PCLATH  ; Keep the pageing ok
 MOVFW STRNUM  ; Get the string number
 CALL CALLSTR

 MOVWF LCDCH
 CALL CHALCD  ; Character to lcd
 BTFSS LCDCH,7  ; Test if end of string flagged - bit 7 set
 GOTO DOSTR  ; Loop until all sent

 RETURN

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

CALLSTR ADDWF PCL
 NOP
 GOTO MSG1 ; 'Microchip'
 GOTO MSG2 ; 'On page 9'

 ; more jumps can be added here

 GOTO MSG3 ; '16C74 drives lcd'


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


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

 RETLW 'M'
 RETLW 'i'
 RETLW 'c'
 RETLW 'r'
 RETLW 'o'
 RETLW 'c'
 RETLW 'h'
 RETLW 'i'
 RETLW 'p'+LAST



MSG2 POINT8

 RETLW 'O'
 RETLW 'n'
 RETLW ' '
 RETLW 'P'
 RETLW 'a'
 RETLW 'g'
 RETLW 'e'
 RETLW ' '
 RETLW '8'+LAST


 ORG 0x900


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

 RETLW '1'
 RETLW '6'
 RETLW 'C'
 RETLW '7'
 RETLW '4'
 RETLW ' '
 RETLW 'd'
 RETLW 'r'
 RETLW 'i'
 RETLW 'v'
 RETLW 'e'
 RETLW 's'
 RETLW ' '
 RETLW 'l'
 RETLW 'c'
 RETLW 'd''+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

 SWAPF   LCDCH,W  ; Get the ms nibble to ls position
 ANDLW   07H  ; Strip to ms nibble in ls position,
    ; and mask top bit in case this is the lset
    ; character in a string
 MOVWF   LCDPORT  ; Data to lcd port
 BSF LCDPORT,LCDRS ; Select lcd data

  ; Clock the ms nibble to the lcd

 CLKLCD

 ; Get the lower nibble and load to the lcd port

 MOVFW   LCDCH  ; Get back the character
 ANDLW   0FH  ; Strip to ms nibble in ls position
 MOVWF   LCDPORT  ; Data to lcd port
 BSF LCDPORT,LCDRS ; Select lcd data

  ; Clock the ls nibble to the lcd.

 CLKLCD

 BCF LCDPORT,LCDRS

 GOTO 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

 SWAPF   LCDCH,W  ; Get the ms nibble to ls position
 ANDLW   0FH  ; Strip to ms nibble in ls position
 MOVWF   LCDPORT  ; Command to lcd port

  ; Clock the ms nibble to the lcd

 CLKLCD

 ; Get the lower nibble and load to the lcd port

 MOVFW   LCDCH  ; Get back the character
 ANDLW   0FH  ; Strip to ms nibble in ls position
 MOVWF   LCDPORT  ; Command to lcd port

  ; Clock the ls nibble to the lcd.

 CLKLCD

 GOTO LCD2  ; Delay then exit from COMLCD


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

LCDCLR1 MOVLW LCDCLER  ; LCD clear command
 MOVWF LCDCH
 GOTO COMLCD


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

LCDCUR1 IORLW LCDCM  ; Cursor move command
 MOVWF LCDCH
 GOTO COMLCD


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

LCDCUR2 IORLW LCDCM  ; Cursor move command
 MOVWF LCDCH
 BSF LCDCH,6  ; This bit set for line 2
 GOTO 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 MOVLW CLKR-.3  ; Gives 48/64 us delay
 GOTO LCDEL

LCD2 MOVLW CLKR-.100 ; Gives 1.6ms delay

LCDEL MOVWF TMR0  ; Set TMR0 prescalar

LCDELA TSTRTC
 BNZ LCDELA  ; Wait for TMR0 to time out

 RETLW .0  ; Exit from CHA/COMLCD routines



 END
