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)
[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
.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
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
* * 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
* * 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
* * 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
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 ); }
/* * 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 */ }