Driving the LCD on the LAB-X3

Chuck McManis

December 30, 2001

 

LCD0.ASM -- Hello World for the LCD

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