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|