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
       	__FUSES _CP_OFF&_LP_OSC&_WDT_OFF&_LVP_OFF
        
        CBLOCK H'20'
            DLY_CNT
            CMD_DELAY
            CMD_TMP
            LCD_TMP
            L_DELAY
            C1
            C2
            LO_COUNT
            HI_COUNT
            NUM:2     ; Number to convert low byte ...
            NUM_STR:5  ;         ... high byte
        ENDC
                
        
LED1            EQU     4
LED2            EQU     5        
LCD_E           EQU     6        

        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
        CALL    LCD_INIT
        BSF     PORTB,LED1
        CLRW
        MOVWF   C1
;        MOVLW   H'FF'
        MOVWF   C2
;
; Print out a simple message
;
msg_loop:
        INCF    C1,F
        SKPNZ
        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
        
wait_loop:        
        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

CVT_NUM:
        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
        ELSE        
            BIN2ASCII   NUM, NUM_STR
        ENDIF        
        RETURN
        
;
; 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 Countingl Program