Driving the LCD on the LAB-X3

Chuck McManis

December 30, 2001


LCD1.ASM -- Counting on the LAB X3 LCD

Now we're getting somewhere. The LCD works and the routines are pretty set. Now to do what at first seems impossible, convert a 16 bit number into ASCII text. It shouldn't be that hard. Of course, there isn't a divide instruction.

Lack of divide makes it more difficult but we can do it the old fashioned way, subtracting :-). This project actually helped in a couple of ways, one it let me re-use some Z80 code from back in the day, and two it gave me an opportunity to check out the macro language in MPASM 5.x.

This project simply displays the contents of a counter on the LCD with the text

Count: 00023

As you can see its pretty basic, no leading zero suppression etc. The BIN2ASCII macro takes about 75 program words (its all "unrolled") and has a worst case run time of 360 cycles when decoding 59999, best case is 84 cycles decoding 0.  

(Back to the projects Page)


; LCD1.ASM -- Convert a 16 bit number to a string and show
; it on the LCD.
; December 28th, 2001
        TITLE "LCD1 - Counting up"
        LIST P=PIC16F628, C=120, N=50, R=HEX
        include "P16F628.inc"
        include "util.inc" ; The wait macro and do_digit macro
        CBLOCK H'20'
            NUM:2     ; Number to convert low byte ...
            NUM_STR:5  ;         ... high byte
LED1            EQU     4
LED2            EQU     5        
LCD_E           EQU     6        

        ORG H'0000'
        GOTO    MAIN            ; Let's get this puppy rolling
        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
        CALL    LCD_INIT
        BSF     PORTB,LED1
        MOVWF   C1
;        MOVLW   H'FF'
        MOVWF   C2
; Print out a simple message
        INCF    C1,F
        INCF    C2,F
        MOVLW   H'80'           ; Location of "Count:"
        CALL    LCD_CMD         ; Set cursor
        MOVLW   'C'
        CALL    LCD_CHAR
        MOVLW   'o'
        CALL    LCD_CHAR
        MOVLW   'u'
        CALL    LCD_CHAR
        MOVLW   'n'
        CALL    LCD_CHAR
        MOVLW   't'
        CALL    LCD_CHAR
        MOVLW   ':'
        CALL    LCD_CHAR
        MOVF    C1,W
        MOVWF   NUM
        MOVF    C2,W
        MOVWF   NUM+1
        CALL    CVT_NUM
        MOVLW   H'8E'           ; Column 15  for 99999
        CALL    LCD_CMD
        MOVF    NUM_STR,W
        CALL    LCD_CHAR
        MOVF    NUM_STR+1,W
        CALL    LCD_CHAR
        MOVF    NUM_STR+2,W
        CALL    LCD_CHAR
        MOVF    NUM_STR+3,W
        CALL    LCD_CHAR
        MOVF    NUM_STR+4,W
        CALL    LCD_CHAR
        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

        IFDEF _DEBUG
            MOVLW   'A'
            MOVWF   DIGIT_0
            MOVLW   'B'
            MOVWF   DIGIT_1
            MOVLW   'C'
            MOVWF   DIGIT_2
            MOVLW   'D'
            MOVWF   DIGIT_3
            MOVLW   'E'
            MOVWF   DIGIT_4
            BIN2ASCII   NUM, NUM_STR
; 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
        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
; 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.
        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.
        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                  ;

; 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.
        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
; 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!
        MOVWF   DLY_CNT         ; Store delay count        (1)
        WAIT    91              ; Delay 95
        GOTO    D2_LOOP         ; Check to see if it was 1 (2)
        WAIT    96              ;       (20)
        DECF    DLY_CNT,F       ; Subtract 1            (1)
        INCFSZ  DLY_CNT,W       ; Check for underflow   (2)
        GOTO    D1_LOOP          ; Not zero so loop      (2)

  Listing 1: LCD Countingl Program