Try this code from the maplin PIC Cookbook, the rest of the book is pretty crap though. ; 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 from John Perkinton Jim Schneringer wrote: > Hello all! > I have been lurking around for sometime and thought it was about > time I came out of the shadows. > I just got into programming PICs a couple months ago and bought a Warp-3 > from Jim Robertson. What a nice feeling it is to program a 16F84 for the > first time. For some reason the wife just doesn't understand. > Anyway, I have programmed LEDs and multiplexed LED displays already. > Now I'm going to try my hand at an LCD 8x2 character display with the > Hitachi controller. I'm looking for a simple, non-serial example of the > 4-bit LCD programming method for a 16F84 that I can tear apart and > learn. If you can steer me towards an example code I would appreciate > it. > > Thanks, > > Jim Schneringer