December 30, 2001
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
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|