Currently I'm working on a project where I want to display some data on an LCD. I'm using a MCP2221A to communicate with a PCF8574, which then sends the bits to the HD44780 LCD.


I'm using the LCD in 4-bit mode so I have to send the data-bytes in nibbles, first the higher and then the lower. The bytes I'm sending have the following composition: [RS,RW,E,K,D4,D5,D6,D7].

I've been following a manual to initialize and write to the LCD. For the I2C communication I'm running a python-script using CircuitPython. Here are the two functions I'm using:

def init(lcd_address):
    commands = {
      "set": "00110011",
      "4bit": "00110010",
      "clear1": "00110000",
      "clear2": "00110001",
      "home1": "00110000",
      "home2": "00110011",
      "ON1": "00110000",
      "ON2": "00111111",

    for i in range(3):
      i2c.writeto(lcd_address, bytes([int(commands["set"], 2)]),stop=False)  # Function set

    i2c.writeto(lcd_address, bytes([int(commands["4bit"], 2)]), stop=False)  # Set 4-bit mode
    i2c.writeto(lcd_address, bytes([int(commands["clear1"], 2)]), stop=False)  # Clear the screen. Higher nibble
    i2c.writeto(lcd_address, bytes([int(commands["clear2"], 2)]), stop=False)  # Clear the screen. Lower nibble
    i2c.writeto(lcd_address, bytes([int(commands["home1"], 2)]), stop=False)  # Set cursor to (0,0). Higher nibble
    i2c.writeto(lcd_address, bytes([int(commands["home2"], 2)]), stop=False)  # Set cursor to (0,0). Lower nibble
    i2c.writeto(lcd_address, bytes([int(commands["ON1"], 2)]), stop=False)  # Turn on display. Higher nibble
    i2c.writeto(lcd_address, bytes([int(commands["ON2"], 2)]), stop=True)  # Turn on display. Lower nibble

def write(lcd_address):  # Writing the letter 'H' to the lcd
    char1 = "10110100"
    char2 = "10110100"

    i2c.writeto(lcd_address, bytes([int(char1, 2)]), stop=False)
    i2c.writeto(lcd_address, bytes([int(char2, 2)]), stop=True)


I can't get the character to show up on the LCD. The bytes in the code are the same that get sent to the LCD (checked with scope), so that is not the problem. I'm guessing it has something to do with the enable pin and how that should be triggered, but I'm not sure.


This is how my code looks now:

def init(lcd_address):
commands = {
  "setD": "00010011",
  "setE": "00110011",
  "setF": "00010011",
  "4bitD": "00010010",
  "4bitE": "00110010",
  "4bitF": "00010010",
  "clear1D": "00010000",
  "clear1E": "00110000",
  "clear1F": "00010000",
  "clear2D": "00010001",
  "clear2E": "00110001",
  "clear2F": "00010001",
  "home1D": "00010000",
  "home1E": "00110000",
  "home1F": "00010000",
  "home2D": "00010011",
  "home2E": "00110011",
  "home2F": "00010011",
  "ON1D": "00010000",
  "ON1E": "00110000",
  "ON1F": "00010000",
  "ON2D": "00011111",
  "ON2E": "00111111",
  "ON2F": "00011111",

for i in range(3):
  i2c.writeto(lcd_address, bytes([int(commands["setD"], 2)]), stop=False)  # Function set
  i2c.writeto(lcd_address, bytes([int(commands["setE"], 2)]), stop=False)
  i2c.writeto(lcd_address, bytes([int(commands["setF"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["4bitD"], 2)]), stop=False)  # Set 4-bit mode
i2c.writeto(lcd_address, bytes([int(commands["4bitE"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["4bitF"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["clear1D"], 2)]), stop=False)  # Clear the screen
i2c.writeto(lcd_address, bytes([int(commands["clear1E"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["clear1F"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["clear2D"], 2)]), stop=False)  # Clear the screen
i2c.writeto(lcd_address, bytes([int(commands["clear2E"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["clear2F"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["home1D"], 2)]), stop=False)  # Set cursor to (0,0)
i2c.writeto(lcd_address, bytes([int(commands["home1E"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["home1F"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["home2D"], 2)]), stop=False)  # Set cursor to (0,0)
i2c.writeto(lcd_address, bytes([int(commands["home2E"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["home2F"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["ON1D"], 2)]), stop=False)  # Display on
i2c.writeto(lcd_address, bytes([int(commands["ON1E"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["ON1F"], 2)]), stop=False)

i2c.writeto(lcd_address, bytes([int(commands["ON2D"], 2)]), stop=False)  # Display on
i2c.writeto(lcd_address, bytes([int(commands["ON2E"], 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(commands["ON2F"], 2)]), stop=True)

def write(lcd_address):  # Writing the letter 'H' to the lcd
char1D = "10010100"
char1E = "10110100"
char1F = "10010100"
char2D = "10010100"
char2E = "10110100"
char2F = "10010100"

i2c.writeto(lcd_address, bytes([int(char1D, 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(char1E, 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(char1F, 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(char2D, 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(char2E, 2)]), stop=False)
i2c.writeto(lcd_address, bytes([int(char2F, 2)]), stop=True)

So now I'm sending 3 bytes per command but the display is still empty. It only flashes once when the code runs.

  • \$\begingroup\$ I get you can send bytes to the PCF8547, but you don't seem to pulse the E line, and you describe how 7 bits are used, but are they counted from MSB, or LSB, and what is the one bit that you did not describe? You should post schematics as you might LSB and MSB bit endianess swapped as well. But largest problem is not generating the protocol at the LCD pins that the LCD requires. \$\endgroup\$
    – Justme
    Commented Aug 4, 2021 at 17:56
  • \$\begingroup\$ Oh, my bad. I left out the K-bit which feeds the base of the transistor that pulls the LCDs K-pin low. But how do i pulse the E-pin without changing the state of the other pins. So shortly, how do i control the E-pin individually? \$\endgroup\$
    – Mark
    Commented Aug 4, 2021 at 18:01
  • \$\begingroup\$ Sending it successive bytes where only E bit is different? Same way than all other libraries that use HD44780 LCDs over PCF8547 IO expander (which is by the way terrible for the purpose, due to it's quasi-bidirectional IO - I recommend not reading from LCD, unless there are series resistors installed on data bus). \$\endgroup\$
    – Justme
    Commented Aug 4, 2021 at 18:08
  • \$\begingroup\$ So just write the same byte twice. Once with E low and once with E high? \$\endgroup\$
    – Mark
    Commented Aug 4, 2021 at 18:11
  • \$\begingroup\$ Not twice, three times. You need to set E low too, before you can change next RS, RW, and data. \$\endgroup\$
    – Justme
    Commented Aug 4, 2021 at 18:14

1 Answer 1


You need to control the E pin correctly.

As per the HD44780 datasheet, the RS, RW, and DATA pins must be stable for some time before E is set high, and they must stay stable also some time after E is set low.

Assuming you want to send something to the display, you basically have to send 3 bytes:

  • data pattern that sets RS, RW, DATA and backlight states, but with E low
  • data pattern that sets RS, RW, DATA and backlight states, but with E high
  • data pattern that sets RS, RW, DATA and backlight states, but with E low

All those three bytes are identical in respect to other bits, except the E bit.

Some implementations skip the third byte, assuming that RS, RW and DATA don't need to be stable before setting E high or after setting E low, but I don't recommend that. Even though it appears to work, it violates HD44780 bus cycle. Some HD44780 compatible clones may not be compatible.

You also don't have the required delays between commands. The sequence to go into 4-bit mode is especially important to get right, so it can reliably go from any weird state to 8-bit mode and back to 4-bit mode correctly. Also the clear and home commands take nearly 2 milliseconds to execute, so the display will not accept any commands while it is already executing a command.

Finally, if the bit order really is [RS, RW, E, LED, D4, D5, D6, D7], the data bits are in wrong order in your code, as your code assumes order of [RS, RW, E, LED, D7, D6, D5, D4]. You need to be sure which of those is sent first and last, and in which order they are. You can test that with simply trying to toggle the LED, either 0x10 lights it up, or 0x08. Standard modules are in the following order, MSB to LSB : [D7, D6, D5, D4, LED, E, RW, RS], so if you have one, you need to swap all bits left and right.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.