<< BACK | NEXT >>

6502 project – Peripherals – 44780 LCD

[15th February 2015]

This is part of my 6502 project, the introduction to which can be found here.

With the main prototype constructed and working, I decided to start adding some peripherals. And again, this is/was totally new to me so had a vertical learning curve (as usual! :D></p>

  </div>


    <p style=).

LCD Display

My choice of display was initally to use a series of 7 segment hexidecimal LED displays. After much searching on the internet and checking forums it seems that these are now quite obsolete and difficult to get hold of. The BCD (binary coded decimal) ones are more prevalent, but my requirements are to be able to display hex values so the BCD displays cannot not be used (hex displays 0-9, A-F where-as BCD dislays 0-9).

I then moved on to looking at LCD displays and here's where I found what I needed. It seems that hobbyists have been using the 44780 Smart LCD displays for years and are perfect for this kind of project. They allow for very easy interfacing, are relatively cheap and don't consume a lot of power. The only downside with the 44780 is that the maximum size seems to be 20 characters x 4 lines. Ideally, I'd liked to have had a larger display, but there may be ways around this later on and to be fair it's not a major issue at the moment.

veroboard_6502_1
veroboard_6502_1
veroboard_6502_1
7 segment LEDs
2 lines x 16 chrs LCD
4 lines x 20 chrs LCD

Interfacing an LCD display

The connections on a 44780 LCD display are quite straight forward. Here is a diagram which helps explain them:

veroboard_6502_1

The connections can be grouped into power, control lines, data lines and backlight:
VSS and VDD - these are the ground (0V) and +5V lines respectively.
VO - contrast control for the displayed characters.
A and K
- these are the anode and cathode of the backlight. Some people connect A directly to +5V and 0V to K; Some use a pot between +5V and A which allows for the dimming of the backlight..
DB0 to DB7 - these are the data lines which are used to transit either 8 bits (utilising DB0-7) or 4 bits (utilising DB4-7)
E - this is the enable line.
R/W
- this is the read/write line.
RS - this is the register select line.

The lines that you have to control via a microcontroller/processor or I/O chip are DB0-7 for data and E, R/W and RS.
DB0-7 contains an eight bit command or data byte
R/W determines if we're sending data, a command or reading data/status. See RS line below.
RS is used in conjunction with R/W. It determines the following:

RS status R/W status Effect
0 0 Send command/instruction
0 1 Read busy flag
1 0 Send data to data register (write character)
1 1 Read data from data register
E is used to send a command or data. This is done on the falling edge.

So, the process of sending a command would be:

Load DB0-7 with the command code
Set RW to 0 (we're writing to the LCD)
Set RS to 0 (we're sending a command)
Set E to 1 (bring E high so...
Set E to 0 ...that we can bring it low - everything actions on the falling edge of E)

And for sending data (writing to the screen):

Load DB0-7 with the data byte. Typically an ASCII code.
Set RW to 0 (we're writing to the LCD)
Set RS to 1 (we're sending data)
Set E to 1 (bring E high so...
Set E to 0 ...that we can bring it low - everything actions on the falling edge of E)

As you can see, it's all fairly simple stuff. However, before we can display anything a certain sequence of commands must be followed before the LCD will work. This gave me quite a few problems before I got it working. The most helpful document I found with regard to the initialisation was a manual provided by a helpful member of the 6502.org forums called BitWise. I've provided it here for easy reference: 44780 LCD user manual. The initialisation commands can be found from page 30 onwards, although the whole thing is worth a read.

There is one more thing to mention. The LCD can be operated in two modes: 8 bit mode and 4 bit mode.

8 bit mode is what is described above and utilises all 8 data lines (DB0-7) to send information to the LCD.
4 bit mode makes use of only 4 lines (DB4-7), causing the LCD to accept two lots of 4 bits to make the 8 bits needed (bits 4-7 then bits 0-3). Each set of bits is action by the falling edge of E (so E is set high then low, twice over). The remaining 4 lines (DB0-3) are either left unconnected or connected to ground/0V.
The main use for 4 bit mode is to enable you to reduce the number connects required to talk to it. This can be extremely useful especially when dealing with microcontrollers or where space for traces on a PCB is limited.

Hooking up the LCD to my prototype

There are a couple of options for this. One is to connect the LCD directly to the data bus and make it a part of the memory map. The other is to go through an I/O chip.

The advantage of the former is to reduce the physical complexity of the task and to save using an I/O chip.
The disadvantage is quite large: Due to the slowness of the LCD, the 6502 can only be clocked at 1MHz or lower. Any faster than this and the LCD cannot keep up. As I have no intention of running this at 1MHz I've decided not to go with this option.

This leaves me using an I/O chip, which means a 65C22 VIA. The great thing about this option is that it's very easy to program a VIA and they're part of the same family as the 65C02 and so there aren't any timing issues to deal with.

Here's a diagram of how I connected the VIA to the my LCD:

Connecting 6522 VIA to 44780 LCD
(click image to enlarge)

Coding the VIA (basics)


My choice to connect the LCD up to my 65C22 VIA has determined how I program the LCD. The VIA hooks into the memory map of my project at address $6000 to $600F (so 16 bytes) with each address representing a register in the VIA controlling a particular function. These registers are expressed as an offset of the base address (which in this case is $6000). A list of these registers and their offsets can be found on page 8 of the 65C22 manual.

The registers that we are interested in are:

DDRA - Data Direction Register - Found at address $6003 (i.e. at the +3 byte offset) - Sets pins on port A as input (logic 0) or output (logic 1). Each pin can be set as either - the whole lot do not need to be all one thing or the other together.
ORA/IRB - Output/Input Register port A - Found at address $6001 (i.e. at the +1 byte offset) - If pins on port A are set to output then writing data to this register will cause it to be outputted on port A for those pins. If port A is set to input then reading this will read the bits on Port A for those pins.

So, to set the the data direction of all the pins on port A to output we need to write logic 1 to all 8 lines. Binary %11111111 is equal to 255 which in hex is $FF:

 
65C02 Code:
LDA #$FF
STA $6003

Now to write a test value (%10101010 / 170 / $AA) so that it appears on the port A pins:

 
65C02 Code:
LDA #$AA
STA $6001



Initialising the LCD

Now that we know how to read/write values from/to the VIA ports we need to write some code to initialise the LCD. As part of that we need to write routines which send both 8 bit and 4 bit mode values.

Routines for writing 8 bit and 4 bit values:

 
65C02 Code:
.LCDwrite8bitmode
' parameter: A = command byte or data byte
' parameter: Y = 0 [LCD command] or 1 [LCD data]
' A, Y are both utilised, X is not


' ---- MSB ----
' Writes the top most 4 bits (bits 7,6,5 and 4) to the VIA. 4 bits is known as a nibble (or nybble)
' Get the high nibble by shunting bits 4-7 to positions 0-3, 4-7 will then be 0's
' dddd xxxx => 0000 dddd

LSR A
LSR A
LSR A
LSR A

' Determine if this is an LCD command or is data and set or clear bit 6 (RS) accordingly. This is determined by the 65C02 calling routine/code setting the Y register to 0 or 1. 0 = LCD data, 1 = LCD command.
CPY #0
BNE LCDisdata8bitmode

.LCDiscmd8bitmode
' $BF = 1011 1111, sets bit 6 to 0
AND #%10111111
BRA LCDcmddata_done8bitmode

.LCDisdata8bitmode
' $40 = 0100 0000, sets bit 6 to 1
ORA #%01000000

.LCDcmddata_done8bitmode
' Set bit 5 of A so that E is high. $10 = %0001 0000 => %xxx1 xxxx. Send data to VIA
ORA #%00010000
STA VIA_outA
' The delay40 routine (not included here) is used to pause things to allow the LCD to do what it needs to do and then be ready for the next lot of information from this routine.
JSR delay40

' Clear bit 5 of A to that E is low. $EF = %1110 1111 => %xxx0 xxxx. Send data
AND #%11101111
STA VIA_outA
JSR delay40

RTS

 
65C02 Code:
.LCDwrite4bitmode
' parameter: A = command byte or data byte
' parameter: Y = 0 [LCD command] or 1 [LCD data]
' A, Y are both utilised, X is not


' each nibble is sent twice - once to set E high, then once to set E low. Reason: The LCD
' actions on the falling edge of E.

' ---- MSB ----
' Get the high nibble but shunting bits 4-7 to positions 0-3, 4-7 will then be 0's
' dddd xxxx => 0000 dddd LSR A LSR A LSR A LSR A

' determine if this is an LCD command or is data and set or clear bit 6 (RS) as needed

CPY #0
BNE LCDisdatamode
.LCDiscmd
' $BF = 1011 1111, sets bit 6 to 0
AND #%10111111
JMP LCDcmddata_done
.LCDisdata
' $40 = 0100 0000, sets bit 6 to 1
ORA #%01000000
.LCDcmddata_done

' Set bit 5 of A so that E is high. $10 = %0001 0000 => %xxx1 xxxx. Send to VIA.
ORA #%00010000
STA VIA_outA
JSR delay40

' Clear bit 5 of A to that E is low. $EF = %1110 1111 => %xxx0 xxxx. Send to VIA.
AND #%11101111
STA VIA_outA
JSR delay40

' ---- LSB ----
' Restore data to A
PLA
' Get lower nibble of data. $0F = %00001111

AND #%00001111

' determine if this is an LCD command or is data and set or clear bit 6 (RS) as needed
CPY #0
BNE LCDisdataLSB
.LCDiscmdLSB
' $BF = 1011 1111, sets bit 6 to 0
AND #%10111111
JMP LCDcmddata_doneLSB
.LCDisdataLSB
' $40 = 0100 0000, sets bit 6 to 1
ORA #%01000000
.LCDcmddata_doneLSB

' Set bit 5 of A so that E is high. $10 = %0001 0000 => %xxx1 xxxx. Send data
ORA #%00010000
STA VIA_outA
JSR delay40

' Clear bit 5 of A to that E is low. $EF = %1110 1111 => %xxx0 xxxx. Send data
AND #%11101111
' Write to output port
STA VIA_outA
JSR delay40

RTS

 
65C02 Code:
VIA_outA = $6001
VIA_DDRA = $6003

.LCDinit

' set DDRA output

LDA #$FF
STA VIA_DDRA

' PA0-3 = LCD DB4 to DB7
' PA4 = E
' PA5 = R/W 0=ok for next op, 1=LCD is busy
' PA6 = RS 0=command, 1=data
' PA7 = not connected


JSR delay40

' set LCD to command mode
LDY #0

' Send 0011 0000 three times to initialise LCD
LDA #%00110000
JSR LCDwrite8bitmode
LDA #%00110000
JSR LCDwrite8bitmode
LDA #%00110000
JSR LCDwrite8bitmode

' Function set: 4 bit mode [but sent in 8bit mode]
' This is soley to get the LCD into 4 bit mode only
' Send 0010 0000

LDA #%00100000
JSR LCDwrite8bitmode

' Now we can set number of lines etc
' Function set: 4bit, 2line, 5x8 dots
' Send 0010 1000

LDA #%00101000
JSR LCDwrite4bitmode

' Turn display off
' Send 0000 1000

LDA #%00001000
JSR LCDwrite4bitmode

' Clear display
' Send 0000 0001

LDA #%00000001
JSR LCDwrite4bitmode

' Entry Mode Set:
' Send 0000 0110

LDA #%00000110
JSR LCDwrite4bitmode

' Display on, Cursor on, Blink on
' Send 0000 1101

LDA #%00001101
JSR LCDwrite4bitmode

RTS

 


breadboard

 



<< BACK |NEXT >>



Site Map | CSS Hover Menus by Css3Menu.com