LCD Module 4 Bit Mode with 6522 VIA
I have a 16 x 2 line LCD module connected directly to the 6502 data bus (see an earlier post for details) and in discussions it was mentioned that a better way to connect it would be using a 6522 VIA. Even better would be to use only 6 pins by putting the module into 4 bit mode. Turned out to be a bit tricky to initialise the LCD module in that mode but got there....
Objectives
- Connect the module through a 6522 VIA using 4 data bits
- Understand how to use BUSY flag in 4 bit mode
- Compare advantages of 4 bit and 8 bit modes
Background
I have a working 16 x 2 line module connected directly to a 65C02 data bus, A0 for register select and RW for read / write. The module needs a positive pulse on the 'E' pin for it to read or write - mine is generated by the address decoding logic. It is in 8n bit mode.
Moving to 4 bit mode required further investigation of the initialisation steps and this turned out to be interesting to I thought it would be worth writing up.
Moving to 4 bit mode required further investigation of the initialisation steps and this turned out to be interesting to I thought it would be worth writing up.
Modules with Hitachi controllers will properly self-initialize if Vcc rises from 0 to 4.5v in a period between 0.1 mS and 10 mS. This is difficult to guarantee, so manual initialisation is required in most applications. If you do use auto-initialization, it will come up in this mode: 8-bit interface, 1/8 duty cycle (1 line mode), 5x8 font, cursor increment right, no shift. On most displays, you want to switch to 1/16 duty cycle (2 line mode) because for all but the 20x1, there are two logical lines as the controller sees it. If you have an 5x11-size character dot matrix module, you'll want to switch to the 5x10 font as well (the 11th line is the cursor).
Manual initialisation requires more steps which if left out means the module will only work correctly IF auto-initialisation works. The safest assumption is to manually initialise and that will now be explained in detail.
The module powers up in 8-bit mode. The initial startup instructions are sent in 8-bit mode, with the lower four bits (which are not connected) of each instruction as don't cares. After the fourth instruction, which switches the module to 4-bit operation, the control bytes are sent on consecutive enable cycles (no delay is required between nibbles). The most significant nibble is sent first, followed immediately by the least significant nibble.
<POWER ON>
<Wait at least 15ms>
<POWER ON>
<Wait at least 15ms>
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 n/c n/c n/c n/c
<Wait at least 4.1ms> RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 n/c n/c n/c n/c
<Wait at least 100us>RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 1 n/c n/c n/c n/c
<Wait at least 100us>RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 0 0 1 0 n/c n/c n/c n/c 4-bit operation
<Wait 40us or till BF=0>
From now on the initialisation steps should be sent as two nibbles in 4 bit mode with two enable pulses. The BUSY flag (BF) can now be used and is more efficient than waiting for a delay.
RS R/W DB7 DB6 DB5 DB4
0 0 0 0 1 0
0 0 N F x x N=Number of lines
0 -- 1 line
1 -- 2 lines
F=font
1 for 5x11 dot matrix
0 for 5x8 dot matrix
x=don't care
<Wait 40us or till BF=0>
RS R/W DB7 DB6 DB5 DB4
0 0 0 0 0 0
0 0 1 0 0 0 Display off, cursor off, blink off
<Wait 40us or till BF=0>
RS R/W DB7 DB6 DB5 DB4
0 0 0 0 0 0
0 0 0 0 0 1 Clear screen, cursor home
<Wait 1.64ms or till BF=0>
RS R/W DB7 DB6 DB5 DB4
0 0 0 0 0 0
0 0 0 1 1 0 Increment cursor to the right
when writing, don't shift screen
<Wait 40us or till BF=0>
<INITIALIZATION COMPLETE>
RS R/W DB7 DB6 DB5 DB4
0 0 0 0 0 0
0 0 1 1 0 0 Display on, cursor off, blink off
Connections
D4 - D7 are the only data pins used in this mode and are connected to PB0 - PB3. RS, RW and E use PB5 - PB7.
I explored using the VIA's handshaking to generate the E pulse and it worked on for writing but not for reading so I reverted to using PB7.
Here is a link to a Gist containing the test code: https://gist.github.com/robinharris/31ba1419e40896f9822a39cfa4ee6bdb
Writing to the module turned out to be straightforward: set the register (0 for command and 1 for data), hold RW low, present the high nibble on D4 - D7 and pulse E high. The critical timing for E is the falling edge. All PortB is set to output. It is perfectly possible to only write to the module providing the required delay is maintained between instructions and data for the internal process to complete. Failure to wait the required time results in the second write being ignored.
A better way is to check the BUSY FLAG (bit 7) after writing and wait till it clears. The steps required are:
- change PortB PB0 - PB3 to input
- set RW high
- set RS low (command register)
- Take E high
- Read high nibble
- Take E Low
- Take E high
- Read low nibble
- change PortB PB0 - PB3 back to output ready for next write
Note that for reads it is the LEADING edge of the E pulse that sets up the data on D4 - D7 so this must be read BEFORE taking E low again. Two pulses of E are required to read both nibbles. Although we only need the high nibble to check the BF, it is important to read the low nibble because the controller is expecting a second pulse of E before becoming ready to receive another command or data.
Sending commands or data in two nibbles is straightforward - high nibble first. Pulse E high, send low nibble and pulse E a second time. No delay is required.
Conclusions
- 4 bit mode is slightly more complicated to set up
- Initialisation is critical and the data sheet does not explain it well - 4 bit or 8 bit mode
- Auto-initialisation may work - sometimes. Beware!
- 4 bit mode works just as well as 8 bit mode and saves 4 pins. These devices are slow so the extra time required to send two nibbles is not likely to be significant.
This post will serve as my record of the learnings I made: if they help anyone else then great! I'd be happy to answer any questions - just leave a comment below.
Hi Robin. I'm following this closely as you know. I must point out that I think you have an error in your wiring diagram above relating to pins PB5 & PB6... they do not reflect whats in the assm code. Your code sets PB6 = RW and PB5 = RS.
ReplyDeleteHi - well spotted; you certainly are following closely to notice that! Thanks so much for letting me know - it was a my mistake and I have corrected it. These posts are for my reference too so it is important they are accurate and complete.
ReplyDelete