Connecting an HD44780 based character LCD to an Intel style bus

I’ve been using these displays for 15 years now, but every time, that same old problem comes up: They’re just a bit annoying to interface with.

Despite them being difficult to interface with, HD44780 and compatible clones remain the defacto standard for character LCDs, with no improved replacement in sight.

There are a lot of ‘Backpack’ boards on the market these days which offer an I2C, SPI or even RS232 interface. Although these boards have made interfacing simpler in electrical terms, the software side of things is typically made worse.

The problem

is pretty simple. The HD44780 is a “Motorola bus” peripheral, notable by it having the “E”, and “R/W” signals. They’re only found on 6800 or 68000 derivative microcontrollers.

Strictly speaking, it is only these Motorola processors that can properly interface with an HD44780, for everything else, some kind of fudge is needed.

I’ve personally been endlessly churning out routines to drive these signals under software control, because despite having used scores of different brands of microcontrollers over the years, not one of them has had the magic Motorola bus these displays require.

Recently I found this page which details how to bodge an HD44780 onto an “Intel bus” microcontroller (an AVR in his example).

Let’s have a look at the issue in the simplest possible terms. There are three signals that differ between Intel and Motorola bus, all of which are used by the HD44780.

Motorola bus

motobus

On Motorola bus, the intention to either read or write to the peripheral is indicated at the same time as the address lines are setup, then, a single signal ‘E’ triggers either the read or write.

Intel bus

intelbusOn Intel bus, there is no R/W signal, instead the ‘E’ signal is replaced by two separate read and write signals.

On both a Motorola and Intel system, the RS signal can be connected to the least significant address line (A0), but when it comes to the read and write, we have a slight problem. By the time we know whether or not the CPU is going to read or write, it’s too late, and no, we can’t just deliver the R/W signal at the same time as ‘E’ because, that’s violation of the timing requirements.

We’re left with the unsolvable problem of not having a replacement for R/W. So that’s it. It’s impossible to directly translate Intel bus to Motorola bus.

Solutions

Because the HD44780 only has one address line, there’s a simple bodge which offers an acceptable solution to this problem:

intelbus1

 

Here, we’ve used an extra address line to drive R/W, and RD and WR are NAND’d into E.

This means the HD44780’s two registers end up with four addresses, two read, and two write. It’s not a direct conversion, but at a pinch, I’ll take it.

Why go to all this all this effort?

For the most part, yes, this is extra hassle to connect a character LCD, but let’s look at the difference this makes to the software.

Before

This is an example of the minimum code which writes a character to the display using the most common ‘bit bang’ mode. This was taken from my 8OD project, which originally was demonstrated using an HD44780 with this method.

void lcd_data(uint8_t data)
{
    lcd_setrs(1);
    lcd_byte(data);
    lcd_strobe(D_43uS);
}

void lcd_byte(uint8_t byte)
{
    outp(PORTD, 0x00FF, byte);
}

void lcd_setrs(int on)
{
    if (on)
        outp(PORTD, 0x400, 0x400);
    else
        outp(PORTD, 0x400, 0x0);

    delay_ncycles(1);
}

void lcd_strobe(uint8_t delay)
{
    outp(PORTD, 0x100, 0x100);
    delay_ncycles(1);
    outp(PORTD, 0x100, 0x0);

    switch (delay)
    {
        case D_1_53mS:
            delay_ncycles(833);
            break;
        case D_39uS:
            delay_ncycles(20);
            break;
        case D_43uS:
            delay_ncycles(23);
            break;
    }
}

That’s an awful lot of fud just to write a single byte.

After

Let’s have a look at the code needed to do the same thing when the HD44780 is memory mapped – attached to the processor bus.

void lcd_data(uint8_t data)
{
    outp(LCD_DATA, data);
    while (inp(LCD_CMD) & CMD_BUSY);
}

Blimey. After attaching the LCD properly, all of that is reduced to 2 lines of code, and depending on the platform and implementation, orders of magnitude better performing. We no longer need any software delays, not even for initialisation. Bus timings are now all native, and we can poll the ‘Busy’ bit on the HD44780 for everything else.

8OD Specific Solution

The board I’m currently doing this work on is 8OD, based on the Intel 8086. Fortunately I’ve got a honking big CPLD on this board, allowing me to experiment with glue logic.

As an added bonus, the 8086 has a signal which directly replaces R/W: DT/R. DT/R is intended for driving bus transceivers but happens to provide the missing timings needed to generate a perfect R/W signal.

intelbus2

Pretty quickly, I whipped up a VHDL module which ties together all of this logic, into a perfect Intel to Motorola bus converter.inteltomoto

After 15 years of using HD44780’s, this is the first time I’ve ever seen one memory mapped.

 

8OD CPLD Top level schematic

(8OD – Main article)

One thing I never got around to when I originally published the details of this project, was any kind of disclosure of what’s in that CPLD. The thing is, that it’s pretty much all VHDL, and not very interesting to look at.

Recently I’ve re-worked it such that it now has a fairly tidy top level schematic. Sure, it still doesn’t reveal every intimate detail of how to build 8OD from primitive logic gates (that would require hundreds, if not thousands of individual gates), but it does give at least some idea of what is going on inside there.

Download it here.

8OD.1 Board: Mea Culpa

(8OD – Main article)

Recently I started looking into the next phase of development I wanted to do on 8OD, specifically, to make a bus available on the 36-pin header and start attaching peripherals to it.

As I looked back through the design, something jumped out at me…

U9

U9 is a not-entirely-necessary, but nice to have bit of logic which performs bus steering, to make the addressing of 8-bit peripherals easier. It is effectively a bus transceiver, although not quite performing the same role as an actual bus transceiver.

 

U1

Yet…

The signals made available on the 8086 for the control of bus transceivers, are N.C. Well well…

Diving back in the the CPLD’s VHDL code, I can see that I’ve derived the control signals for U9 from the 8086’s bus control signals, not the dedicated transceiver control signals. This of course is less than ideal, but is it actually a problem? Let’s get those timing diagrams out and have a look.

First stop, the 8086 read timings. Straight away, it doesn’t look good. Because the #OE’s of the transceiver are asserted for the period of the #CS signals of the two peripherals behind it, the de-assertion of #RD will reverse the direction of the transceiver while it is still driving the bus.

Therefore the above diagram shows that this inappropriate use of RD# causes a potential bus clash between peripherals and the transceiver (U9), whereas DT/#R (Not used by my design) is well clear of any such eventualities. How the heck did I miss that?

But as previously stated, this only highlights a potential clash. Because the peripheral is assumed to be driving the bus during this period, we have to examine it to determine if there is an actual clash. Let’s take a look at the SC16C554 datasheet:

timing3

That’s not good, it’s now very likely that there is a clash. In terms of the UART (U10), this is clearly represented and quantifiable by the symbol ‘t12h’.

timing4

Which is 15 nanoseconds per bus cycle.

The only remaining hope lies within the propagation times of U9 (SN74LVC16T245). I’ve got a pretty good feeling there isn’t going to be any good news there either.

timing5

And there it is. U9 can change direction significantly faster than U10 can release the bus. There’s officially a major problem with my design.

I’m a little miffed about this. I really thought the 8OD.1 board was free of mistakes, certainly, it didn’t show any problems during my testing. In some respects, what I’ve just found is the worst kind of problem, as it is likely to be missed during design verification, and will lead to premature failure, after the product has been shipped.

It’s just as well I didn’t mass produce these things.

So what next?

8od.1modWire mods.

And a couple of pull-up resistors. This time I checked the need for these carefully, as I was keen not to get my backside kicked by yet another quirky detail, specifically that control signals tend to float during certain periods, i.e. Hold Acknowledge, Reset.

timing7And there they are on the original SDK-86 schematic, circa 1978.

I’ve now got DT/#R connected to the DIR signals on U9, and #DEN to the CPLD to control the #OE signals for U9. This is fairly faithful to the original guidelines, and will make these boards reliable in the long term.

So now I’ve got to revise the board. I guess this isn’t such a bad thing, as there’s already a list of things I want to improve with the current design.

Moral of the story

8OD’s 8-bit bus spur was a design annoyance, which I didn’t put as much thought into as I should have. As is always the case when designing complex electronics: If you haven’t thought of absolutely everything, there will always be a major problem hiding in the overlooked details.

ROVATool 2.0 released

(ROVATools – Main article)

With the ROVA USB-TOOLS programmer expensive and sold by few vendors, in 2013 I added support for Parallel port programming,
but with PCs equipped with Parallel ports increasingly rare, but this hasn’t been enough.

Recently I was contacted by a knowledgeable, well equipped reader by the name of Rajko Stojadinovic – who has added a frequently requested feature to ROVATool: Even more programming hardware options.

FTDI Based boards

This is a new option, which utilises inexpensive FTDI based boards for programming Realtek devices.

Read more here

ALTERA USB Blaster II Clones

(Or pretty much anything else with a Cypress EZUSB-FX2 on it) This option isn’t entirely new, but details how it’s possible to convert one of these inexpensive dongles into a ROVA USB-TOOLS programmer.

Read more here

Other changes

There aren’t any! Other than that I’ve split out ROVATool into its own download, as most of the users of this suite use the tool for RTD2660 platforms, there is little point in bundling the ROVAEdit tool with it, which is not applicable.

Download

ROVATools v1.2 released

After several emails received from users – I’ve released a new minor version of ROVATools

There are two changes which are applicable to RTD266x platforms only.

  • Added support for Pm25LD010 flash chip
  • Fixed issue where RTD266x platforms cannot correctly program .BIN files which aren’t padded to 256 bytes

It can be downloaded here.

Mystery character problem solved

(Continuation of this post)

After about an hour of sending thousands of web requests, bingo, the mystery character appeared, and the logic analyser breakpoint I set had triggered. I was pleased to see a clean and clear write of 0xFF to 0x20481 – the exact value to the location I suspected was being trashed.

Capture of the write to const section in RAM
Capture of the write to const section in RAM

After scrolling through reams of bus transactions and correlating it to a dissassembly, which I used Watcom’s ‘wdis’ tool to create, the problem was becoming apparent.

Although there was only one problem that caused the original fault, I spotted another while I was at it:

1 – I’d forgotten to push the AX register to the stack in the ISR. A bit of a bummer for any interrupted code that was using it.

2 – The second problem had me feeling like I’d failed computer science 101. I’m using the W5100’s interrupt so the application doesn’t have to waste time polling the SPI for stuff to do. Generally speaking the interrupt handler adhered to good practise – Doing no real work, just flagging stuff for the main routine to do.

The oops? I was still reading from the SPI in the ISR, and the main routine code was too, so one thread of w1500_read() would be interrupted by another copy of w5100_read(), stuffing the whole thing up, because the SPI controller and the W5100 are a shared resource, and I wasn’t disabling interrupts for the execution of the non interrupt context version.

In the end I moved the W5100 flags ‘read’ out of the ISR, and into the main loop, instead just flagging the interrupt pin in the ISR, which is safe.

Did I really need a logic analyser for this? Not really, but heck, once I had a capture, it helped me find both problems in minutes.

Back to the Logic Analyser

Recently I started on getting the Arduino Ethernet shield working with 8OD. Aside from the pull up resistor problem which was found and fixed fairly easily, another much more ominous issue cropped up while I was making a little web demo app.

strangechar

Crap. After a few hundred web requests – it appears. Straight away I can see that the source of this text, which is in a “const” section in RAM is being modified. There’s nothing in the code that I can see which would be modifying this area. This is not good.

Either it’s memory corruption caused by code bug, or a hardware fault. I have been able to rule out fault with a specific unit, because it reproduces easily on a second board. Beyond that, I’m stumped. It’s now time to break out with the logic analyser, and put a breakpoint on that memory location, and hopefully see what’s changing it.

Before I can even do that, I’ve got to prepare an 8OD.1 unit for logic analysis, which is a bit of a job.

8OD.1 logic analyser connections
8OD.1 logic analyser connections

Many of the connections needed for logic analysis are on the EPROM socket, but there’s still quite a lot that aren’t. The rest are exposed as test pads which I then have to solder wires and headers to. This is a little bit of a pain but with the PCB only being 2 layer, adding footprints for test connectors (I.e. Mictor test probes) is out of the question.

8086 based 8OD.1 attached to 16700 logic analyser
One of the 8OD.1’s would inevitably find its self back on this spot

With 4 pods on a 16752A in use, this is about the minimum I can get away with to do a meaningful analysis on 8OD.