Go Down

Topic: New LiquidCrystal library - LCD library (Read 70404 times) previous topic - next topic


As for the Wire.h, every single sketch that uses the Wire library has to have the include in the sketch. It is a real pain. How did you sort this out in the GLCD? Relative includes?

Wire.h isn't a problem for GLCD is currently it currently doesn't use or support the 2wire stuff.  :D

With this new LCD  library the issue is that the sketch is not using the Wire library.
The LCD library is the the one actually using the Wire library.
And so the complexity is that when using older LiquidCrystal sketches, the 2wire stuff is not used at all
(no include for Wire.h) even though it is required for the new LCD library modules to compile.

This has to be resolved if you want the library to be a drop in replacement with no edits on older source.
To get it work, I changed the includes in I2CIO files to use "../Wire/Wire.h" instead of <Wire.h>
It works, but it means that the library must live in the arduino libraries directory vs under the uses sketch area.
This probably isn't that big of an issue since in order to use it they have to remove the originally supplied
LiquidCrystal library because this new library cannot coexist with the current LiquidCrystal library.

Have you thought any more about just not providing 100% backward compatibility by default
by say having a hd44780 library and then provide a LiquidCrystal class or thin library
that can be turned on for those that want full compatibility at the expense of removing their current LiquidCrystal library?

Or perhaps maybe never offering 100% backward compatibility and always require them to
edit their code to use the new library?
The edits are quite minimal and are limited to header file names and constructors names.
That way you don't require them to make any changes to their existing Arduino install and they can install
this new library in their sketchbook area.
They can use the new library if they use the new library headers and constructors
and get the stock LiquidCrystal library if they use the LiquidCrystal headers and LiquidCrystal constructors.
It would keep a very clean separation and allow them to install or remove the library in their own
sketchbook area.

--- bill


Oct 31, 2011, 12:10 am Last Edit: Oct 31, 2011, 09:43 am by fm Reason: 1
Ummmm! I need to reconsider after seeing how the entire compilation chain works (people wonder why I like to develop my stuff with eclipse).

For the linker problem, I got round it by not declaring the "begin" and "send" as pure virtual methods. For some reason, which I still need to figure out, the thing doesn't like pure virtual methods on the base class. If I change it to an empty method it is cool.

The nice thing I like about the library is that the I2C implementation on the example files are almost 700 bytes less that the 4bit library (not bad).


I've uploaded version 1.1.1 up to the repository, all tested in 0022 and 1.0-RC2.

Tomorrow, a bit more thinking and considerations regarding the library as a drop-in or not...



Rel. V 1.1.2 of the LCD library has been released with performance enhancements.

Release highlights

  • Version V 1.1.2 is 3.25 times faster than the original LiquidCrystal library

  • Minor comments added to source code

  • Documentation only available in .html format

Project wiki - https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
Project donwload - https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads


Version V 1.1.2 is 3.25 times faster than the original LiquidCrystal library

Which APIs?

Does this measurement take into account the slow down due to calling via the use of virtual methods?

Sounds very interesting. I will try to download next week.



All the operations apart from the home and clear which are slow by controller definition. However, I am running these down to see if I can get them to go a bit faster.

The comparison is 1 to 1, i.e. same sketch running with the LiquidCrystal Lib and the LCD Library, no source code changes, just replace one library by another.

The test, writes 16x2 characters to the display, clears the LCD and starts again. Each cycle the performance measured is about 3.25 times faster.



That is awesome

Thanks for that.
Try it out and let me know how it behaves with your LCD/s. I've got only a few models to try it on.


Very nice. I like it.
It now compiles and runs using existing Liquidcrystal code on 0022, and 1.0rc2
I've tested this on this shield: http://emartee.com/product/42054/Arduino%20Keypad%20LCD%20Shield%20V2.0
Unfortunately, I can't tell you which LCD it is as it is soldered on the shield.

I think the challenge will be for things like I2c and trying to be compatible with other i2c
implementations and to not break other lcd libraries.
For example, your i2c backpack and backpacks
like this: https://www.adafruit.com/products/292
or this: http://emartee.com/product/42098/Wrobot%20IIC/I2C%20LCD%201602%20Shield%20%20B
don't use the same pinout for the bits from the i2c register output going into the hd44780 interface.
Many Arduino users are quite hardware ignorant and may really struggle with these types of differences.
I'm not sure how to best support these different hd44780 I2C backpacks and yet make it easy for the Arduino user.
Its a real tough challenge.

Some vendors use a separate lcd library for their board. This is quite simple and avoids any compatibility
or library collisions as it is a separate library and the user simply copies
over the .cpp and .h files into the libraries directory and thats it. They include the vendors "xxx_i2c.h" header
and then it all works just like the LiquidCrystal library.

While adafruit has a seperate custom i2c lcd library for their backpack: https://github.com/adafruit/MCP23008-library
Adafruit also now has released a multi headed LiquidCrystal library that works for both 4 bit parallel and their
own i2c  or SPI: https://github.com/adafruit/LiquidCrystal
Their implementation is slower than a real i2c implementation as it uses an "arduino like" interface across the i2c. i.e. they
do pinMode(), DigitalWrite() type operations through the i2c to control the lcd pins.
So it does independent i2c operations for setting every single individual hd44780 signal.
It does have the advantage that if they would update their i2c constructor they could configure which
i2c output register bit controls which lcd signal. Today they have it hard coded to their own i2c backpack
but this could allow it work with any backpack.

I like your implementation as it can be made to support any hd4480 backpack.
I think the challenge is how to deal with all these different interfaces when replacing the LiquidCrystal library.
i.e. Your library cannot coexist with the latest AdaFruit library. Both want to replace the LiquidCrystal library
and both have hardcoded i2c implementations that are incompatible with each other.

From an end users perspective, it gets tricky at best and more than likely frustrating with the current state
of libraries in that if you have different i2c backpacks, it may or may not be possible to control them with just
sketch changes. You may have to replace LiquidCrystal libraries between sketch builds for the different
boards. And that is pretty ugly/painful.

As it is today, the i2c lcd libraries look a bit problematic and painful to use if you have more than a single
vendor's i2c lcd backpack.

I think with your library model, it can be resolved.
It could be through either adding additional supported "boards", or by updating the i2c constructors to support
the ability to configure which hd44780 pin is connected to which i2c register bit. kind of the way the
existing LiquidCrystal constructors work with the 4 bit interface. In the case of i2c, the i2c register bit number
would be specified rather than an arduino pin number.
I think the latter, while not the absolute fastest way to run the interface, offers the greatest flexibility and
ease of use for the users especially those that want/need to support different i2c/spi lcd backpacks
that are wired up differently.

On performance tuning, I would caution you to be careful when using some of the techniques that the code is
currently using. For example, in functions like pulseEnable() some of the delays have been removed.
This is making an assumption about a particular API implementation  [ digitalWrite() ] that
offers no such guarantee. ie. digital does not promise to take any particular amount of time.
So it theoretically could take zero time. While 0 really isn't reality, much smaller than 1us or even
the needed 450ns is a potential reality. Consider if this library is to be ported to say the chipKit or the Maple
then you may have issues. Also if the arduino code is compiled differently by compiling all the code together
vs seperately and building core libraries you can get optimizations that can dramatically affect
the timing. If you replace the poor Arduino core code with smarter code, then it could
potentially optimize the digitalWrite() functions down to single cycle instructions which on a standard arduino board
is only 62.5ns

If you still want to go down the path of trying to squeeze out the performance by removing or reducing delays like this,
I'd encourage using some sort of ifdef that can turn them back on to insert the proper delays for environments
that have faster digitalWrite() implementations.
BTW, if you want to step just a bit outside of the Arduino core library functions, there are other delay functions
that can offer shorter than 1us delays with much better accuracy than the Arduino delayMicroseconds() function
which has terrible accuracy.

--- bill


Hi Bill,

thanks again for your feedback. It seams that you are reading my mind... I was just polishing up the I2C class, with new constructors to enable configuration of the control and data pins of the LCD. This is something that I had in mind for the I2C class to enable people to configure the pin mapping of their "backpacks".

Should the backpack, controller chip differ from what is already there, it is a simple question of just extending the class hierarchy.

As far as optimizations are concerned, I am not too concerned about a different digitalWrite coming into the scene, in the sense that people would have to rewrite parts of the SW. However, I do think it is a nice have compilation flag to compile in our out the FAST mode doesn't hurt at all. In those lines, I was also going to write a fast digital write to find the limits of the LCD. How much more will I be able to squeeze out? Perhaps a few usecs per write operation, but it will be very specific to AVRs.

Keep you all posted, stay tuned for version 1.1.3 of the library.


As far as optimizations are concerned, I am not too concerned about a different digitalWrite coming into the scene, in the sense that people would have to rewrite parts of the SW.

Well, in some cases that re-writing has already been done.
There are already Arduino compatible s/w core libraries for the Maple and the Chipkit
which use 32 bit ARMs running at 60-80+mhz.
I have not looked at their digitalWrite() implementation but the processor is already
5-6 times faster. So even if they used the same crappy Arduino core code digitalWrite()
implementation, they would likely already be sub microsecond for digitalWrite() given
that the current digitalWrite() on a 16Mhz AVR is about 4us.

Paul's Teensy for example is AVR based but does not use the same core code.
It's implementation is much better and much faster that the stock Arduino core
- in some cases it can reduce to a single clock cycle for digitalWrite().
The Arduino team refuses to use it.

There is also the digitalwritefast project:
(not the way I'd do it, and not the way the Paul did it for Teensy) but it does
already exist today.

The method used in the dwf project gives you very fast i/o operations when all the constants
are known at compile time.  This method of optimization is quite simple and a little too simple minded.
While it works great for a sketch where all parameters can be known at compile time,
for a library that allows configuration of pins, this would be of no benefit since it would revert
back to the existing digitalWrite().

What really needs to be done is to throw out the current digitalWrite() implementation
and re-write how digitalWrite() works so it not only does the same things that
dwf  is doine  but also reduces the number of lookups since much
of the information being looked up *is* known at compile time.
And that is what Paul's teensy core code does.

If the Arduino team hadn't defined its interface using naked constants (i.e used #defines like DPIN_0 for digital 0 etc
instead of allowing naked constants like 0 or 1 etc)
many other things could be done to speed up the implementation even for pins that are not constants.

BTW, you are looking for additional support of your library, I'd bet you'd be greeted fairly well by
the chipKit and Maple guys as they are more open than Arduino and are also looking to beef up their
library support.

--- bill


Hi Bill,

just uploaded the last changes to the LCD library. I haven't released it as such, but uploaded the change set to the main repo. This is what will become V 1.1.3.

Main highlights:
- Configuration of I2C extender pin mapping (with a very low hit on performance - less than 0,8%).
- Configurable FAST_MODE, i.e. setting the control line pulse duration to be optimized or not.

I have also seen that the library, even without the FAST_MODE active is 2x more performant than the original LiquidCrystal.

If you are interested is in the regular place, but download it through the repo, i.e. (get source).

After a weeks testing I be releasing it.

I have also posted the library to the Maple team, see what they say/think.


Very cool. I've give it a try.
--- bill


Nov 07, 2011, 02:24 am Last Edit: Nov 07, 2011, 02:32 am by bperrybap Reason: 1
I have several questions on the backlight stuff.
It appears that your board uses I2C for the data and control lines but uses a dedicated pin for the backlight control.
Other boards use a pin off the i2c i/o expander to control the backlight.
I see some i2c backlight control code but it looks like it doesn't do anything yet.
It also looked like the i2c backlight masks in LiquidCrystal_I2C.h were assuming an active low signal
from the expander to the backlight would turn it on.
Is this is mid progress?
Is there a solder pad on your board to enable backlight control from the i2c output register instead
of having to use a dedicated pin? (yes no pwm but it saves an AVR pin since on/off controlcould be done through i2c)

The backlight control is challenging, since it looks like it can potentially be
done in multiple ways and can potentially be an active high or active low signal.
Michael and I punted on backlight control so far in the glcd library so I'm very interested
in seeing how this works out.

I think it would be really nice if the backlight control were a function in the base lcd class
so that it could work across all the interfaces rather than just the devices that add it in
as a user expansion.

The tricky part is how to configure it.
What makes it particularly difficult is all the combinations particularly on i2c.
Since the constructors must be able to distinguish between using an AVR pin and a bit
in the i/o expander.
For full flexibility the code also has to know whether the backlight control is active high or active low.
And then there is PWM control.

I'm not sure about the initialization but what about using an interface that is slightly different from
the display() nodisplay() interface for backlight control?
What about using  backlight(pwmvalue) as well as backlight() and nobacklight()
The interface can be used for pwm as well as non pwm.
That way people can turn it on and off and try to use pwm on all devices and those
devices that don't support pwm simply turn it on if the pwm value is non zero.
Just an idea....

The magic is figuring out how to configure the pin.
It seems pretty easy to handle the active low stuff.
One possibility is If the constructor is configured to take integers (int or int8_t vs uint8_t) then
you could use a negative value to represent negative logic (active low) signal
at least on the backlight pin.

I would think that when using 4 bit mode that the backlight pin (if specified) is always an Arduino pin.
The tricky part is how to select between an Arduino Pin and a i/o expander bit when i2c is used.
When using i2c I would think that the default would be that the backlight pin is an i2c expander bit
rather than a Arduino pin.

Maybe it could be handled with a macro so that the user simply specifies his intension in the constructor
and the macro ORs in some magic value to help determine which is which.


Code: [Select]

LiquidCrystal_I2C lcd(lcd_Addr, En, Rw, Rs, d0,  d1, d2, d3,  iobit);
LiquidCrystal_I2C lcd(lcd_Addr, En, Rw, Rs, d0,  d1, d2, d3,  BLARDUINO(arduino_blpin));
LiquidCrystal_I2C lcd(lcd_Addr, En, Rw, Rs, d0,  d1, d2, d3,  -iobit); // negative logic
LiquidCrystal_I2C lcd(lcd_Addr, En, Rw, Rs, d0,  d1, d2, d3,  -BLARDUINO(arduino_blpin)); // negative logic

LiquidCrystal lcd(12, 11, 5, 4, 3, 2, arduino_blpin);
LiquidCrystal lcd(12, 11, 5, 4, 3, 2, BLADRUINO(arduino_blpin)); // maybe either or both for consistency?

Where BLARDUINO() is something like (avoid using integer sign bit)
Code: [Select]
#define BLARDUINO(x) (x|_BV(14))

Maybe even specify the backlight pin type in all cases for consistency.

just a thought.

Another area that may need some attention is the RW line in i2c mode.
Some backpacks like the adafruit don't use the RW line.

--- bill


Hi Bill,

I need to give it a thought. For the I2C driver the intention was to have it in the constructor, considering it as an integral part of the LCD. However, only for those that are part of the backpack. This would be like a specialized method of that class.

Trying to control the backlight is a source of a lot of discussion, in the sense that: do you consider the backlight as part of the LCD or not? When you control them using a 4 bit interface, part of is it just another pin?

Anyway, I will go through your comments and give it a thought. I am sure that you chaps have given it a lot more thought than I have.

As per the RW I did give it a though, this being the main reason for not publishing the library as such. In the previous version you just defined the RW to 0x0 and bingo. Here, I need to overload the value so it is not used as a pin.

In any case, right now is not a big deal since in the library it is not used (only in the constructor) and would work.

Go Up