Can’t display characters on 16×2 LCD

i2clcdmicrochippythontexas instruments

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.

How:

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
      sleep(0.01)

    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)
    sleep(0.02)
    i2c.writeto(lcd_address, bytes([int(char2, 2)]), stop=True)

Problem:

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.

EDIT

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):
  sleep(0.01)
  i2c.writeto(lcd_address, bytes([int(commands["setD"], 2)]), stop=False)  # Function set
  sleep(0.01)
  i2c.writeto(lcd_address, bytes([int(commands["setE"], 2)]), stop=False)
  sleep(0.01)
  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)
sleep(0.02)
i2c.writeto(lcd_address, bytes([int(char1E, 2)]), stop=False)
sleep(0.02)
i2c.writeto(lcd_address, bytes([int(char1F, 2)]), stop=False)
sleep(0.02)
i2c.writeto(lcd_address, bytes([int(char2D, 2)]), stop=False)
sleep(0.02)
i2c.writeto(lcd_address, bytes([int(char2E, 2)]), stop=False)
sleep(0.02)
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.

Best Answer

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.