Any HD44780 (LCD + I2C) experts around?

Hi there,

I’m busy porting the terrible Arduino LCD library over to NRF52. https://github.com/johnrickman/LiquidCrystal_I2C

It’s terrible as it uses CPU delays which cripple performance elsewhere. The version I’m doing uses peripherals only. Unfortunately I’m having a spot of bother getting anything but corruption onto the screen. The hardware and I2C side are fine but what I’m sending isn’t. Most likely I’ve borked either the timings during init or the commands. One area is the library above is actually writing 6 bytes as part of the ‘4 bit mode’ whereas other libraries are writing 4 bytes.

Not shown here is the code which ensures there’s 50us between commands/data in 4-bit mode but this is only uses right at the end and it’s broken before then.

Are there any HD44780 experts here that can spot a problem?

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_FUNCTIONSET 0x20
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

// flags for backlight control
#define LCD_BACKLIGHT 0x08

#define LCD_CMD_EN 0b100  // Enable bit
#define LCD_CMD_RW 0b10  // Read/Write bit
#define LCD_CMD_RS 0b1  // Register select bit
static void lcdTransactionStart (void) {

  dmaBuffer.locked = 1;
  dmaBuffer.writeBuffer = getNextBuffer(dmaBuffer.writeBuffer);
}

static void lcdTransactionEnd (void) {
  
  dmaBuffer.locked = 0;
  if (!dmaBuffer.writing) {
    __startTransfer();
  }
}

static void lcdTransactionWrite (uint8_t* data, uint8_t size) {

  memcpy(&dmaBuffer.writeBuffer->data, data, size);
  dmaBuffer.writeBuffer->size += size;
}

static void send (uint8_t data, uint8_t mode) {

  uint8_t packet[4];
  uint8_t focus = data & 0xF0;
  packet[0] = focus | mode | LCD_CMD_EN;
  packet[1] = focus | mode;
  focus = (data << 4) & 0xF0;
  packet[2] = focus | mode | LCD_CMD_EN;
  packet[3] = focus | mode;
  lcdTransactionWrite(&packet[0], 4);
}

static void lcdTransactionAppendCommand (uint8_t data) {

  send(data, 0);
}

static void lcdTransactionAppendChar (uint8_t data) {

  send(data, LCD_CMD_RS);
}

void lcdTransactionAppendString (char *str) {

  while (*str) {
    lcdTransactionAppendChar(*str++);
  }
}

// use on init or one off commands
static void lcdCmd4 (uint8_t cmd) {

  lcdTransactionStart();
  lcdTransactionAppendCommand(cmd);
  lcdTransactionEnd();
  while (dmaBuffer.writing) {}
  nrf_delay_ms(1);
}

void lcdInit (void) {

  dmaBuffer.transferBuffer = &dmaBuffer.buffers[0];
  dmaBuffer.writeBuffer = &dmaBuffer.buffers[BUFFER_ARRAY-1];

  // 8 bit mode
  nrf_delay_ms(100);  // wait for >40ms
  lcdCmd4(LCD_FUNCTIONSET|LCD_8BITMODE);
  nrf_delay_ms(4);  // wait for >4.1ms
  lcdCmd4(LCD_FUNCTIONSET|LCD_8BITMODE);
  nrf_delay_ms(4);  // wait for >4.1ms
  lcdCmd4(LCD_FUNCTIONSET|LCD_8BITMODE);
  nrf_delay_ms(1);
  // 4 bit mode
  lcdCmd4(LCD_FUNCTIONSET|LCD_4BITMODE);

  // display initialisation
  lcdCmd4(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE);

  lcdCmd4(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
  lcdCmd4(LCD_CLEARDISPLAY);
  nrf_delay_ms(2000);
  lcdCmd4(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
  lcdCmd4(LCD_RETURNHOME);
  nrf_delay_ms(2000);
}

I have, indeed I've been cross referencing with it to try and spot where I've had a problem. I've also emailed him an hour ago as he seems to be a living manual for HD44780!

lcdCmd4(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);

You forgot this part

LCD_DISPLAYCONTROL 0x08

lcdCmd4(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);

Yee ha!! - Spotted!

I now have the letter ‘o’ from ‘hello’ on the screen.

Shouldn’t the character be moving along automatically via the ‘LCD_ENTRYSHIFTDECREMENT’’ option?

I also can’t seem to retain the backlight although I’m including the option.

static void send (uint8_t data, uint8_t mode) {

  uint8_t packet[4];
  uint8_t focus = data & 0xF0;
  packet[0] = focus | mode | LCD_BACKLIGHT | LCD_CMD_EN;
  packet[1] = focus | mode | LCD_BACKLIGHT;
  focus = (data << 4) & 0xF0;
  packet[2] = focus | mode | LCD_BACKLIGHT | LCD_CMD_EN;
  packet[3] = focus | mode | LCD_BACKLIGHT;
  lcdTransactionWrite(&packet[0], 4);
}

I think the backlight pin is attached to the MSBit of the data byte you are clocking in. You can just send 0x80 and the backlight should turn ON regardless whether the LCD is initialized or not.

As far as getting only "O" from "Hello", not sure since you are using DMA (from what I can tell) and my driver is native I2C so can't compare the two. Maybe each byte is received too fast that the I2C backpack can't keep up?

Had an error in my dma buffer, now working beautifully.

Thanks again for the spot earlier, I think you saved me a huge amount of time.

Depend on the schematic. This is what I have in my backpack

Note the MSBit of the I2C expander connected to the backlight pin (indirectly)

I released it GitHub - andrewcharnley/nordic-nrf52-lcd2004-hd44780-i2c-async

Unfortunately I have sporadic corruption, probably on the i2c data which eventually causes a HardFault. As far as I can tell the only difference is I'm not leaving the short gap between the 3 bytes of the 6 byte (4-bit) datacommand send, however the same occurs if I use the same 50us after the 3 bytes (as well as the 6 bytes).

I'm also using a level translator, with 1k resistors on the 3v side and the same 4.7k on the 5v side.