Hitachi HD44780 text display under Linux


/content/2021/display.jpg

For a project I'm using a text display containing the Hitachi HD44780 chip. These displays come in different sizes, common are 2 lines with 16 characters or 4 lines with 20 characters. The latter is also sold under the name 2004a. These displays use 5V. So these days with most CPUs and microcontrollers running with 3.3V or lower, the display is often connected to an I²C bus via a GPIO extender based on the PCF8574. The GPIO extenders are usually soldered to the display connector. You can buy the display and the GPIO extender separately or packaged together. You will always have to solder the GPIO extender to the display.

Now the correct way to connect the display to a 3.3V I²C-bus would be with a level-converter. But there is a hardware-hack to remove the pullup resistors on the PCF8574 breakout board (they're connected to +5V) which makes the device compatible with 3.3V installations: The minimum high logic level tolerated by the PCF8574 is given as 0.7 * VCC (which would mean 3.5V) but works in practice when driven with 3.3V. Note that the numbers of the resistors (R5/R6 in the hardware-hack link) may vary in different PCF8574 boards, I've seen R8, R9 for these resistors as well as no number at all. The resistors are 4.7 kΩ, usually labelled 472 in the SMD variant.

I investigated if there is a Linux driver for these displays and discovered one in drivers/auxdisplay/hd44780.c. On first glance the driver does not support I²C via the PCF8574 I/O expander. So I wrote a driver and submitted it to the kernel.

In the discussion (thanks, Geert) it turned out that there is a driver for the PCF8574 in the kernel (I had discovered so much) and that the HD44780 driver in the kernel can can be configured via appropriate device tree magic to use the I/O expander. The following device tree incantations define an overlay that configures the PCF8574 on its default I²C address 0x27 and then uses the I/O expander for configuring the I/Os for the hd44780 driver:

// Note that on most boards another fragment must enable the I2C-1 Bus

/dts-v1/;
/plugin/;

/ {
        fragment@0 {
                target = <&i2c1>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <0>;

                        pcf8574: pcf8574@27 {
                                compatible = "nxp,pcf8574";
                                reg = <0x27>;
                                gpio-controller;
                                #gpio-cells = <2>;
                        };

                };
        };

        fragment@1 {
                target-path = "/";
                __overlay__ {
                        hd44780 {
                                compatible = "hit,hd44780";
                                display-height-chars = <2>;
                                display-width-chars  = <16>;
                                data-gpios = <&pcf8574 4 0>,
                                             <&pcf8574 5 0>,
                                             <&pcf8574 6 0>,
                                             <&pcf8574 7 0>;
                                enable-gpios = <&pcf8574 2 0>;
                                rs-gpios = <&pcf8574 0 0>;
                                rw-gpios = <&pcf8574 1 0>;
                                backlight-gpios = <&pcf8574 3 0>;
                        };
                };
        };
};

Since this is non-obvious not just to me (in my research I've discovered at least two out-of-tree Linux driver implementations of drivers for the HD44780 with the PCF8574 I/O expander) I've submitted a documentation patch to make this better documented for others searching a Linux driver.

Note that the driver for this display uses escape sequences to access the various special functions of the display (e.g. turning the backlight on and off, clearing the screen or defining user-defined character bitmaps). I think those are documented only in the source-code in drivers/auxdisplay/charlcd.c.