XaiJu
beneater
beneater

patreon


4-bit LCD interface

Many of you noticed that for the keyboard videos, I modified the LCD to operate in 4-bit mode, freeing up some IO lines to make the 8-bit keyboard interface possible. I’ve gotten a few messages from different people wondering how that works.

The hardware part is pretty simple. Basically 4 bits of data plus the three control signals from the LCD are all connected to port B on the 6522. The 4 data bits are D4-D7 on the LCD and connected as follows. Pins D0-D3 on the LCD are now unused:

LCD D4 - 6522 PB0 pin 10
LCD D5 - 6522 PB1 pin 11
LCD D6 - 6522 PB2 pin 12
LCD D7 - 6522 PB3 pin 13

The 3 control signals are also moved over to port B as follows:

LCD RS - 6522 PB4 pin 14
LCD RW - 6522 PB5 pin 15
LCD E - 6522 PB6 pin 16

This frees up all 8 bits of port A to use for the keyboard or whatever else.

Here’s the code from the end of the last video, which includes all the LCD subroutines modified for 4-bit operation: https://eater.net/downloads/keyboard.s

There’s a new subroutine, lcd_init, which puts the display into 4-bit mode. After that, all of the instructions and characters are sent to the LCD in two steps, 4 bits at a time. So you’ll see the lcd_instruction subroutine and print_char subroutine are both modified to do that. There is also a modified lcd_wait subroutine since checking the busy flag is a bit more complicated: The status register has to be read in two 4-bit chunks, even though we only care about one bit—the busy flag.

But with those modified subroutines, everything else works pretty much the same. You still call the lcd_instruction and print_char subroutines with the 8-bit character or instruction in the A register, so the rest of your code doesn’t need to change.

One thing that’s a little annoying is that now when you reset the CPU, the LCD might not reset properly. That’s because the lcd_init routine that puts it into 4-bit mode assumes the LCD just powered on and is in 8-bit mode. So if the LCD is already in 4-bit mode and you reset the CPU, the lcd_init will just cause everything else to get out of sync by 4 bits. The solution is to reset the CPU again to get it back in sync. So basically you’ll have to hit reset twice to actually reset it.

Hope that helps!

-Ben

4-bit LCD interface

Comments

Got it to work by adding Set 8-bit mode 3 times before setting the 4-bit mode. My problem was after reset, the LCD got set to 4-bit more but only 1 line mode, using the Enter key made the curser go off screen instead to the 2nd line it went to position 40 but on line 1. lcd_init: lda #%00000011 ; 1st Set 8-bit mode sta PORTB ora #E sta PORTB and #%00001111 sta PORTB lda #%00000011 ; 2nd Set 8-bit mode sta PORTB ora #E sta PORTB and #%00001111 sta PORTB lda #%00000011 ; 3rd Set 8-bit mode sta PORTB ora #E sta PORTB and #%00001111 sta PORTB lda #%00000010 ; Set 4-bit mode sta PORTB ora #E sta PORTB and #%00001111 sta PORTB rts

For people of the future, if you don't want to hit reset twice every time, the HD44780U datasheet has a procedure called "Initializing by Instruction" in Figure 24 (p. 46) that forces a reset in 4-bit mode. Essentially, it sends the high nibble to request 8-bit mode 3 times using only timing between calls, then requests 4-bit mode as usual. The reason that it requests 8-bit mode 3 times is that if it were in 4-bit mode, the first two would be taken as both halves of a request. The third one ensures that it's really in the right state. I implemented this and it works like a charm.

Ian Flanigan

Ok - found an answer: you can't switch. Wikipedia is your friend. The ROM is determined by which version of the HD44780U is used by the LCD. From the article: "Two versions of the ROM have been developed: HD44780UA00, the standard (Japanese) version, which includes katakana characters and some Greek letters and mathematical symbols HD44780UA02, a European version, which includes the complete set of Greek, Cyrillic and Western European characters (with diacritics) The 7-bit ASCII subset for the Japanese version is non-standard: it supplies a Yen symbol where the backslash character is normally found, and left and right arrow symbols in place of tilde and the rubout character."

Tim Walkowski

As an aside to this conversation, does anyone know how to select the ROM code A02 instead of ROM code A00 in the HD4470? ROM A02 will display the "\" and "~" characters correctly while the ROM A00 shows other symbols. (pages 17, 18 of the HD44780U datasheet) The rest of the ROM characters map as expected...

Tim Walkowski

I send 3 instructions for 8-bit mode before sending a single instruction for 4-bit mode in my initialization code and the lcd behaves as expected. However, the display is blank until I press reset after power-up.

Tim Walkowski

No problem, I'm glad it worked for you!

Luke McNinch

Worked like a charm! Thanks again

nevermind... I see it now. Its in software only... no change to the hardware. In the "reset" routine with the lcd_init

xedover

I'm confused. I'm not seeing it... what exactly was it that people noticed and how? I watched all the videos again, and still didn't see it. What am I missing?

xedover

Thanks so much for your help. I can't wait to try it out!

It's not needed. The 74HC595 shift register has that functionality built-in using the output enable pin. Sorry, I was calling it "chip select". Ben ties them low so they're always in output mode, but pulling them high with one of the PORTA pins puts them in 3-state mode so you can use those lines for the LCD.

Luke McNinch

Do you use something like a 74LS245 to isolate the shift register outputs when they're not being used?

Oh yes, it's there on the HD44780 datasheet, just not on the datasheet for the LCD module I was looking at. The suggested timings are more generous than the ones you would derive from my explanation of why it does what it does too. Thanks.

The LCD controller data sheet describes the procedure for software reset and establishing 4 bit mode on page 46. There’s a sequence of commands and also minimum delays specified between some of the commands.

Phil Dennis

I just remembered, you do have to be careful to disable interrupts in your LCD subroutines so you don't mess with PORTB in the middle of LCD communication.

Luke McNinch

It works basically like another data bus. When the LCD isn't enabled, you can do anything with the data bits. The shift register outputs can be disabled. Ben hard-wired the chip select but I connected it to a pin on PORTA which I toggle in the keyboard interrupt handler, then read PORTB. I'm going from memory but I think that's basically it.

Luke McNinch

@Luke McNinch - how did you share PORTB between the shift registers and the LCD?

Oooh cool. I'll have to give this a try. Makes complete sense though.

Ben Eater

Thanks for the tips! I ended up following the advice of another patron and shared PORTB between the shift register and the LCD (8-bit) and used a 4th pin on PORTA for Chip Select on the shift registers. It worked great!

Luke McNinch

There is a process for avoiding the flakiness at startup: the trick is to send 0011???? (on DB7-DB0 where the ?s are not connected) to force it into 8-bit mode first. That command is a no-op if sent multiple times: if it starts up in 8-bit mode then you don't need the command but it doesn't hurt to send it again. If it starts up in 4-bit mode awaiting the first nibble then you can send that command twice to enter 8-bit mode. If it starts up in 4-bit mode awaiting the second nibble then you can send that command three times, once to complete the pending command and twice more to enter 8-bit mode. In any case if you send 0011???? three times you're now in 8-bit mode, so that sending 0010???? puts you into 4-bit mode and then another 0010???? followed by NF00???? sends the actual Function Set command you wanted.

I said "programmable resister because I forgot it was actually called a R2R DAC (I think anyway).

Warren Garabrandt

What if the power for the lcd were going through a mosfet that the CPU could control? Then the cpu could turn on and off the lcd when it needed to reset it, and you could have a screen sleep mode that wakes up from a key press. Maybe shift + a function key can turn the display on and off. I used a 16 bit programmable resister that took spi input to set a resistance value between 0 and 65535 when I was working on a project a while back. Could it be used to set the contrast of the display with a shift + function key for higher and another for lower, like a laptop? These could be a fun thing to do and show how to interface the cpu with outside things.

Warren Garabrandt


More Creators