Electronic – HD44780 initialization for 4-bit mode

avri2clcd

I have successfully implemented the I2C protocol necessary to communicate with the device, but I am absolutely stumped on the initialization sequence to get it to work. I have tried countless different sequences that I have found on various websites, different datasheets, and even sequences I just contrived myselfed.

Here is the specific device that I am using:
I2C Backpack & LCD

On that same page is a link to the schematic of the I2C device where you can see that 4 data lines (therefore 4-bit), RS, RW, BackLight, and E-CLK are driven from the device output. I soldered the driver to the LCD screen and measured the voltage on each pin as I pass in data after addressing the device with address 0x27 to ensure the correct data is getting to the screen with the correct format. Since the Clk pin is driven as an output of the driver, it's necessary to shift in the data with the clock as HIGH, and then again as LOW. I simply cannot get this to work and am quite honestly at a point where I do not have any more ideas to try.

Assuming the I2C protocol works, which it does because I get ACKs from the driver after addressing and each byte sent, here is the code I have written to drive the screen.

 #define F_CPU 1000000
 #include <util/delay.h>
 #include "i2c.h"

#define I2C_DEL_T       100     //4uS
#define I2C_DEL()       (_delay_us(I2C_DEL_T))
#define I2C_READ        1
#define I2C_WRITE       0 
#define RW_BIT          0

 #define LCD_RS         0
 #define LCD_RW         1
 #define LCD_CLK        2
 #define LCD_BL         3

 #define COMMAND        0
 #define DATA           1
 #define LCD_READ       1
 #define LCD_WRITE      0
 #define BL_OFF         0
 #define BL_ON          1

 void lcdInit(void);
 void writeByteWithClock(uint8_t RS, uint8_t RW, uint8_t BL, uint8_t data);
 void writeNibbleWithClock(uint8_t RS, uint8_t RW, uint8_t BL, uint8_t data);


 //----------------------------------------------------------------------------
 void writeByteWithClock(uint8_t RS, uint8_t RW, uint8_t BL, uint8_t data){
uint8_t temp = data;    //init temp = data and overwrite lower nibble 

//upper nibble
temp &= 0xF0;           
temp |= ( (RS<<LCD_RS) | (RW<<LCD_RW) | (BL<<LCD_BL) | (1<<LCD_CLK) ); //set as sent RS, RW, BL, and CLK high  
i2c_write(temp);
I2C_DEL(); 
i2c_write( temp & ~(1<<LCD_CLK) );          //drive CLK bit low and send again
I2C_DEL(); 

//lower nibble 
temp &= 0x0F;                               //clear upper nibble and retain lower nibble (RS, RW, etc)
temp |= ( (data<<4) | (1<<LCD_CLK) ) ;      //set upper nibble same as data && drive CLK high
i2c_write(temp);
I2C_DEL();
i2c_write( temp & ~(1<<LCD_CLK) );          //drive CLK bit low and send again
I2C_DEL();
 }

 //----------------------------------------------------------------------------
 void writeNibbleWithClock(uint8_t RS, uint8_t RW, uint8_t BL, uint8_t data){
uint8_t temp = data;    //init temp = data and overwrite lower nibble
temp &= 0xF0;           //clear low nibble
temp |= ( (RS<<LCD_RS) | (RW<<LCD_RW) | (BL<<LCD_BL) | (1<<LCD_CLK) ); //set as sent - RS, RW, BL, and CLK high

i2c_write(temp);
I2C_DEL();
i2c_write( temp & ~(1<<LCD_CLK) );  //drive CLK bit low and send again
I2C_DEL();
 }

 //----------------------------------------------------------------------------
 void lcdInit(void){
_delay_ms(100); 

writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x30); _delay_us(4500);
writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x30); _delay_us(150);
writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x30); _delay_us(100);
writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x20); _delay_us(100);          //4-bit mode 

writeByteWithClock(COMMAND, LCD_WRITE, BL_ON, 0x28); _delay_us(100);            //N=1 (2 line), F=0 (5x8)
writeByteWithClock(COMMAND, LCD_WRITE, BL_ON, 0x08); _delay_us(100);            //Display off, Cursor Off, Blink off
writeByteWithClock(COMMAND, LCD_WRITE, BL_ON, 0x01); _delay_us(5000);           //clear
writeByteWithClock(COMMAND, LCD_WRITE, BL_ON, 0x06); _delay_us(100);            //ID=1(increment), S=0 (no shift)

writeByteWithClock(COMMAND, LCD_WRITE, BL_ON, 0x0C); _delay_us(100);            //Display on, Cursor Off, Blink off
 }

Sites I've sourced:

And countless datasheets.

Any ideas?

Best Answer

The lcd that you linked is based on a HD44780 display controller. Here is the initialization sequence. The I2C backpack is actually 4-Bit interfaced.
What you're doing wrong is:
writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x30); _delay_us(4500); writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x30); _delay_us(150); writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x30); _delay_us(100); writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x20); _delay_us(100);

What you must be doing is
writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x3); _delay_us(4500); writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x3); _delay_us(150); writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x3); _delay_us(100); writeNibbleWithClock(COMMAND, LCD_WRITE, BL_ON, 0x2); _delay_us(100);

Hope it helps !