Yet another LiquidCrystal_I2C library

Good news everyone.

I'm pleased to present, one of the best libraries (in my humble opinion) for for HD44780 LCD display and its clones operated in 4 bit mode over I2C bus with 8-bit I/O .

Works with ATtinny, ESP8266 cores and fully customizable (can work with any custom shields based on PCF8574 expander).

I hope one day this library will a part of Arduino IDE.

You can download it from my Github.

I'm curious . . . in the initialization routine why do you call the first four function set instructions 'attempts'?

It is encouraging to see that you actually followed the data sheet flowchart and turned the display off after the function set and then remembered to turn it back on again after the initialization is complete. Not everyone thinks this two step procedure is necessary but it only takes one extra instruction to do things as recommended.

On the other hand, you have provided 'Yet another LiquidCrystal_I2C library' with exactly the same name as so many others - with the attendant likelihood of compounding the confusion that already exists.

Don

@ enjoyneering

As Don says the IDE library manager already calls up a library of that name so it is unlikely it will get added as is. There are also about 5 other library variants with the same name. Forum posts here show that causes a lot of grief to newbies as the other libraries must be weeded out to avoid conflicts and it is not clear what library a Newbie is using when they post a sketch.

I have a reluctance to adopt any libraries with identical names to one I already have as this can "break" some of my existing working sketches.

I have taken the liberty of cloning and batch renaming the library to "PCF8574_I2C_LCD" so I can test it alongside other libraries LCD I2C libraries. If anyone else want to take the same approach then the copy is temporarily here.

Note however that this copy will not be kept up-to-date and therefore should be used with caution. I will be deleted at some point.

To use other I2C addresses (other than those built into the library) I had to use type casting (which is inconvenient) e.g.:

#include <PCF8574_I2C_LCD.h>

PCF8574_I2C_LCD lcd( (PCF8574_address) 0x3F );

Once that was done one of my existing I2C clock+temperature sketches worked fine though the sketch FLASH size did increase by ~500 bytes for some reason compared to the LiquidCrystal_I2C library the IDE library manager calls up. (Not a big problem though).

The library looks very neatly written. Good job!

It is faster than the one in the library manager.
Using IDE 1.6.10
This library transfers a character to the display in 1096us vs the one in the library manager transfers a character in 1458us.

One big issue will be backward compatibility with the existing LiquidCrystal_I2C. This library uses the same name but does not use the same API as the other LiquidCrystal_I2C library.
In particular, init() and setbacklight() are missing and the constructors are different.
This will add to the already existing confusion over the "LiquidCrystal_I2C" library.

floresta:
I'm curious . . . in the initialization routine why do you call the first four function set instructions 'attempts'?

Technically, the first 3 commands are "attempts".
As they are all attempting to put the display back into 8 bit mode.
Depending on the state of the LCD when the sequence begins, it might take 1, 2, or 3 "goto to 8 bit mode" commands or "attempts" to reliably get the display back into 8 bit mode. If the first succeeds the other 2 wouldn't be necessary. If the second command succeeds, then the 3rd wouldn't be necessary. But if the lcd is in 4 bit mode and has already received the first nibble, prior to that 3 command sequence starting, then all 3 commands are necessary to put the lcd back into 8 bit mode.
The problem is that since there is no way to know the state of the LCD when the command sequence is started, the code must always do all 3 command "attempts" to reliably get the display back into 8 bit mode so that it can then put the display into 4 bit mode which assures 4 bit nibble sync between the host and the LCD.

But the 4th command / "attempt" for sure is not an "attempt" at all. That command is putting the display into 4 bit mode, knowing that the display is guaranteed to be in 8 bit mode from the prior 3 commands, prior to this "goto 4 bit mode" command being issued.
This command is pretty much guaranteed to work, and when complete the LCD is in 4 bit mode and the host and the LCD are in nibble sync.

It is encouraging to see that you actually followed the data sheet flowchart and turned the display off after the function set and then remembered to turn it back on again after the initialization is complete. Not everyone thinks this two step procedure is necessary but it only takes one extra instruction to do things as recommended.

I'm one of those that does not believe that is necessary.
My view is that all it does is turn off the display during some of programming which tries to ensure that the display doesn't momentarily go pixel whacky while it is being re-programmed from things like font size or rows changing before the display is finally cleared.

--- bill

Just to follow up, on the "3 commands",
There really aren't always 3 commands. E is humped 3 times but depending on the LCD mode,
There are either 3 commands or two commands that are processed by the LCD depending on the lcd state when the sequence is started.

  • in 8 bit mode (3 commands)
    goto 8 bit mode (essentially a nop as LCD is already in 8 bit mode)
    goto 8 bit mode (essentially a nop as LCD is already in 8 bit mode)
    goto 8 bit mode (essentially a nop as LCD is already in 8 bit mode)

  • in 4 bit mode and in sync with host; lcd expecting first nibble (2 commands)
    goto 8 bit mode (sent as 2 nibbles since lcd is in 4 bit mode) - puts lcd into 8 bit mode
    goto 8 bit mode (LCD receives this as an 8 bit command as lcd is already in 8 bit mode - essentially a nop)

  • in 4 bit mode and out of sync with host; lcd expecting 2nd nibble (2 commands
    GARBAGE command (1st byte sent completes unknown command - which is why first delay is so long
    goto 8 bit mode (sent as 2 nibbles since lcd is in 4 bit mode) - this puts lcd into 8 bit mode.

So it really does take three "attempts" to reliably put the LCD into 8 bit mode.

--- bill

Thank you Bill for you explanation. You are absolutely right. I also believe that turning display off is not necessary, but decided to follow the procedure.

I haven't seen any build-in LiquidCrystal_I2C library in Arduino IDE. Where is it?

bodmer:
FLASH size did increase by ~500 bytes for some reason compared to the LiquidCrystal_I2C library the IDE library manager calls up. (Not a big problem though).

Mmm. Maybe because I added some experimental function. Such as "printHorizontalGraph", "printVerticalGraph", "setBrightness" and "busyFlagCheck" for the future use.

enjoyneering:
Thank you Bill for you explanation. You are absolutely right. I also believe that turning display off is not necessary, but decided to follow the procedure.

I haven't seen any build-in LiquidCrystal_I2C library in Arduino IDE. Where is it?

In the IDE library manager with all the other MANY libraries available.

Unfortunately, the way the arduino.cc library manager works is a train wreck in progress.
It updates into the sketchbook/libraries area. That in itself isn't that bad. The bad part is that it is all one naming space so all library names must be unique.
And then the REALLY REALLY bad part is that the IDE itself has not started to use this mechanism tol update its bundled libraries into that same area.
The problem with that is a bundled library will stomp on a custom version you have created in your sketchbook/libraries area.
Also, since the sketchbook is shared across all IDE versions, if you have a recent IDE that updates a bundled library it will install that into the sketchbook/libraries area and it can potentially break for older versions of IDEs.

I have more than 19 different IDEs installed that I do testing on for my libraries, and this is a big problem.
(I never update any IDE bundled libraries, and make sure the auto udpate option is disabled)

I've been trying to get them to change this for years.
IMO, the ideal way would be for libraries to update "in place" and each library has its own .jason file (or it could be done through their .properties file) as to where its repo lives.

That way there is no single naming space and the names no longer have to be unique. And once a library is installed into one of the library areas, the library manager could update it based on where that library says its repository is.
And each version of the IDE installed could update its libraries separately without impacting another IDEs bundled libraries.

The way it works now, the initial setup is totally manual. You have to create an arduino github issue asking somebody at arduino to add your library to "the list" of known libraries. Each library has to have a unique name and it must live on github and follow the library 1.5 library spec in terms of naming and .properties file.
Then once per hour, there is a daemon that scans all the repos of all the libraries in "the list" looking for new tags. If it finds a new tag in the correct version format for a library, it copies the files for that version to its server and then adds that to the list of available versions for that library and the IDE library manager will display it.

If you ignore how it all works under the hood, and the potential issues that it sometimes creates, it is a very cool feature that makes installing libraries super easy.

Since the name you selected is already in use, you would not ever be able to add it to the library manager so that users can install it with just a few clicks from the IDE.

--- bill

enjoyneering:
Mmm. Maybe because I added some experimental function. Such as "printHorizontalGraph", "printVerticalGraph", "setBrightness" and "busyFlagCheck" for the future use.

But normally that stuff should optimized out if not used.
Best thing is to look at a listing output from the linker to see exactly what got pulled in.

--- bill

Small suggestion - the "internal reset / initialization " , I may not be using same term as Hitachi doc does, is really done internally on power up. No software needed for that.

It has its own timing and the result is "all character bits " are on.
So the back-light is important.

Majority of "problems" with first usage of the LCD is people forget about this "power up initialization " or the software "takes off" without the user having a chance to observer the result of it.

BTW since you were given a suggestion not using the same name, I would suggest to drop the "begin" method name too.
You are technically initializing the device functions , not beginning it.

Good show.

Jim

Just to follow up, on the "3 commands" . . .

Regardless of how the LCD controller interprets what is happening the program code should be issuing what is essentially three initial 'function set' commands. Don't forget that the 4-bit mode was not originally intended to 'save' I/O pins, it was designed to interface with 4-bit microprocessors such as the Intel 4004.

My opinion is that, since there is no documentation as to what is actually going on inside the controller, we should not try to figure out what each of the individual steps may or may not be doing.

I think we should conceptually group the first three 'function set' instructions together and describe what they are doing as a whole and that is to get the controller into a known state.

Two terms that come to mind are 'reset' and 'synchronization' but there are probably others.

Don

It took me a bit to realize how the constructor parameters worked.
It doesn't seem to be documented and it is reversed/backwards from all the other LCD libraries.
All the other "LiquidCrystal" type libraries have a set of fixed ordered parameters of lcd function that get passed
i/o pin #s.
The argument position of the parameter determines the hd44780 LCD function and the value is the i/o pin number connected to that LCD function pin.
For direct pin control you pass in Arduino pin #s, for output port devices you pass in the port bit number.

This library uses a fixed set of i/o pins that get passed a hd44780 pin # of the hd44780 LCD function it is controlling.
The argument position determines the i/o port bit number in the expansion port, and the value is the hd44780 pin number of that function. The exception being the backlight which is pin 16 - but that i/o port pin is really driving a backlight circuit and not the actual LCD.

It is very different from other libraries. And is worthy of some documentation to explain how it works.

--- bill

Small suggestion - the "internal reset / initialization " , I may not be using same term as Hitachi doc does, is really done internally on power up. No software needed for that.

Hitachi calls it 'Initializing by Internal Reset Circuit' and it does indeed happen on power up. This is not a problem when the display is used as originally intended, as a clock display or as visual feedback to the operator of a printer or other device. In those cases:

  • the power supply design is part of the overall product design and it can be made to satisfy the requirements
  • the LCD is always powered up at the same time as whatever is controlling it, probably a microprocessor
    In our application neither of these applies:
  • the power supply is frequently an inexpensive wall wart or a computer USB port
  • and >>> the controlling microprocessor may be reset while the LCD controller keeps running <<<

So we can't rely on 'Initializing by Internal Reset Circuit'.

It has its own timing and the result is "all character bits " are on.

No - the display is left OFF, just as it is left OFF in the recommended 'Initializing by Instruction' flowcharts.

Most of the time when people are having problems only some of the character bits are on. These would be the ones that correspond to the first line of the memory (there are only two) and they are the upper row of a two row display, the first and third rows of a four row display, and the left half of most 16x1 displays (which are internally 8x2).

So the back-light is important.

The backlight has nothing to do with the LCD controller and is not mentioned in the Hitachi datasheet. Most displays didn't have a backlight in the digital stone age and those that did required a separate 100v+ power supply (I have some around here somewhere).

Granted it is very hard to see some current displays without a working backlight so that should be dealt with first.

Don

floresta:
Regardless of how the LCD controller interprets what is happening the program code should be issuing what is essentially three initial 'function set' commands. Don't forget that the 4-bit mode was not originally intended to 'save' I/O pins, it was designed to interface with 4-bit microprocessors such as the Intel 4004.

My opinion is that, since there is no documentation as to what is actually going on inside the controller, we should not try to figure out what each of the individual steps may or may not be doing.

I think we should conceptually group the first three 'function set' instructions together and describe what they are doing as a whole and that is to get the controller into a known state.

While the first the 3 humps of E do get the LCD to a known state of 8 bit mode, it isn't always 3 actual function set commands.
The LCD may process it as 3 commands or 2, it depends on the mode the LCD is currently in.
We don't have to know exactly what is going on inside the chip.
All we have to know is the documented commands. And based on that, we can know that the 3 humps of E in that sequence will generate either 3 or 2 actual commands to the LCD. And that in order to cover the case where the LCD is out of nibble sync, the fist hump of E can generate a command that could be almost anything including a home instruction (when processing that first "function set command", the LCD sees it as the 2nd/lower nibble of the instruction and that nibble will have bits 0 and 1 set. So if the first nibble sent prior to the initialization sequence was a zero, the LCD will process a home instruction), and that is why the first delay in that sequence is so much longer than the other delays.
It must accommodate that worst case command execution time (home instruction) since busy can't yet be used.

The initialization tables in figures 23 & 24 have assumed a 100kHz clock which explains why the initial delay is 4.1ms (vs 1.52ms from table 6) and the other delay(s) is 100us (vs 37us from table 6)

Those delays are exactly 2.7x the delays in table 6 for clear/home of 1.52ms and 37us for other instructions.
Nothing magic, the initialization sequence chart uses normal commands but appears to have used a slower reference of 100khz instead of the 270kHz that was used in table 6.

--- bill

While the first the 3 humps of E do get the LCD to a known state of 8 bit mode, it isn't always 3 actual function set commands.

As far as the programmer is concerned they are.

All we have to know is the documented commands.

And there were three of them.

. . . everything else . . .

It's interesting, but we don't really have to care about any of that. All we are concerned about is that the three 'commands' that we sent did what ever was necessary to put the LCD controller into a known state.

Now all we have to do is agree on what what we want to call that sequence. If the device had a hardware input to achieve the same result it would probably be called 'reset'. . . .

Don

floresta:
As far as the programmer is concerned they are.

My point being that the the LCD is not necessarily processing 3 function set instructions even if the host thinks it is sending 3 by setting up the data lines and strobing E three times.

The LCD may only process 2 function set commands from those 3 strobes of E.
The host is driving the upper data pins (d4 to d7) and possibly the lower d0-d3 pins but not necessarily.
When E is strobed, the LCD pulls in either a nibble using d4-d7 or a full byte using d0-d7 depending on whether it is in 4 bit or 8 bit mode.
If the LCD was in 4 bit mode, there will not be three hd44780 function set instructions processed by the LCD for that sequence.
And that is the point, and why it matters, and why there must be three of these E strobes with the Function set bit pattern on D4-d7 done to get reliably get the LCD back into 8 bit mode and why the first delay after sending the initial instruction nibble is longer than the others.

-- bill

floresta:
Now all we have to do is agree on what what we want to call that sequence. If the device had a hardware input to achieve the same result it would probably be called 'reset'. . . .

I would called it "soft reset". During the power up the LCD does hard reset by itself. After the power up, if you reset just an arduino you have to do the "soft reset" or turn off/on screen manually to trigger the hard reset.

As I mentioned, the library can work with custom i2c shields.

LiquidCrystal_I2C lcd(PCF8574_ADDR_A21_A11_A01, 4, 5, 6, 16, 11, 12, 13, 14, POSITIVE);

Where LCD pin no.4 connected to PCF8574 port P0 (pin 4), LCD pin no.5 connected to PCF8574 port P1 (pin 5),
LCD pin no.6 connected to PCF8574 port P2 (pin 6), LCD pin no.16 connected to PCF8574 port P3 (pin 7) and etc

for example if for some reason the LCD pin no.4 connected to PCF8574 port P7 (pin 12) and LCD pin no.14 connected to PCF8574 port P0 (pin 4). Your initialization string will be:

LiquidCrystal_I2C lcd(PCF8574_ADDR_A21_A11_A01, 14, 5, 6, 16, 11, 12, 13, 4, POSITIVE);

The library was almost finished a while ago, the real trick for me was to make it work with any custom shields.

enjoyneering:
As I mentioned, the library can work with custom i2c shields.

LiquidCrystal_I2C lcd(PCF8574_ADDR_A21_A11_A01, 4, 5, 6, 16, 11, 12, 13, 14, POSITIVE);

Where LCD pin no.4 connected to PCF8574 port P0 (pin 4), LCD pin no.5 connected to PCF8574 port P1 (pin 5),
LCD pin no.6 connected to PCF8574 port P2 (pin 6), LCD pin no.16 connected to PCF8574 port P3 (pin 7) and etc

for example if for some reason the LCD pin no.4 connected to PCF8574 port P7 (pin 12) and LCD pin no.14 connected to PCF8574 port P0 (pin 4). Your initialization string will be:

LiquidCrystal_I2C lcd(PCF8574_ADDR_A21_A11_A01, 14, 5, 6, 16, 11, 12, 13, 4, POSITIVE);

The library was almost finished a while ago, the real trick for me was to make it work with any custom shields.

I do not use shields, however, I was under the impression that shield is an hardware interface to processor board hence the LCD I2C shield connection is the I2C - two wire interface - if you prefer to call it that way.
So I2C LCD shield hardware should be concerned only with the I2C interface pins.

The hardware interface between PCF8574 and the LCD itself , or customization, is just a matter of passing correct parameters to the class constructor.
Jim

maybe "shields" means "LCD backpack"?