December 30, 2001
Yup, the next thing was to figure out how to talk to the friggin' LCD on this thing. The details are sketchy at best. As it turns out the LCD is write only, no busy waiting possible, just dead reckoning by delaying long enough for it to finish. Thus, it isn't pretty but it does work, LCD0.ASM.
And just to emphasize that this is a pain. My original version did not wait long enough for the LCD to initialize and sometimes the LCD came up with the message and most times it came up blank. Thanks to the support guys at microEngineering Labs I got it debugged and running.
Final update, this one for real. As it turns out waiting was not the problem, the problem was that I failed to correctly switch the display on at the end of the initialization cycle. This one is now running happily, always turns on the display, and now it displays a bouncing "Hello" next to a bouncing "World." This then will be the basis for my future LCD routines.
(Back to the projects Page);
;
;
; LCD0.ASM -- Hello World on the LCD in assembly
;
; Once this works we will have a working set of LCD routines.
;
TITLE "LCD0 - Hello World"
LIST P=PIC16F628, C=120, N=50, R=HEX
include "P16F628.inc"
include "wait.inc" ; The WAIT macro
__FUSES _CP_OFF&_LP_OSC&_WDT_OFF&_LVP_OFF
CBLOCK H'20'
DLY_CNT
CMD_DELAY
CMD_TMP
LCD_TMP
I
L_DELAY
H_LOC
W_LOC
ENDC
LED1 EQU 4
LED2 EQU 5
LCD_E EQU 6
LINE1 EQU 0x080 ; Set display to line 1 character 0
LINE2 EQU 0x0C0 ; Set display to line 2 character 0
FUNC_SET EQU 0x028 ; 4 bits, 2 lines, 5x7 Font
DISP_ON EQU 0x00C ; Display on
DISP_ON_C EQU 0x00E ; Display on, Cursor on
DISP_ON_B EQU 0x00F ; Display on, Cursor on, Blink cursor
DISP_OFF EQU 0x008 ; Display off
CLR_DISP EQU 0x001 ; Clear the Display
LONG_DELAY MACRO LOOPS
MOVLW (LOOPS)
MOVWF L_DELAY
MOVWF D'100' ; 10 mS delay
CALL DELAY ; per lo0p
DECFSZ L_DELAY,F ; Count down
GOTO $-3 ; Loop
ENDM
ORG H'0000'
GOTO MAIN ; Let's get this puppy rolling
MAIN:
CLRF STATUS ; Set Bank 0
CLRF PORTA ; Clear PortA
CLRF PORTB ; and clear PortB
MOVLW H'07' ; Make PortA Digital I/O
MOVWF CMCON ; By setting CMCON<0:3>
BSF STATUS,RP0 ; Set Bank 1
CLRF TRISA ; Now A is all outputs
CLRF TRISB ; B all outputs
BSF TRISB,7 ; Button S1
CLRF STATUS ; Back to BANK 0
BSF PORTB,LED1 ; Turn On LED1
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
CALL LCD_INIT
MOVLW H'80'
MOVWF H_LOC
MOVLW H'CA'
MOVWF W_LOC
BSF PORTB,LED1
;
; Print out a simple message
;
msg_loop:
MOVF H_LOC,W ; Location of Hello
CALL LCD_CMD ; Set cursor
MOVLW D'8' ; Clear it out
MOVWF I ; Store it in I
ERASE_HELLO:
MOVLW ' '
CALL LCD_CHAR
DECFSZ I,F
GOTO ERASE_HELLO
MOVF H_LOC,W ; Get position back
XORLW H'40' ; Toggle bit 6
MOVWF H_LOC ; New Hello Location
CALL LCD_CMD
MOVLW 'H'
CALL LCD_CHAR
MOVLW 'e'
CALL LCD_CHAR
MOVLW 'l'
CALL LCD_CHAR
MOVLW 'l'
CALL LCD_CHAR
MOVLW 'o'
CALL LCD_CHAR
MOVF W_LOC,W
CALL LCD_CMD
MOVLW D'8'
MOVWF I
ERASE_WORLD:
MOVLW ' '
CALL LCD_CHAR
DECFSZ I,F
GOTO ERASE_WORLD
MOVF W_LOC,W
XORLW H'40'
MOVWF W_LOC
CALL LCD_CMD
MOVLW 'W'
CALL LCD_CHAR
MOVLW 'o'
CALL LCD_CHAR
MOVLW 'r'
CALL LCD_CHAR
MOVLW 'l'
CALL LCD_CHAR
MOVLW 'd'
CALL LCD_CHAR
MOVLW '!'
CALL LCD_CHAR
wait_loop:
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
MOVLW D'200'
CALL DELAY
MOVF PORTB,W ; Get port B
XORLW H'FF' ; Toggle It
XORLW H'CF' ; Revert it back
MOVWF PORTB ; Now LED1 and LED2 are alternates
GOTO msg_loop
;
; The LCD on the LAB-X3 (modified) is connected thusly:
; RA0 - DB4 RA4 - RS
; RA1 - DB5
; RA2 - DB6 RB6 - E (this was my modification)
; RA3 - DB7
;
; The LCD needs to be initialized into 4 bit mode and then
; we should be able to write letters to it. Note that on the
; LAB-X3 the LCD is ALWAYS in write mode, no reading allowed so
; you just have to hope it isn't busy.
;
; According to the Hitachi data sheet, you first have to
; wait 15 mS to insure the display is "stable" after Vcc is
; applied. Then the following sequence puts it into "4 bit"
; mode (reset by instruction)
; 0x03 ' wait > 4.1 mS
; 0x03 ' wait > .1 mS
; 0x03 ' wait > .1 mS
; 0x02 ' wait > .1 mS
; Display is now in 4-bit mode so initialize it with:
; 0x02 0x08 ' Set 4-bit mode, 2 line display
; 0x00 0x08 ' Display "ON"
; 0x00 0x01 ' Clear Display
; 0x00 0x06 ' Entry mode, auto increment cursor
;
LCD_INIT:
MOVLW D'200' ; Wait for LCD to settle
CALL DELAY
MOVLW H'03' ; Set LCD for 4 bits
MOVWF PORTA ; Data Lines
BSF PORTB,LCD_E ; Toggle E
BCF PORTB,LCD_E ;
MOVLW H'50' ; Wait 5 mS
CALL DELAY
BSF PORTB, LCD_E ; Send command again
BCF PORTB, LCD_E
MOVLW H'2'
CALL DELAY
BSF PORTB, LCD_E ; Third time's the charm
BCF PORTB, LCD_E
MOVLW H'2'
CALL DELAY
MOVLW H'02' ; Set for 4 bits
MOVWF PORTA ; like so
BSF PORTB, LCD_E ;
BCF PORTB, LCD_E
MOVLW H'2' ; Wait .2 mS
CALL DELAY
;
; Now at this point its in 4-bit mode so send setup
; commands through the 4-bit interface.
;
MOVLW H'28' ; Set 2 line display, 4 bit I/O
CALL LCD_CMD ;
MOVLW H'08' ; Turn off the Display
CALL LCD_CMD
MOVLW H'01' ; Clear the contents of the display
CALL LCD_CMD
MOVLW H'06' ; Set the Entry mode
CALL LCD_CMD
MOVLW H'0C' ; Turn it on again
CALL LCD_CMD
RETURN ; Ready to rock and roll
;
; LCD_CMD
;
; Generic routine to send a command to the LCD. Since some
; commands take longer to run this routine waits 1mS after it
; sending the command to insure the LCD is done with it.
;
LCD_CMD:
MOVWF LCD_TMP ; Store command
MOVLW H'1' ; This is the generic delay
MOVWF CMD_DELAY ; Used by default
MOVLW H'FC' ; This is how we check for clear/home
ANDWF LCD_TMP,W ; If any bit other than 0 or 1 is set
BTFSS STATUS,Z ;
GOTO OK_DELAY ; If non-zero leave delay alone
MOVLW D'20' ; Else store 2mS delay value.
MOVWF CMD_DELAY
OK_DELAY:
SWAPF LCD_TMP,W ; Read it, put upper nibble down
ANDLW H'0f' ; Turn OFF the R/S bit
MOVWF PORTA ; Out it goes
BSF PORTB,LCD_E ; Clock it out
BCF PORTB,LCD_E ; Like so
MOVF LCD_TMP,W ; Get lower nybble
ANDLW H'0F' ; Turn off R/S
MOVWF PORTA ; Put it on PortA
BSF PORTB,LCD_E ; Clock it out
BCF PORTB,LCD_E ;
MOVF CMD_DELAY,W ; Wait for it to complete
CALL DELAY
RETURN ;
;
; LCD_CHAR
;
; Generic routine to send a command to the LCD. In this
; version it just sends it, a "smarter" version would watch
; for <CR> or <LF> and do something appropriate to the display.
;
LCD_CHAR:
MOVWF LCD_TMP ; Store it in LCD_TMP
SWAPF LCD_TMP,W ; Upper Nybble
ANDLW H'0F' ; Clear upper bits
IORLW H'10' ; Turn On R/S bit
MOVWF PORTA ; Put it out to PortA
BSF PORTB,LCD_E ; Clock it out
BCF PORTB,LCD_E ;
MOVF LCD_TMP,W ; Get the lower nybble
ANDLW H'0F' ; Clear upper bits
IORLW H'10' ; Turn on R/S Bit
MOVWF PORTA ; Out to PORTA
BSF PORTB, LCD_E ; Clock it out
BCF PORTB, LCD_E ;
MOVLW H'2' ; Wait a bit
CALL DELAY
RETURN
;
; Delay Loop
;
; This function is designed to delay for 100uS times the number
; of times through the loop
; Once through :
; 1+91+2+1+2+1+2 = 100
;
; Twice through
; 1+91+2+1+1+2+96+1+2+1+2 = 200
; ******
; Thrice through
; 1+91+2+1+1+2+96+1+1+2+96+1+2+1+2 = 300
; ******** ******
; "N" times through (n * 100) cycles.
;
; NOTE: This will have to be changed when you change the
; frequency from 4Mhz, but for the LAB-X3 it works great!
;
DELAY:
MOVWF DLY_CNT ; Store delay count (1)
WAIT 91 ; Delay 95
GOTO D2_LOOP ; Check to see if it was 1 (2)
D1_LOOP:
WAIT 96 ; (20)
D2_LOOP:
DECF DLY_CNT,F ; Subtract 1 (1)
INCFSZ DLY_CNT,W ; Check for underflow (2)
GOTO D1_LOOP ; Not zero so loop (2)
NOP
RETURN
END |
||
| Listing 1: LCD Control Program |