Problem initializing Lcd.

Hi all,
I am struggling with a issue here, I made a LCD controller that lets you control a LCD by i2c,
The page is here: http://freeduality.tk/?page_id=16
the library for the project: GitHub - Duality4Y/IOlcd: lcd controled through a IOexpander (MCP23016)

However i noticed that the Lcd doesn’t always initialize, I fixed this by putting a delay(200); before lcd.init();
what lcd.init(); does is initialize the i2c connection, however i am trying to do this with a sketch:
the lcd won’t initialize, not with the way explained here: http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html the way like in begin1(); in the sketch.
or the way i initialize it in the library (which is begin0():wink:

the sketch is bare, it doesn’t use the library.
I hope someone can help me solve the issue, things I have noticed that initialize the lcd is pressing the reset after a upload,
opening a Serial monitor.

The example from the library that works (with the delay(200); added);

//include the nesecary header files for this to work.
#include <IOlcd1_0.h>
#include <Wire.h>;

IOlcd1_0 lcd;
long count = 1;

void setup(){
  //initialize the chip(in this case MCP23016) at address 0x20
  delay(200);
  lcd.init(0x20, MCP23016);
  //start the lcd
  lcd.begin(16,2);
  //at the begining of the first line.
  lcd.home();
  //clear lcd just incase.
  lcd.clear();
  //print hello world to the first line.
  lcd.print("Hello, World!");
}

void loop(){
  //set cursor to the second line.
  lcd.setCursor(0,1);
  //number of seconds active, is printed.
  lcd.print(count);
  count++;
  delay(1000);
}

the code i am currently working on:

#include <Wire.h>;

#define address 0x20
#define Enable 64
#define RS 128

#define IODIR0 0x06
#define IODIR1 0x07

//a High is 1
#define allHigh 0xFF
//a low is 0
#define allLow 0x00
//input is 1
#define allInput 0xFF
//output is 0
#define allOutput 0x00

//Data buss port0
#define Port0 0x00
//Rs and E connected to Port1
#define Port1 0x01


void setup(){
	pinMode(13, OUTPUT);
	begin1();
	for(int i=0;i<255;i++){
		write(i);
		}
	}

void loop(){
	
	}
//this is what i have in my IOlcd library.
void begin0(){
	initChip();
	delay(100);
	//tell we are bitmode 8 and 2 lines
	writeCommand(0x38);
	//wait a bit
	delayMicroseconds(4500);
	//try second time
	writeCommand(0x38);
	delayMicroseconds(1500);
	//third and last try
	writeCommand(0x38);
	
	//set display on and enter entry mode
	writeCommand(0x0c);
	writeCommand(0x06);
	writeCommand(0x02);
	delayMicroseconds(60);
	}
	
//this is from here: http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html
void begin1(){
	initChip();
	delay(100);
	writeCommand(0x30);
	delay(5);
	writeCommand(0x30);
	delayMicroseconds(150);
	writeCommand(0x30);
	delayMicroseconds(150);
	writeCommand(0x38);
	delayMicroseconds(60);
	writeCommand(0x08);
	delayMicroseconds(60);
	writeCommand(0x01);
	delay(5);
	writeCommand(0x06);
	delayMicroseconds(60);
	//turn the display on:
	writeCommand(0x0C);
	delayMicroseconds(60);
	}
//this is a test init.
void initChip(){
	Wire.begin();
	sendData(IODIR0, allOutput);
	sendData(IODIR1, allOutput);
	}

int byteShift(int num){
	int var = 0;
	int i, x, y, p;
	int s = 8;
	
	for(i=0;i<(s/2);i++){
		p = s-i-1;
		x = num&(1<<p);
		x = x >> p;
		
		y = num & (1<<i);
		y = y>>i;
		
		var = var | (x<<i);
		var = var | (y<<p);
		}
	return var;
	}

//void writeCommand4bits(uint8_t _data){
	//writeCommand(_data>>4);
	//writeCommand(_data&0x08);
	//}

//void write4bits(uint8_t _data){
	//write(_data>>4);
	//write(_data&0x08);
	//}
	
void writeCommand(uint8_t _data){
	_data = byteShift(_data);
	sendData(Port0, _data);
	sendData(Port1, Enable);
	sendData(Port1, 0x00);
	sendData(Port1, Enable);
	}
void write(uint8_t _data){
	_data = byteShift(_data);
	sendData(Port0, _data);
	sendData(Port1, RS | Enable);
	sendData(Port1, RS);
	sendData(Port1, Enable);
	}

boolean sendData(uint8_t port, uint8_t _data){
	Wire.beginTransmission(address);
	Wire.write(port);
	Wire.write(_data);
	Wire.endTransmission();
	delayMicroseconds(60);
	}

Greetings Duality.

Thanks!

Bummer that all your data lines are hooked up backwards to the expander port bits.

One thing that looks off to me is that it looks like your enable output pin is backwards. E is normally driven low then pushed high, then dropped back to low again.

But like I said back in December in your other thread about this board: http://arduino.cc/forum/index.php/topic,84269.0.html I'd recommend that you write a thin sub device class layer and interface into fm's library that is tested and working on Arduino and ChipKit boards and across many different interfaces from i2c, direct pin control and serial shift registers.

The total amount of code needed is very minimal, probably similar to the amount of code in your example sketch in this thread. It will save much time and head ache.

You can even start with fm's LiquidCrystal_I2c.cpp and .h Then rename it something like LiquidCrystal_MCP23016.cpp and .h and then rename the classes to match and re-write the send() and init() routines for the MCP23016. It is a very small amount of code as all it has to do is move the data bits and control lines across the interface while the upper layer LCD class handles all the rest.

--- bill

you meen functions like digitalwrite(); that would write to the IO expander instead of the arduino? like you said in that post 4 bit for smaller ioexpander, is what i wanted to make, but first i have to get this working though :) the reason its backwards on the ioexpander(data lines) that it would be easier to make a pcb :P I was planning on making a function to handle the wireing, in a way that you could self say i want the wiring this way or the other way :) indeed i think a layer would help me there,

All though the primairy goal of making the library and hardware, was to learn more on lcd interfacing, in arduino its easy you download a library and hookup the hardware, without really knowing what is going on. I want to believe! :) but in this case Know.

Hmm pulse the E high then low? I'll try that, Some how i though i had to bring the enable HIGH, the LOW and then HIGH again.

Greetings, Duality

the lcd won't initialize, not with the way explained here: http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html the way like in begin1(); in the sketch.

This technique will work every time if the timing diagrams are implemented properly.

or the way i initialize it in the library (which is begin0()

This technique will probably work most of the time if you add the missing delay after the third 0x38. It would also have to be implemented properly.

Don

floresta, what do you meen by implemented properly :)?

floresta, what do you meen by implemented properly

Bill already mentioned how you did not deal with the Enable line properly. You also have to deal with the RS line and you have to have the proper set up and hold times implemented as shown in the 'Write Operation' timing diagram.

Don

Thanks for explaining that!

Because English is not my native language, I don't always fully understand what you guys say :).

Greetings, Duality

Awesome this works:

when I upload, take the power off (unplug the usb).
and replug the usb, the lcd initializes !
just like that :slight_smile: I’ll have it wait a few minutes without power to be sure,
that this is not something that just happens :slight_smile:

#include <Wire.h>;

#define address 0x20
#define Enable 64
#define RS 128

#define IODIR0 0x06
#define IODIR1 0x07

//a High is 1
#define allHigh 0xFF
//a low is 0
#define allLow 0x00
//input is 1
#define allInput 0xFF
//output is 0
#define allOutput 0x00

//Data buss port0
#define Port0 0x00
//Rs and E connected to Port1
#define Port1 0x01


void setup(){
	pinMode(13, OUTPUT);
	begin0();
	for(int i=0;i<255;i++){
		write(i);
		}
	}

void loop(){
	
	}
//this is what i have in my IOlcd library.
void begin0(){
	initChip();
	delay(100);
	//tell we are bitmode 8 and 2 lines
	writeCommand(0x38);
	//wait a bit
	delayMicroseconds(4500);
	//try second time
	writeCommand(0x38);
	delayMicroseconds(1500);
	//third and last try
	writeCommand(0x38);
	
	//set display on and enter entry mode
	writeCommand(0x0c);
	writeCommand(0x06);
	writeCommand(0x02);
	delayMicroseconds(60);
	}
	
//this is from here: http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html
void begin1(){
	initChip();
	delay(100);
	writeCommand(0x30);
	delay(5);
	writeCommand(0x30);
	delayMicroseconds(150);
	writeCommand(0x30);
	delayMicroseconds(150);
	writeCommand(0x38);
	delayMicroseconds(60);
	writeCommand(0x08);
	delayMicroseconds(60);
	writeCommand(0x01);
	delay(5);
	writeCommand(0x06);
	delayMicroseconds(60);
	//turn the display on:
	writeCommand(0x0C);
	delayMicroseconds(60);
	}
//this is a test init.
void initChip(){
	delay(200);
	Wire.begin();
	sendData(IODIR0, allOutput);
	sendData(IODIR1, allOutput);
	}

int byteShift(int num){
	int var = 0;
	int i, x, y, p;
	int s = 8;
	
	for(i=0;i<(s/2);i++){
		p = s-i-1;
		x = num&(1<<p);
		x = x >> p;
		
		y = num & (1<<i);
		y = y>>i;
		
		var = var | (x<<i);
		var = var | (y<<p);
		}
	return var;
	}

//void writeCommand4bits(uint8_t _data){
	//writeCommand(_data>>4);
	//writeCommand(_data&0x08);
	//}

//void write4bits(uint8_t _data){
	//write(_data>>4);
	//write(_data&0x08);
	//}
	
void writeCommand(uint8_t _data){
	_data = byteShift(_data);
	sendData(Port0, _data);
	sendData(Port1, Enable); //write enable high
	sendData(Port1, 0x00); //write enable low;
	delayMicroseconds(60); //write time wait.
	}
void write(uint8_t _data){
	_data = byteShift(_data);
	sendData(Port0, _data);
	sendData(Port1, RS | Enable);
	sendData(Port1, 0x00);
	delayMicroseconds(60); //write time wait.
	}

void sendData(uint8_t port, uint8_t _data){
	Wire.beginTransmission(address);
	Wire.write(port);
	Wire.write(_data);
	Wire.endTransmission();
	}

if i look at the datasheet: www.adafruit.com/datasheets/HD44780.pdf
and then at table 6; it looks like the instruction time it takes is 37uS ?
I put 60us delay in writeCommand and write, to have the instruction wait.

if I remove the delay(200); that’s before Wire.begin(); the lcd won’t initialize anymore.

I'd still recommend that you use fm's lcd library and write a sub device class. It gives you known working code for lcds of different sizes as well as support for backlight control and is supported by the LCDbitmap library ( http://arduino.cc/forum/index.php/topic,103956.0.html ) that allows you to do some graphics on these text based lcds.

--- bill

More on hd44780 below.


The hd44780 interface uses 8 data lines, 2 control lines (R/W, RS) and an enable/strobe. The interface mandates that things be done in a certain order/way not only from a command/data message point of view but from an electrical signal point of view on the interface bus. At the lowest h/w bus level you must honor the timing of the data and control lines with respect to when the enable line is raised and lowered. The timing diagrams on page 58 of the Hitachi spec show what is necessary. www.adafruit.com/datasheets/HD44780.pdf The timing values are on page 52.

In order to have a very robust and reliable interface that works in all conditions (temperature, voltage, etc..) you must honor those timings. Where this gets problematic in an arduino environment is that the actual wiring of the components is not 100% of the electrical interface. Software running on the AVR is performing part of the h/w function because software is driving the data and control lines.

I have seen many (if not most) arduino lcd libraries out there that"work" that are violating these timing requirements. (including what you are currently doing) Most often it is tAS that is being violated. This is because to do it correctly can require an extra update of the control lines before raising E and on slow interfaces like i2c it can noticeably affect the performance.

If you want to write you own level code, you will need to become comfortable with reading and understanding the datasheet. While not difficult, it does take a bit of getting used to all the terminology and language used.

Awesome this works:...

It looks like you are using the wrong initialization routine. If you want your code to work reliably you should follow the datasheet.

Because English is not my native language, I don't always fully understand what you guys say

I wonder why the user profile has an entry for 'location' ... ?

if i look at the datasheet: www.adafruit.com/datasheets/HD44780.pdf and then at table 6; it looks like the instruction time it takes is 37uS ?

But you also have to look at the conditions under which that 37uS is valid. If you follow the [u]LCD Initialization[/u] link at http://web.alfredstate.edu/weimandn and scroll down to the very last paragraph (About the Delays) you will find a full explanation. Here is the abbreviated version:

The datasheet specifies that most instructions execute in 37 uS with a clock frequency of 270 KHz.   Elsewhere in that same datasheet the clock specifications indicate that it may run as low as 190 KHz, which would increase the instruction execution time by more than 40%.   It is this 'worst case' upon which I have based my recommended time delays.

Don

so 270Khz is clock speed at which you can feed the instructions?
does this meen I have to take the I2C clock speed and calculate instruction delay from there?
or is that the frequency you pulse the Enable/RS ?

I think I have to read this a few more times, the data sheet as well as what you said floresta,
Then sleep a few night’s Thinking on this matter,
And then I might get it :slight_smile:
I’ll sure to be studying the data sheet more though,

This code, initializes with begin1(); which is the way of the website link :slight_smile:

#include <Wire.h>;

#define address 0x20
#define Enable 64
#define RS 128

#define IODIR0 0x06
#define IODIR1 0x07

//a High is 1
#define allHigh 0xFF
//a low is 0
#define allLow 0x00
//input is 1
#define allInput 0xFF
//output is 0
#define allOutput 0x00

//Data buss port0
#define Port0 0x00
//Rs and E connected to Port1
#define Port1 0x01


void setup(){
	pinMode(13, OUTPUT);
	begin1();
	for(int i=0;i<255;i++){
		write(i);
		}
	}

void loop(){
	
	}
//this is what i have in my IOlcd library.
void begin0(){
	initChip();
	delay(100);
	//tell we are bitmode 8 and 2 lines
	writeCommand(0x38);
	//wait a bit
	delayMicroseconds(4500);
	//try second time
	writeCommand(0x38);
	delayMicroseconds(1500);
	//third and last try
	writeCommand(0x38);
	
	//set display on and enter entry mode
	writeCommand(0x0c);
	writeCommand(0x06);
	writeCommand(0x02);
	delayMicroseconds(60);
	}
	
//this is from here: http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html
void begin1(){
	initChip();
	delay(100);
	writeCommand(0x30);
	delay(5);
	writeCommand(0x30);
	delayMicroseconds(150);
	writeCommand(0x30);
	delayMicroseconds(150);
	writeCommand(0x38);
	delayMicroseconds(60);
	writeCommand(0x08);
	delayMicroseconds(60);
	writeCommand(0x01);
	delay(5);
	writeCommand(0x06);
	delayMicroseconds(60);
	//turn the display on:
	writeCommand(0x0C);
	delayMicroseconds(60);
	}
//this is a test init.
void initChip(){
	//delay(200);
	Wire.begin();
	sendData(IODIR0, allOutput);
	sendData(IODIR1, allOutput);
	}

int byteShift(int num){
	int var = 0;
	int i, x, y, p;
	int s = 8;
	
	for(i=0;i<(s/2);i++){
		p = s-i-1;
		x = num&(1<<p);
		x = x >> p;
		
		y = num & (1<<i);
		y = y>>i;
		
		var = var | (x<<i);
		var = var | (y<<p);
		}
	return var;
	}

//void writeCommand4bits(uint8_t _data){
	//writeCommand(_data>>4);
	//writeCommand(_data&0x08);
	//}

//void write4bits(uint8_t _data){
	//write(_data>>4);
	//write(_data&0x08);
	//}
	
void writeCommand(uint8_t _data){
	_data = byteShift(_data);
	sendData(Port0, _data);
	sendData(Port1, Enable); //write enable high
	sendData(Port1, 0x00); //write enable low;
	delayMicroseconds(60); //write time wait.
	}
void write(uint8_t _data){
	_data = byteShift(_data);
	sendData(Port0, _data);
	sendData(Port1, RS | Enable);
	sendData(Port1, 0x00);
	delayMicroseconds(60); //write time wait.
	}

void sendData(uint8_t port, uint8_t _data){
	Wire.beginTransmission(address);
	Wire.write((uint8_t)port);
	Wire.write((uint8_t)_data);
	Wire.endTransmission();
	}

so 270Khz is clock speed at which you can feed the instructions? does this meen I have to take the I2C clock speed and calculate instruction delay from there? or is that the frequency you pulse the Enable/RS ?

No to all of that. The 270 KHz refers to the clock speed of the processor used in the LCD controller. The clock is designed to run at 270 KHz and the timing information given in table 6 assumes that it is running at that speed (until you read the fine print).

The clock on any particular LCD module may or may not be running at that speed so you really should design your software to work properly under the worst possible conditions. When the LCD controller processor runs slower than normal it takes that controller longer than normal to complete each instruction. If you do not properly deal with this additional time requirement you may send out new information to the LCD controller before it is done dealing with the previous information.

Don

floresta: If you do not properly deal with this additional time requirement you may send out new information to the LCD controller before it is done dealing with the previous information. Don

duality, The lcd has the ability to tell you when it is busy, but that requires using the BUSY status, which requires using the r/w line in coordination with RS to read it. Almost none of the libraries out there use the BUSY status because it takes extra software and often additional hardware to implement it. As a result they are having to guess when the LCD is done doing something by doing a blind wait of some amount of time. (That is what Don is talking about above) And there is the problem. Since there is no way to really know when the LCD is done doing something and is ready to do something else without using BUSY, you have to wait long enough to ensure that it works on the slowest LCD because the software as no way of know the speed of the LCD being used. (again, what Don is talking about)

I still recommend that you write a sub device class for fm's lcd library while it also does not use BUSY, it has been tested on many different lcds with different sizes and on many different micro-controllers running at different clock rates.

--- bill