Menu Général
Doc Hitachi(format pdf )           -         Ecran LCD EPSON (format pdf )      -     Cours LCD(en anglais, pdf )
                                                                                               Doc LCD Techno              -                Doc LCD Resumé
 

L'afficheur LCD  piloté par 68HC11

LCD (programming & pinouts)

Contents:
[Document Version: 1.10][Last Updated: 9/12/96]


1. LCD pintout & setup

(From Steve Hill)

 An LCD backlight would be run off of 5vDC, while an EL backlight uses the inverter that you described. The "two extra" pins are for the backlight and are not polarized - hook the outputs of the inverter to these pins.

 Vss should be hooked to ground - I believe your origional post may have been in error.

 Vdd should be hooked to +5v.

 Vo (pin 3) should be connected to the wiper on a 10K pot which is accross pins 1 and 2.

 Pin 4 is register select. When high you are writing data (characters to the display) and when low you are writing to control registers.

 Pin 5 is Read/Write. Can be tied low since a basic implementation has no need to read from the display registers - but is useful in more advanced applications.

 Pin 6 is Enable. The data on pins 7-14 is latched on the falling edge of this signal. Just strobe it low after you have set up data on the data pins.

 Pins 7-14 are data I/O. Using all these pins for an 8 bit interface. Use only DB4-7 (pins 11-14) for a 4 bit interface.

 Prior to writing characters to the display, it must be initialized. A typical initialization sequence is as follows: (assumes R/W tied low)

  1. Power ON
  2. Function SET (8 data bits, 2 lines, 5x7 dot format) RS=0 Data=0x38
  3. Function SET (repeat above 4 times) RS=0 Data=0x38
  4. Function SET RS=0 Data=0x38
  5. Function SET RS=0 Data=0x38
  6. Entry Mode (Increment one, no shift) RS=0 Data=0x06
  7. Display ON/OFF (display ON, Cursor ON, Blink OFF) RS=0 Data=0x0E
  8. Display Clear RS=0 Data=0x01
  9. DD RAM Address Set RS=0 Data=0x80
(note the address could be different for your display - try it - consult data sheet) Also note that the second display line probably isn't at an address that is consecutive with the first (0x80-0x80 typical first line; 0xC0-0xCF typeical second line) Now you're ready to write data. Just set RS high and write the character codes you want. DDRAM address is automatically incremented. To get to the second line you need to set the DDRAM address as shown above.


2. LCD programming

(From Bill Mayhew)

 [Most LCDs have same/similar programming]
 
 

RS R/W B7 B6 B5 B4 B3 B2 B1 B0

0  0   0  0  0  0  0  0  0  1        Clear Display

0  0   0  0  0  0  0  0  1  X        Return Home

0  0   0  0  0  0  0  1  D  S        Entry Mode Set
                                     D: 0=direction -, 1=direction +
                                     S: 0=no shift, 1=shift

0  0   0  0  0  0  1  D  C  B        Display on/off
                                     D: 0=disply off, 1=on
                                     C: 0=cursor off, 1=on
                                     B: 0=blink off, 1=on

0  0   0  0  0  1  S  R  X  X        Display or Cursor Shift
                                     S: 0=cursor only, 1=both
                                     R: 0=right; 1=left

0  0   0  0  1  D  N  F  X  X        Set data interface
                                     D: 0=8 bit, 1=4 bit
                                     N: 0=F works, 1=F is ignored
                                     F: 0=5x7 dots, 1=5x10 dots

0  0   0  1  A  A  A  A  A  A        Set char gen RAM address
                                     Lets you select one of 64
                                     chars for loading a bit map

0  0   1  A  A  A  A  A  A  A        Set display RAM address
                                     Lets you select an address
                                     in the display buffer.  With
                                     this you can write in a pos-
                                     ition that is off the viewable
                                     display.

0  1   B  A  A  A  A  A  A  A        Read busy flag, address pointer
                                     If B=1, display is updating.
                                     Also returns address of last
                                     data transfer.

1  0   D  D  D  D  D  D  D  D        Write Data
                                     Writes to Display if previous
                                     instruction was SET DISPLAY
                                     RAM ADDR.  Writes to char gen if
                                     previous instruction was SET
                                     CHAR GEN RAM ADDR.

1  1   D  D  D  D  D  D  D  D        Read Data
                                     Reads from Display if previous
                                     instruction was SET DISPLAY
                                     RAM ADDR.  Reads from char gen if
                                     previous instruction was SET
                                     CHAR GEN RAM ADDR.
It is best to read the busy flag to see if the previous operation you requested is complete. You can also go by timing if you are trying to minimize the number of input operations. Timing:
 
 
Clear               82uS to 1.64 mS  (assuming 250 KHz clock)
Return home         40uS to 1.6 mS
Entry mode set      40uS 
Display on/off      40uS
Display/cursor shf  40uS
Function set        40uS
Set CG RAM addr     40uS
Set disp RAM addr   40uS
Read busy flag       1uS
Write Data          40uS
Read Data           40uS

3. LCD programming sample (6502)

(From Bill Mayhew)
 
 
        .nolist
        .page
        .title  "Watch dog program"
        .list
        .eject
;
;watch dog single board computer system
;Bill Mayhew,  Neoucom, January 1989
;traget cpu Rockwell 6502 with 1 MHz clock
;
;revised March 6, 1989 to support warm boot into
;bypass mode.
;
;6522 chip registers
outb    .equ    $c000
outa    .equ    $c001
ddrb    .equ    $c002
ddra    .equ    $c003
t1l     .equ    $c004
t1h     .equ    $c005
t2l     .equ    $c008
t2h     .equ    $c009
acr     .equ    $c00b
pcr     .equ    $c00c
flags   .equ    $c00d
icr     .equ    $c00e
;
;assorted page zero locations
asave   .equ    $00             ;used by intrpt
ysave   .equ    $01             ;used by intrpt
casave  .equ    $02             ;used by chrout
cysave  .equ    $03             ;used by chrout
caddr   .equ    $04             ;address of cursor on display unit
cmode   .equ    $05             ;current mode of cursor
pasave  .equ    $06             ;used by print
pysave  .equ    $07             ;used by print
msgbase .equ    $08             ;word used to hold beginning of message
slcount .equ    $0a             ;used by sleep routine
sysave  .equ    $0b             ;used by sleep
tmin    .equ    $0c             ;used in count-down timer
min     .equ    $0d
tsec    .equ    $0e
sec     .equ    $0f
tenth   .equ    $10
twait   .equ    $11
owait   .equ    $12
getsw   .equ    $13             ;vector switch for char input
getvec  .equ    $14             ;word vector for char input routine
cookie  .equ    $16             ;word 'magic cookie' to determine reset mode
;
;
        .org    $f000           ;assemble into system ROM
entry   .equ    *
        sei                     ;kill interrupts whilst setting up
        cld                     ;operate in binary mode
        lda     #$07            ;only bits 0, 1 and 2 are outputs
        sta     ddra            ;enable ddra
        lda     #$ff            ;ddr output mask -- all bits on
        sta     ddrb            ;enable ddrb
        lda     #$c2            ;mask to enable timer #1 & ca1 trigger
        sta     icr             ;set interrupt control register
        lda     #$40            ;mask for t1 continuous mode
        sta     acr             ;set auxiliary control register
        lda     #$fb            ;ss relay on, lcd in text mode, beeper off
        sta     outa            ;place bits in output register
        lda     #(50000 % $100) ;loop time of 50.000 mS
        sta     t1l             ;least significant byte of timer
        lda     #(50000 >> 8)
        sta     t1h             ;most significant byte of timer
        lda     #$ae            ;mask to generate PB2 pulse on outb write
        sta     pcr             ;set peripheral control register
        jsr     newline         ;initially clear the display
        lda     cookie          ;check magic cookoie; $1234 means warm boot.
        cmp     #$34            ;cookie lsb
        bne     e1              ;continue if not magic value
        lda     cookie+1
        cmp     #$12            ;cookie msb
        bne     e1
        jmp     bypass          ;go to bypass mode if magic value found
e1      lda     #$34            ;set up magic cookie
        sta     cookie          ;lsb
        lda     #$12
        sta     cookie+1        ;msb
        lda     #(init1 % $100) ;get first initial message
        sta     msgbase
        lda     #(init1 >> 8)
        sta     msgbase+1
        jsr     print           ;send line to display
        lda     #3              ;sleep 3 seconds
        jsr     sleep
        jsr     newline
        lda     #(init2 % $100) ;get second initial message
        sta     msgbase
        lda     #(init2 >> 8)
        sta     msgbase+1
        jsr     print
        lda     #3
        jsr     sleep
        jsr     newline
        lda     #(init3 % $100) ;get third initial message
        sta     msgbase
        lda     #(init3 >> 8)
        sta     msgbase+1
        jsr     print
        lda     #3
        jsr     sleep
        lda     #(i1 % $100)    ;initialize char interrupt vector
        sta     getvec
        lda     #(i1 >> 8)
        sta     getvec+1
        lda     #0
        sta     cookie          ;clear cookie
        sta     cookie+1
        lda     t1h             ;reset timer
        cli                     ;interrupts are ok now
;
;
mlp     .equ    *               ;kernel begins here
        lda     #$7             ;reset count-down clock
        sta     tmin
        lda     #0
        sta     min
        sta     tsec
        sta     sec
        sta     tenth
        lda     twait           ;set oldwait-time = this-wait-time
        sta     owait
        sei                     ;can't interrupt print operations
        jsr     newline         ;clear display
        lda     #(resetm % $100)
        sta     msgbase
        lda     #(resetm >> 8)
        sta     msgbase+1
        jsr     print           ;print static element of time display
ml1     ldy     tmin            ;get tens of minutes left
        lda     #9              ;cursor in column 9
        sei                     ;can't interrupt print operations
        jsr     tput            ;put ascii char on display
        ldy     min
        lda     #10
        jsr     tput
        ldy     tsec
        lda     #12
        jsr     tput
        ldy     sec
        lda     #13
        jsr     tput
        ldy     tenth
        lda     #15
        jsr     tput
        cli                     ;o. k. to interrupt now
                                ;
        lda     #0              ;set accum=0
        ldy     #4              ;see if all 5 time bytes are zero
ml2     ora     tmin,y          ;tmin+4, +3, +2, +1, +0
        dey
        bpl     ml2             ;loop if y >= 0
        ora     #0              ;set flags
        beq     mlre            ;do a reset when 00:00.0
                                ;
        dec     tenth
        bpl     ml3             ;skip other digits if >= 0
        lda     #9
        sta     tenth
        dec     sec
        bpl     ml3
        lda     #9
        sta     sec
        dec     tsec
        bpl     ml3
        lda     #5
        sta     tsec
        dec     min
        bpl     ml3
        lda     #9
        sta     min
        dec     tmin
ml3     lda     twait           ;what is our hash count?
        cmp     owait           ;same as before?
        beq     ml3             ;wait for interrupt to change hash count
        sta     owait           ;twait --> owait
ml4     lda     twait
        cmp     owait
        beq     ml4
        sta     owait
        jmp     ml1             ;repeat process for next 1/10 second
                                ;
mlre    sei                     ;can't interrupt power-cycle
        jsr     newline         ;have to reboot because 70 minutes expired
        lda     #(bootm % $100)
        sta     msgbase
        lda     #(bootm >> 8)
        sta     msgbase+1
        jsr     print
        lda     outa
        and     #$fe            ;set bit 0=0
        sta     outa            ;shut off solid state relay
        lda     #30             ;sleep 30 seconds to allow power-clycle
        jsr     sleep
        lda     outa
        ora     #$01            ;set bit 0=1
        sta     outa
        cli                     ;o. k. to interrupt now
        jmp     mlp             ;re-do this routine forever!
;
;
tput    .equ    *               ;ascii print a decimal number in y, cursor in a
        pha
        lda     outa            ;enter command mode
        and     #$fd            ;bit 0=0
        sta     outa
        pla
        ora     #$80            ;set msb
        jsr     chrout
        lda     outa
        ora     #$02            ;enter text mode
        sta     outa
        tya                     ;get decimal #
        clc
        adc     #48             ;adjust for printable ascii
        sta     outb
        rts
;
;
intrpt  .equ    *
        pha
        lda     flags           ;get interrupt flags from 6522 via chip
        and     #$40            ;see if t1 interrupt has happened
        beq     ijmp            ;else it must have been from the CA-1 pin
        inc     twait           ;change hash variable
        lda     t1l             ;read register to clear the flag bit
        lda     flags           ;see if other interrupt is pending
        and     #$02
        bne     ijmp
        pla
        rti                     ;resume processing
                                ;
ijmp    jmp     (getvec)
                                ;
i1      pla                     ;get rid of pushed accumulator
        lda     outa            ;read outa to clear the CA-1 flag bit
        jsr     newline         ;clear display
        lda     #(portm % $100)
        sta     msgbase
        lda     #(portm >> 8)
        sta     msgbase+1
        jsr     print           ;display busy activity message
        lda     #0
        sta     getsw           ;initially exit at end of this sleep
        lda     #(i2 % $100)    ;re-aim vector
        sta     getvec
        lda     #(i2 >> 8)
        sta     getvec+1
        pla                     ;dispose of interrupt vector and flags
        pla
        pla
i1x     cli                     ;"manually" re-enable interrupting
        lda     #1              ;sleep for one second
        jsr     sleep
        sei                     ;disable interrupts for checking vector
        lda     getsw           ;any more sleep?
        bne     i1y
        dec     getsw           ;less sleep now
        jmp     i1x             ;go sleep
i1y     lda     #(i1 % $100)    ;re-aim vector
        sta     getvec
        lda     #(i1 >> 8)
        sta     getvec+1
        cli                     ;o. k. for interrupts now
        jmp     mlp             ;jump back into main loop
                                ;
i2      lda     #1              ;force one more second of sleep
        sta     getsw
        lda     outa            ;read port to reset interrupt
        pla
        rti
;
;
delay   .equ    *               ;wait a short time
        pha
        lda     acr             ;get timer control
        and     #$df            ;mask t2 control bit
        sta     acr             ;update status
        pla
        pha
        sta     t2l             ;load timer latch
        sty     t2h             ;writing MSB triggers timer
wait    lda     flags           ;get flags
        and     #$20            ;mask out the t2 status bit
        beq     wait            ;loop until it times out
        pla
        rts
;
;
sleep   .equ    *               ;sleep for n seconds, n passed in accum.
        sty     sysave
        ora     #0              ;set flags
sl1     beq     slend
        sec
        sbc     #1              ;decrement sleep time
        pha
        lda     #20             ;sleep in 20 50-mS increments
        sta     slcount
sl2     dec     slcount
        bne     sl3
        pla
        jmp     sl1
sl3     lda     #(50000 % $100) ;50 mS time constant
        ldy     #(50000 >> 8)
        jsr     delay
        jmp     sl2
        ldy     sysave
slend   rts
;
;
chrout  .equ    *               ;send char in accum to display
        sta     outb            ;put in PIA output
        sta     casave          ;in case caller wants it back
        sty     cysave
        lda     #120            ;wait 120 uS
        ldy     #0
        jsr     delay           ;burn up some time
        ldy     cysave
        lda     casave
        rts
;
;
putc    .equ    *               ;put char into display, maybe with scroll
        pha
        lda     caddr           ;where is cursor now?
        cmp     #16             ;past last column?
        bne     pc1             ;if not, don't worry, be happy
        lda     cmode           ;check if scrolling is on
        bne     pc2             ;skip this if it is already on
        lda     outa
        and     #$fd            ;bit 1=0, puts lcd in command mode
        sta     outa
        lda     #$07            ;display board code that enables scrolling
        jsr     chrout          ;send
        lda     outa
        ora     #$02            ;bit 1=1, puts lcd in character mode
        sta     outa
        inc     cmode           ;flag the status byte
pc1     inc     caddr           ;show that cursor moved
pc2     pla
        jsr     chrout          ;send user's character
        rts                     ;that's it!
;
;
newline .equ    *               ;clear display & home cursor with full reset
        pha
        lda     outa            ;get status
        and     #$fd            ;bit 1=0, enter command mode
        sta     outa
        lda     #$01            ;display board reset code
        jsr     chrout          ;send to display
        lda     #(5000 % $100)  ;time constant for 5 mS delay
        ldy     #(5000 >> 8)
        jsr     delay           ;burn up some time
        lda     #$34            ;select 5 * 10 bit character set
        jsr     chrout
        lda     #$0c            ;unblank display
        jsr     chrout
        lda     #$06            ;disable display scrolling
        jsr     chrout
        lda     #0
        sta     cmode           ;indicate scrolling is disabled
        sta     caddr           ;cursor is in column zero
        lda     outa
        ora     #$02            ;bit 1=1, enter lcd text mode
        sta     outa
        pla
        rts
;
;
print   .equ    *               ;print a message on entry, msgbase points to msg
        sta     pasave
        sty     pysave
        ldy     #0
pl      lda     (msgbase),y     ;get character
        beq     pend            ;if a \0, that's end of msg.
        jsr     putc
        iny
        bne     pl              ;branch always
pend    ldy     pysave
        lda     pasave
        rts
;
;
bypass  .equ    *               ;bypass mode
        lda     #0              ;clear magic cookie
        sta     cookie
        sta     cookie+1
        lda     #(bpassm % $100)
        sta     msgbase
        lda     #(bpassm >> 8)
        sta     msgbase+1
        jsr     print
b1      lda     outa            ;get output port
        ora     #$04            ;turn on beeper
        sta     outa
        lda     #(50000 % $100 )
        ldy     #(50000 >> 8)   ;50 mS time constant
        jsr     delay
        jsr     delay
        jsr     delay
        jsr     delay           ;wait total 200 mS
        lda     outa
        and     #$fb            ;turn off beeper
        sta     outa
        lda     #120            ;sleep for two minutes
        jsr     sleep
        jmp     b1              ;do forever until reset
;
;
init1   .text   "  Watch Cat 1.0"
        .byte   0
init2   .text   "   Bill Ma"
        .byte   %11111001       ;y with lower case descender
        .text   "hew"
        .byte   0
init3   .text   "  NEOUCOM  1989"
        .byte   0
resetm  .text   "Reset in   :  ."
        .byte   0
portm   .text   "   Port Active"
        .byte   0
bootm   .text   "RESETTING SYSTEM"
        .byte   0
bpassm  .text   "  BYPASS MODE"
        .byte   0
;
;
        .org    $f7fa           ;hardwire vectors
        .word   entry           ;NMI points to system entry point
        .word   entry           ;address of system entry point
        .word   intrpt          ;address of IRQ service routine
        .end

4. Where to buy LCD displays

(From Marc Christensen)

 If you can use 1x16 they are easy to find on the surplus market. Timeline, Inc:

 TimeLine Inc
23605 Telo Ave
Torrance, CA 90505
USA (800) 872-8878
CA (800) 223-9977

 has lots of LCD displays. For example, 1x16's got for $6.00 - $8.00 with the controllers built into them and backlight. They also have a 4 Col x 2 Row for $5.00. Many of the displays can use larger characters and fewer lines if you want to try that.

 Marlin P Jones has a 1x24 without the controller for $1.00


5. Controlling LCD with a Motorola 68hc11

(From Alan Kilian)
 
 
*
*       Author: Alan Kilian   kilian(at)cray.com
*
*       Title : HC11 LCD driver routines
*
*       File Name : lcd.asm
*
*       Description : This program demonstrates the use of a HC11 processor
*                     to control a LCD panel
*
*       History : 02/10/93 Created.
*                 02/11/93 Added power-up delay
*                 03/17/94 Tried to get reliable operation with 256 X 64 bit
*                          display. Epson LM213B
*                 03/18/94 Added the LCD_RST line to control the *RESET line
*                 03/19/94 Added proper *RESET timeing. Combined SELECT_REG
*                          and STORE_REG into one routine.
*                 03/30/94 Changed to using BSET/BCLR
*                 04/04/94 Finally got BSET/BCLR to work!!!
*
*       Port usage:
*       PORTC Bit0 = DB0 through BIT7 = DB7
*       PORTA Bit7 = E
*             Bit6 = RS
*             Bit5 = RW
*             Bit4 = Reset
*
*       Note : This program is written for the MC68HC811E2 processor running
*              in single-chip mode. It is designed for use with a processor
*              running with an 8 mHz crystal. If you are using a different
*              crystal frequency you will need to re-compute all of the
*              timing values in this code.
*
*              The structure, serial I/O and command processor portions of
*              this program evolved from the program HEXLOB40 written by
*              Fred Martin and Randy Sargent and we thank them greatly.
****************************************************************************
 
* One-Byte Registers
PORTA   EQU     $1000
PACTL   EQU     $1026
PORTB   EQU     $1004   ; PORT B data register
PORTC   EQU     $1003
DDRC    EQU     $1007
SPCR    EQU     $1028
BAUD    EQU     $102B   ; SCI Baud Rate Control Register
SCCR1   EQU     $102C   ; SCI Control Register 1
SCCR2   EQU     $102D   ; SCI Control Register 2
SCSR    EQU     $102E   ; SCI Status Register
SCDR    EQU     $102F   ; SCI Data Register
* Masks for serial port
PORTD_WOM       EQU     $20
BAUD1200        EQU     $B3
BAUD9600        EQU     $B0
TRENA           EQU     $0C     ; Transmit, Receive ENAble
RDRF            EQU     $20     ; Receive Data Register Full
TDRE            EQU     $80     ; Transmit Data Register Empty
 
* Masks for the PORTA LCD control signals
LCD_E           EQU     $80     ; Bit 7 of PORTA is an active HIGH "E"
LCD_RS          EQU     $40     ; Bit 6 of PORTA is an active HIGH reg#1
LCD_RW          EQU     $20     ; Bit 5 of PORTA is an active LOW write
LCD_RST         EQU     $10     ; Bit 4 of PORTA is an active LOW reset
 
* Masks for PORTC direction
ALL_OUTPUT      EQU     $FF     ; 1 = output, 0 = input
 
* The BUSY bit on PORTC
BUSY            EQU     $80     ; Bit 7 of PORTC
 
* The names of the LCD registers
MODE            EQU     $00
PITCH           EQU     $01
NCHARS          EQU     $02
DUTY            EQU     $03
CURPOS          EQU     $04
DUMMY1          EQU     $05     ; There is no command 5
DUMMY2          EQU     $06     ; There is no command 6
DUMMY3          EQU     $07     ; There is no command 7
DSLOW           EQU     $08
DSHIGH          EQU     $09
CURLOW          EQU     $0A
CURHIGH         EQU     $0B
WRDAT           EQU     $0C
RDDAT           EQU     $0D
CLRBIT          EQU     $0E
SETBIT          EQU     $0F
 
* Values for various LCD things
DISPLAY_WIDTH   EQU     256     ; Pixels wide
DISPLAY_HEIGHT  EQU     64      ; Pixels tall
DUTY_CYCLE      EQU     64      ; 1/64 DECIMAL duty cycle
CHAR_WIDTH      EQU     8       ; How wide is the char cell?
CHAR_HEIGHT     EQU     8       ; How tall is the char cell?
CURSOR_LINE     EQU     8       ; Cursor is on line 8 of the char cell
 
* Bits for the mode command
DISPLAY_ON      EQU     $20     ; Is the display on?
MASTER          EQU     $10     ; Master/Slave mode
BLINK           EQU     $08     ; Does the cursor blink?
CURSOR_ON       EQU     $04     ; Is the cursor displayed?
GRAPHICS_MODE   EQU     $02     ; Graphics or character mode?
EXTERNAL_CG     EQU     $01     ; Is the character generator internal?
 
********************************************************************************
* zero page RAM definitions. Do not use FCB here. It will stomp EEBOOT20.
********************************************************************************
 
        ORG     $00             ; The beginning of RAM
 
**********************************************************************
*                              MAIN CODE                             *
**********************************************************************
 
        ORG     $F800           ; $F800 is the beginning of EEPROM
*                               ; on a MC68HC811E2 processor
 
Start:
        LDS     #$00FF          ; Set stack at the top of ram
 
        LDAA    #ALL_OUTPUT
        STAA    DDRC            ; Set PORTD for all outputs.
 
        JSR     SERIAL_SETUP
 
        JSR     LCD_RESET
        JSR     LCD_SET
        JSR     LCD_HOME
        JSR     LCD_HOME
 
        JSR     PRT_STR
 
mainloop
        BRA     mainloop
 
SERIAL_SETUP
        LDX     #$1000
        BCLR    SPCR,X PORTD_WOM        ; turn off wired-or mode
        LDAA    #BAUD9600
        STAA    BAUD
        LDAA    #TRENA
        STAA    SCCR2
        RTS
 
LCD_RESET
        LDAA    #00
        STAA    PORTA                   ; Reset the LCD panel
        JSR     BIGDELAY
        LDAA    #LCD_RST
        STAA    PORTA                   ; Release the reset line
        JSR     BIGDELAY
        RTS
 
BIGDELAY
        LDAA    #$FF            ; Delay to allow the LCD panel to power-up
BIG1    LDAB    #$FF
BIG2    DECB
        BNE     BIG2
        DECA
        BNE     BIG1
        RTS
 
LCD_HOME
        LDAA    #DSLOW          ; Set display address low
        LDAB    #$00            ; Start displaying at address zero
        JSR     SELECT_REG
 
        LDAA    #DSHIGH         ; Set display address high
        LDAB    #$00            ; Start displaying at address zero
        JSR     SELECT_REG
 
        LDAA    #CURLOW         ; Set cursor address low
        LDAB    #$00            ; Put the cursor at address zero
        JSR     SELECT_REG
 
        LDAA    #CURHIGH        ; Set cursor address high
        LDAB    #$00            ; Put the cursor at address zero
        JSR     SELECT_REG
        RTS
 
PRT_STR LDX     #STR
        LDAA    #WRDAT
PRT0    LDAB    0,X
        CMPB    #'='            ;String terminates with a '=' character
        BEQ     PRT1
        JSR     SELECT_REG
        INX
        BRA     PRT0
PRT1    RTS
 
PRT_CHR PSHA
        PSHB
        TAB
        LDAA    #WRDAT
        JSR     SELECT_REG
        PULB
        PULA
        RTS
 
LCD_SET
        LDAA    #$80
        STAA    PACTL
 
        LDAA    #MODE           ; Command 0 Mode control
        LDAB    #DISPLAY_ON|MASTER|BLINK|CURSOR_ON
        JSR     SELECT_REG
 
        LDAA    #PITCH          ; Command 1 Character pitch
        LDAB    #CHAR_HEIGHT-1
        ASLB
        ASLB
        ASLB
        ASLB
        ORB     #CHAR_WIDTH-1
        JSR     SELECT_REG
 
        LDAA    #NCHARS         ; Command 2 Number of characters
        LDAB    #DISPLAY_WIDTH/CHAR_WIDTH
        SUBB    #1
        JSR     SELECT_REG
 
        LDAA    #DUTY           ; Command 3 Duty cycle
        LDAB    #DUTY_CYCLE-1
        JSR     SELECT_REG
 
        LDAA    #CURPOS         ; Command 4 Cursor position
        LDAB    #CURSOR_LINE-1
        JSR     SELECT_REG
        RTS
 
 
SELECT_REG
        PSHX
        PSHB
        LDX     #$1000
        JSR     WAIT_BUSY
        STAA    PORTC           ; Set the data lines
        BSET    PORTA,X #LCD_RS         ; Set RS high and RW low
        BSET    PORTA,X #LCD_E   ; Set RS and E high
        BCLR    PORTA,X #LCD_E            ; Set RS low also.
        BCLR    PORTA,X #LCD_RS         ; Set E low
 
        PULB
        STAB    PORTC                   ; Set the data lines
        BSET    PORTA,X #LCD_E          ; Set E High
        BCLR    PORTA,X #LCD_E
        PULX
        RTS
 
WAIT_BUSY
        PSHB
        BCLR    DDRC,X #BUSY
        
        BSET    PORTA,X #LCD_RS|LCD_RW
        BSET    PORTA,X #LCD_RS|LCD_RW|LCD_E
 
WAIT    BRSET   PORTC,X #BUSY *         ; Wait until the BUSY bit goes zero
 
        BCLR    PORTA,X #LCD_E
        BCLR    PORTA,X #LCD_RW
        BCLR    PORTA,X #LCD_RS
 
        BSET    DDRC,X #BUSY
        PULB
        RTS
 
STR     FCC     'Shutter Speed.1 = '
 
BadInt  RTI                     ; Set all unused vectors here
        Org     $FFC0           ; Where the interrupt vectors are
 
        FDB     BadInt  * $FFC0 ; Reserved
        FDB     BadInt  * $FFC2 ; Reserved
        FDB     BadInt  * $FFC4 ; Reserved
        FDB     BadInt  * $FFC6 ; Reserved
        FDB     BadInt  * $FFC8 ; Reserved
        FDB     BadInt  * $FFCA ; Reserved
        FDB     BadInt  * $FFCC ; Reserved
        FDB     BadInt  * $FFCE ; Reserved
        FDB     BadInt  * $FFD0 ; Reserved
        FDB     BadInt  * $FFD2 ; Reserved
        FDB     BadInt  * $FFD4 ; Reserved
 
        FDB     BadInt  * $FFD6 ; SCI Serial System
        FDB     BadInt  * $FFD8 ; SPI Serial Transfer Complete
        FDB     BadInt  * $FFDA ; Pulse Accumulator Input Edge
        FDB     BadInt  * $FFDC ; Pulse Accumulator Overflow
        FDB     BadInt  * $FFDE ; Timer Overflow
        FDB     BadInt  * $FFE0 ; In Capture 4/Output Compare 5 (TI4O5)
        FDB     BadInt  * $FFE2 ; Timer Output Compare 4 (TOC4)
        FDB     BadInt  * $FFE4 ; Timer Output Compare 3 (TOC3) 
        FDB     BadInt  * $FFE6 ; Timer Output Compare 2 (TOC2)
        FDB     BadInt  * $FFE8 ; Timer Output Compare 1 (TOC1)
        FDB     BadInt  * $FFEA ; Timer Input Capture 3 (TIC3)
        FDB     BadInt  * $FFEC ; Timer Input Capture 2 (TIC2)
        FDB     BadInt  * $FFEE ; Timer Input Capture 1 (TIC1)
        FDB     BadInt  * $FFF0 ; Real Time Interrupt (RTI)
        FDB     BadInt  * $FFF2 ; External Pin or Parallel I/O (IRQ)
        FDB     BadInt  * $FFF4 ; Pseudo Non-Maskable Interrupt (XIRQ)
        FDB     BadInt  * $FFF6 ; Software Interrupt (SWI)
        FDB     BadInt  * $FFF8 ; Illegal Opcode Trap ()
        FDB     BadInt  * $FFFA ; COP Failure (Reset) ()
        FDB     BadInt  * $FFFC ; COP Clock Monitor Fail (Reset) ()
        FDB     Start   * $FFFE ; /RESET
        END

6. LCD software posting for 68HC11

(From Christopher E. Piggott, WZ2B)
 
 
*
* Routines for the Hitachi HD44780 LCD controller, as used in many
* Optrex and Densitron text-only displays
*
* Written for the 68HC11 by:
*
* Christopher Piggott, WZ2B
* Rochester, New York
*
*
* This stuff is open for anybody to use as they wish.  I only ask
* that you give me a credit for anything you use or distribute,
* and I warn that there is no warranty whatsoever.
*
***
*
* This should be fairly useful even if you don't know the 68HC11
* language or architecture.  Let me explain that I have the LCD
* display hooked up to general-purpose I/O port PRTC, which is an
* 8-bit bi-directional port.  The DDR (data direction register for
* each port) selects whether the pins of PORT C are inputs or outputs.
*
* In addition to PORT C, you need to have a READ/WRITE line, an ENABLE
* line, and a REGISTER SELECT line.  The RS sets whether you are writing
* data to the display, or are writing to one of the internal
* configuration registers.
*
* I'm not sure how much info you guys need on configuring the displays,
* but there is a lot of stuff to configure.  This is definitely enough
* to get started.
*
* One other thing about the 68HC11 - there are two index registers, X
* and Y.  In this program, I'm assuming that all of the 'HC11's I/O
* port registers (memory mapped) are in the $1000-103F block.  Therefore,
* I'm loading Y with $1000 and accessing the ports indexed to Y.
*
*
* BTW, this assembles with the 2500AD cross-assembler for the IBM PC.
* If you're unfortunate enough to be using the PASM stuff that comes
* from Motorola, you may have to massage it a little, but the code
* stays the same.
*
*
* Good luck
*
* Chris, WZ2B, cep4478@ultb.isc.rit.edu
*

BASE:           EQU     $1000                   ; base address of ports
DDIR:           EQU     07h                     ;       offset to DDRC
DPORT:          EQU     03h                     ;       offset to PRTC
CTRL:           EQU     04h                     ;       offset to PRTB

RW:             EQU     01h                     ; READ/WRITE bit
RS:             EQU     02h                     ; Register Select bit
ENABLE:         EQU     04h                     ; ENABLE bit
BUSYBIT:        EQU     80h                     ; BUSY flag

**********************************************************************

* This can start anywhere you want.  I wrote this for the 'HC11EVB
* so I stuck it at $C000h.

        ORG     $C000

        JSR     initlcd

        LDX     #string1
loop1:
        LDAA    0,X
        BEQ     done1
        JSR     print
        INX
        BRA     loop1

done1:
        LDX     #string2
loop2:
        LDAA    0,X
        BEQ     done2
        JSR     print
        INX
        BRA     loop2

done2:
        SWI
        STOP

**********************************************************************

string1:        FCC     "The Earth is flat"
                FCB     00h
string2:        FCC     "-- Thomas Dolby"
                FCB     00h

**********************************************************************

initlcd:
        LDY     #BASE

        LDAA    #0FFh                   ; set PORT D to be OUTPUTS
        STAA    DDIR,Y

        LDAA    #38h                    ; configure display
        JSR     command

        LDAA    #0Dh                    ; turn on display, block cursor
        JSR     command

        LDAA    #01h                    ; clear screen
        JSR     command

        RTS


**********************************************************************
* wait:
*       spinlock until LCD is ready to accept another command
*       (the LCD controller is a little slow and can only process
*       one command or data word at a time)
**********************************************************************

wait:
        BCLR    DDIR,Y,BUSYBIT                  ; SET BASE+DDIR TO OUTPUT
        BCLR    CTRL,Y,RS                       ; RS=0 (register)
        BSET    CTRL,Y,RW                       ; RW=1 (write)

wloop:
        BSET    CTRL,Y,ENABLE                   ; strobe enable
        BRCLR   DPORT,Y,BUSYBIT,ready           ; if DATA & 7 jmp to ready
        
notready:
        BCLR    CTRL,Y,ENABLE                   ; turn off enable bit
        BRA     wloop

ready:
        BCLR    CTRL,Y,ENABLE                   ; turn off enable bit
        BSET    DDIR,Y,BUSYBIT                  ; set DDRA for output
        RTS
        
**********************************************************************
* command:
*       transmit a command from the accumulator to the LCD display
**********************************************************************

command:
        JSR     wait                            ; make sure dispay is not busy
        STAA    DPORT,Y                         ; put command on data bus
        BCLR    CTRL,Y,RW                       ; RW = 0 (write)
        BCLR    CTRL,Y,RS                       ; RS = 0 (command)
        BSET    CTRL,Y,ENABLE                   ; strobe ENABLE
        BCLR    CTRL,Y,ENABLE                   ; clear ENABLE

        RTS

**********************************************************************
* print:
*       output a character from the accumulator to the LCD display
**********************************************************************

print:
        JSR     wait                            ; make sure display is not busy
        STAA    DPORT,Y                         ; put A on data bus
        BCLR    CTRL,Y,RW                       ; RW = 0 (write)
        BSET    CTRL,Y,RS                       ; RS = 1 (data)
        BSET    CTRL,Y,ENABLE                   ; strobe enable
        BCLR    CTRL,Y,ENABLE                   ; clear enable
        BCLR    CTRL,Y,RS                       ; RS = 0 (command)

        RTS

**********************************************************************
        
        END

7. LCD software for 68HCx05

(From Christopher E. Piggott, WZ2B)


*
* Routines for the Hitachi HD44780 LCD controller, as used in many
* Optrex and Densitron text-only displays
*
* Written for the 68HC05 by:
*
* Christopher Piggott, WZ2B
* Rochester, New York
*
*
* This stuff is open for anybody to use as they wish.  I only ask
* that you give me a credit for anything you use or distribute,
* and I warn that there is no warranty whatsoever.
*
***
*
* These routines do the same thing as the 68HC11 ones I just posted.
* They are a little more complete.  However, I think that they are
* also a little more confusing.
*
* This is a code fragment.  I'm not really worried about it, though,
* because there aren't nearly as many guys fooling with the 'HC05 as
* there are with the 'HC11.  Therefore, while this code is certainly
* executable "as-is", I'm mainly just providing it as a reference
* for people to learn how to write their own stuff.
*
* One other thing...in case anybody didn't catch this already, these
* displays are available on the surplus market fairly CHEAP.  Also,
* DigiKey sells them.  There are lots of configurations - I have mostly
* 20x4 and 16x2 versions.
*
*
*
* Good luck
*
* Chris, WZ2B, cep4478@ultb.isc.rit.edu
*

*
* This contains three separate files.  I'll let you split them up
* yourself.  The first two are short.
*
*

********************** THIS IS FILE LCD.H **********************

BLINK_ON:       EQU     00001001b
BLINK_OFF:      EQU     00001000b
DISPLAY_ON:     EQU     00001100b
DISPLAY_OFF:    EQU     00001000b
CURSOR_ON:      EQU     00001010b
CURSOR_OFF:     EQU     00001000b

PUTS:   .MACRO  ARG1
                CLRX
puts_loop#:     LDA     ARG1,X
                BEQ     puts_ret#
                JSR     putchar
                INCX
                BRA     puts_loop#

puts_ret#:      .ENDM

********************** THIS IS FILE PORTS.H **********************

DDRA            EQU     $0004
PRTA            EQU     $0000
DDRB            EQU     $0005
PRTB            EQU     $0001
DDRC            EQU     $0006
PRTC            EQU     $0002
DDRD            EQU     $0007
PRTD            EQU     $0003

RW              EQU     2
RS              EQU     3
ENABLE          EQU     4
BUSY            EQU     7

lcdctrl         EQU     PRTD
lcddata         EQU     PRTC
lcdcdir         EQU     DDRD
lcdddir         EQU     DDRC

OPTIONINIT:     EQU     11000010b
COPCR:          EQU     $1E

TCNTH           EQU     $18
TCNTL           EQU     $19
OCHR            EQU     $16
OCLR            EQU     $17
TCR             EQU     $12
TSR             EQU     $13

ONE_MSEC        EQU     307d

********************** THIS IS THE MAIN FILE, LCD.ASM **********************

                INCLUDE ports.h
                INCLUDE lcd.h

                PUBLIC  initlcd,putchar,lcdclr,lcdloc,lcdcmd

                CODE

*****************************************************************************
* SUBROUTINE
*       initlcd - initialize LCD display
*
* NOTES
*       destroys accumulator
*****************************************************************************

initlcd:
                LDA     #FFh                    * set ports to OUTPUT
                STA     lcdcdir
                STA     lcdddir

                LDA     #00111000b              * select 8-bit bus
                JSR     lcdcmd

                LDA     #(DISPLAY_ON^BLINK_ON)

                JSR     lcdcmd                  * with block style cursor

                LDA     #01h                    * clear screen
                JSR     lcdcmd

                RTS                             * that's all, folks

*****************************************************************************
* SUBROUTINE
*       putchar - put a byte out the LCD data port
*
* INPUT
*       A - byte to display
*****************************************************************************

putchar:
                JSR     wait                    * wait until LCD is ready
                STA     lcddata                 * store data on data bus

                BCLR    RW,lcdctrl              * RW=0 (write)
                BSET    RS,lcdctrl              * RS=1 (DATA)

                BSET    ENABLE,lcdctrl          * strobe ENABLE line
                BCLR    ENABLE,lcdctrl

                RTS                             * that's all, folks

*****************************************************************************
* SUBROUTINE
*       lcdcmd - send a command to the LCD display
*
* INPUT
*       A - byte to put into LCD command register
*****************************************************************************

lcdcmd:
                JSR     wait
                STA     lcddata
                BCLR    RW,lcdctrl
                BCLR    RS,lcdctrl
                BSET    ENABLE,lcdctrl
                BCLR    ENABLE,lcdctrl

                RTS
        
*****************************************************************************
* SUBROUTINE
*       wait - spinlocks until LCD display is ready to accept data or
*               a command
*****************************************************************************

wait:
                CLR     lcdddir                 * set data port to inputs

                BCLR    RS,lcdctrl              * RS=0 (select register)
                BSET    RW,lcdctrl              * RW=1 (read)
loop:
                BSET    ENABLE,lcdctrl          * strobe ENABLE on
                BRCLR   BUSY,lcddata,ready      * if busy, quit now

notready:
                BCLR    ENABLE,lcdctrl          * otherwise, turn off ENABLE
                BRA     loop                    * and test it again

ready:
                BCLR    ENABLE,lcdctrl          * turn off ENABLE bit
                DEC     lcdddir                 * reset data back to outputs

                RTS                             * that's all, folks

*****************************************************************************
* SUBROUTINE
*       puts - put a string on the LCD display
*
* INPUT
*       X - points to start of NULL-TERMINATED string
*
* NOTES
*       destroys accumulator
*****************************************************************************

puts:
                LDA     $160,X
                BEQ     puts_ret
                JSR     putchar
                INCX
                BRA     puts

puts_ret:
                RTS

*****************************************************************************
* SUBROUTINE
*       lcdloc - position the cursor of the LCD display
*
* INPUT
*       A - position to which to set cursor
*
* NOTES
*       MSB of A gets set on return
*****************************************************************************

lcdloc:
                ORA     #80h
                JSR     lcdcmd

                RTS

*****************************************************************************
* SUBROUTINE
*       lcdclr - clear LCD display
*
* NOTES
*       accumulator destroyed
*****************************************************************************

lcdclr:
                LDA     #01h
                JSR     lcdcmd

                RTS

8. Code for PC & 20x4 display

(From "J. R." Clyde A. Spidell)

 Check the controler on your LCD if it is a HD44780, the following code will work if modified a bit.

 This is my code.. it is for a 20x4 LCD but the commands are the same... You will need to midify the set cursor location routine...
 
 

//Code for 20x4 LCD
//Copyright (c) 1994
//by "J. R." Clyde A. Spidell

#include "dos.h"
#include "stdio.h"
#include "timer.h"
#include "lcd.h"
#include "charlcd.h"
#include "error.h"

//Constants
const unsigned int LCD_BASE = 0x100;
const unsigned int LCD_CMD = 0x00;
const unsigned int LCD_STATUS = 0x02;
const unsigned int LCD_READ = 0x06;
const unsigned int LCD_WRITE = 0x04;
const unsigned char CLEAR_DISPLAY = 0x01;
const unsigned char HOME_CURSOR = 0x02;
const unsigned char SET_DEFAULT_MOVE_CURSOR_RIGHT = 0x06;
const unsigned char DISPLAY_ON = 0x0C;
const unsigned char CURSOR_ON = 0x02;
const unsigned char BLINK_ON = 0x01;
const unsigned char MOVE_CURSOR_RIGHT = 0x14;
const unsigned char MOVE_CURSOR_LEFT = 0x10;
const unsigned char SET_8BITS_4LINES_10DOTS = 0x3C;
const unsigned char SET_DRAM_ADDRESS = 0x80;
const unsigned char BUSY_FLAG = 0x80;
const unsigned int ROWS = 4;
const unsigned int COLUMNS_PER_ROW = 20;
unsigned long SHORT_DELAY = 5;
unsigned long LONG_DELAY = 0xb00;

void CharLCDHomeCursor(void)
{       outp( LCD_BASE+LCD_CMD, HOME_CURSOR );
        microdelay( LONG_DELAY );
}

//ClearScreen.  The cursor is sent to the home position
void CharLCDClearScreen(void)
{
        LCDHomeCursor();
        outp( LCD_BASE+LCD_CMD, CLEAR_DISPLAY );
        microdelay( LONG_DELAY );
}

//Set Cursor Location
//This sets the cursor's position using row/column notation
//The top left corner is row 0, column 0
//The bottom right corner is row 3, column 19
void CharLCDSetCursorLoc(int row, int column)
{
        int position_number;
        row %= ROWS;
        column %= COLUMNS_PER_ROW;
        switch (row)
        {
                case 1: row=2; break;
                case 2: row=1; break;
        }
        position_number = row*COLUMNS_PER_ROW + column;
        if (position_number > 39)
                position_number += 24;
        outp( LCD_BASE+LCD_CMD, SET_DRAM_ADDRESS | position_number );
        microdelay( SHORT_DELAY );
}

//Print String
//This will display a given string on the lcd screen
void CharLCDPrintString(int row, int column, const char far* print_string)
{       char far* current_char;
        (const char far*) current_char = print_string;
        LCDSetCursorLoc(row,column);
        while (*current_char != 0 )
        {
                if ( *current_char == '\n' )
                        column = COLUMNS_PER_ROW;
                else
                {       outp( LCD_BASE+LCD_WRITE, *current_char );
                        microdelay( SHORT_DELAY );
                        column++;
                }
                if ( column >= COLUMNS_PER_ROW )
                {       ++row;
                        column = 0;
                        LCDSetCursorLoc( row, column );
                }
                current_char++;
        }
}

//Print Character
//This will display a given character on the lcd screen
void CharLCDPrintChar( int row, int column, const char c )
{       LCDSetCursorLoc(row,column);
        outp( LCD_BASE+LCD_WRITE, c );
        microdelay( SHORT_DELAY );
}

//Put Character
//This will display a given character on the lcd screen
//Arguments are:Character to be printed
//Assumes last cursor location
void CharLCDPutChar( char c )
{       outp( LCD_BASE+LCD_WRITE, c );
        microdelay( LONG_DELAY );
}

//LCD Initialization
void CharLCD(void)
{
        outp( LCD_BASE+LCD_CMD, SET_8BITS_4LINES_10DOTS );
        microdelay( LONG_DELAY );
        outp( LCD_BASE+LCD_CMD, SET_DEFAULT_MOVE_CURSOR_RIGHT );
        microdelay( LONG_DELAY );
        outp( LCD_BASE+LCD_CMD, CLEAR_DISPLAY);
        microdelay( LONG_DELAY );
        outp( LCD_BASE+LCD_CMD, DISPLAY_ON+CURSOR_ON+BLINK_ON );
        microdelay( LONG_DELAY );
}

9. Test Hitachi LCD with Parallel Port on a PC

(From David Tait (david.tait@man.ac.uk))
/*
 *  TESTLCD.C
 *
 *  Test a Hitachi HD44780 based LCD module by using the PC parallel
 *  port to write a string to the display.  Compile with a Borland C
 *  or C++ compiler (DOS target) (note: early versions of Turbo C had
 *  a bug in the delay routine which is needed by this program).  To
 *  simplify things long delays are used rather than checking the BUSY
 *  flag.
 *
 *
 *  Usage:
 *          testlcd               print the default string
 *          testlcd string        print the given string (but not "-")
 *          testlcd -             print stdin (and scroll if necessary)
 *
 *
 *  The LCD is connected with a 4-bit data interface:
 *
 *        LPT Signal     LPT pin      LCD Signal    LCD pin
 *        ==========     =======      ==========    =======
 *       SELECT PRINTER    17            DB7           14
 *       INIT PRINTER      16            DB6           13
 *       AUTO LINEFEED     14            DB5           12
 *       STROBE             1            DB4           11
 *       D0                 2            EN             6
 *       D1                 3            R/W            5
 *       D2                 4            RS             4
 *                                       VO  (*)        3
 *                                       VDD (+5V)      2
 *       GND               25            VSS            1
 *
 *
 *       (*) VO is connected to the slider of a 10k pot between VDD
 *           and VSS
 *
 *
 *  Copyright (C) 1996 David Tait
 *  Free for non-profit use and like anything that's free this software
 *  comes with absolutely no warranty
 *
 *  Version 0.0  3rd March 1996
 *
 */

#include <stdio.h>
#include <stddef.h>
#include <dos.h>

#define CHARS               16                      /* LCD width in chars */
#define out_data(w)         outportb(c_reg,(w)^0xB) /* bits 3,1,0 inverted */
#define out_cntl(en,rw,rs)  outportb(d_reg,(en)|((rw)<<1)|((rs)<<2))
#define move_to(a)          write8(0x80+a,0,1)
#define home()              write8(2,0,5)
#define cur_off()           write8(0xC,0,1)


int d_reg;
int c_reg;


void idle(void)
{
    out_cntl(0,1,1);
    out_data(0xF);     /* all O/C ouputs high and can be used as inputs */
}


void setup(void)
{
    d_reg = peek(0, 0x408);      /* port address of LPT1 data register */
    c_reg = d_reg+2;             /* port address of LPT1 control register */
}


void write4(int w, int rs, int ms)
{
    out_data(w);
    out_cntl(0,0,rs);            /* cycle EN */
    out_cntl(1,0,rs);
    out_cntl(0,0,rs);
    delay(ms);
}


void write8(int w, int rs, int ms)
{
    write4((w&0xF0)>>4,rs,0);         /* write high nibble */
    write4(w&0xF,rs,ms);              /* then low nibble */
}


void init_lcd(int n)
{
    idle();
    delay(15);                /* not necessary here of course */
    write4(3,0,5);            /* set 8-bit mode */
    write4(3,0,5);            /* and again */
    write4(3,0,5);            /* and again */
    write4(2,0,5);            /* set 4-bit mode */
    write8(0x20+8*n,0,5);     /* set 4-bit mode, n+1 lines, 5x7 dots */
    write8(6,0,5);            /* set cursor to move forward */
    write8(1,0,5);            /* clear display */
    write8(0xF,0,5);          /* display on, cursor on, blink */
    idle();
}


void print(char *s)
{
    while ( *s )
        write8(*s++,1,1);
    idle();
}


void scroll(void)
{
    int c, n=0;

    init_lcd(0);                       /* select one line mode */
    cur_off();
    while ( (c=getchar()) != EOF ) {
        if ( n == CHARS )
            write8(7,0,1);             /* need to enable scrolling now */
        write8(c,1,1);
        delay(150);
        ++n;
    }
    idle();
}


void main(int argc, char *argv[])
{
    setup();
    init_lcd(1);                        /* two line mode */

    if ( argc < 2 ) {                   /* print default string */
        print("Look:");
        move_to(40);
        print("4bit mode works!");
        home();
        cur_off();
        idle();
    } else if ( *argv[1]=='-' )
        scroll();                       /* or print stdin */
    else
        print(argv[1]);                 /* or print argument */
}


Please check attribution section for Author of this document! This article was written by filipg@repairfaq.org[mailto]. The most recent version is available on the WWW server http://www.repairfaq.org/filipg/[Copyright][Disclaimer]