Pages: 1 2 [3] 4 5 ... 10   Go Down
Author Topic: New LiquidCrystal library - LCD library  (Read 25051 times)
0 Members and 1 Guest are viewing this topic.
Minnesota USA
Offline Offline
Sr. Member
****
Karma: 1
Posts: 323
Made it mahself outta sand 'n wahr.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

nice work!

I have only quickly looked at the code. The standard LiquidCrystal initializes the display when you declare the LiquidCrystal object; you can dispense with the begin() statement if you have the size display it assumes. I don't see that in your code.

When Paul Stoffregen was helping me with optimizing the LiquidCrystal440 line of this we eventually eliminated the 8 bit mode internally (we kept the API but ignored 4 of the pins). We did that because when we were testing the LCD's busy flag, we needed to set pinMode on all the data pins twice. So the fastest modes of writing to the display (using the busy flag) were 4 bit rather than 8 bit. The 8 bit mode is a little faster if you don't check the busy flag, but it isn't a big difference. see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1264823411

Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 36
Posts: 4326
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
you can dispense with the begin() statement if you have the size display it assumes.
That's a big IF.  For some unknown reason the default is for a true 16x1 display, the kind with an auxiliary driver, probably one of the rarest configurations currently available.  

Don
Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 41
Posts: 2182
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi chaps,

thanks for your kind comments.

I was more inline with Don's comments when refactoring the code. It is a big "if" assuming that when you create the object the LCD you have in place is a 16x1, they are very rare and I have a big question mark about how people a currently using the library. It could be argued that it would be more appropriate then to have the default configuration to a 16x2 LCD and initialize the LCD with that configuration.

However, when looking at all the "standard" libraries, they all have a two phase initialization: creation and initialization. Could it be done? Well yes. They only pending item would be for those libraries that don't use the regular 4 bit/8 bit interface such as the I2C or even Serial interface. Because I2C and Serial need to be explicitly initialized after the runtime is up. Therefore, for consistency with the entire LCD library hierarchy, all LCDs behave the same way when being created and initialized.

As for speed improvements, one of the main improvements for the 4bit (LiquidCrystal class) is to initialize all the IOs to outputs and assume that is the state of the pins. So, when writing to the LCD there is no need to change the direction every time. If you want to read back from the LCD, only the read operations would change the port direction, read and restore the default IO pin configuration. These operations would incur in a time penalty, but they are hardly every used. Even in the standard library doesn't read back. Just this change gives almost a x2 speed improvement to the LiquidCrystal library.

fm

Logged

   

Minnesota USA
Offline Offline
Sr. Member
****
Karma: 1
Posts: 323
Made it mahself outta sand 'n wahr.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

part of the advantage of testing the busy flag is reliability. When I've worked on displays that weren't able to keep up with the fixed delays in the standard LIquidCrystal, it became clear that waiting for the busy flag greatly increased the reliability--once you get initialization right (initialization can't use the busy flag). I still puzzle over how much faster the code runs with the busy flag testing than I can make it run without that; I can't just try to slowly reduce the delays until it doesn't run and come anywhere close to the speed of testing the busy flag. Here's a quote from the thread I pointed to before:

Quote
Speed testing
All of the interface modes go faster than the eye can follow.  This version of the software is significantly slower than previous versions when using timed delays. I found an LCD (Axman) that needed longer delays and in the interests of making the code foolproof, I lengthened the delays to make that LCD work. However Paul Stoffregen has significantly speeded up the code when testing the busy flag and so those options run significantly faster than before. I compared the speeds of the different interfaces--writing 80 characters to the screen then 80 blanks and looping through that 20 times. The results  on a Mega are:
Axman 4 data pins no RW 1349 milliseconds  |  nonAxman 1349
Axman 4 data pins  + RW  565 milliseconds  |  nonAxman  468
Axman 8 data pins no RW 1314 milliseconds  |  nonAxman 1314
Axman 8 data pins  + RW  520 milliseconds  |  nonAxman  500
Axman 4 pins + user busy 369 milliseconds  |  nonAxman  316

I also have a Teensy++2.0 board. One of the interesting things about that board is that the software that comes with it includes considerable optimization of digitalRead, digitalWrite etc. The board runs at 16 megaHz, just like the Mega, but speeding up those commands results in an impressive change in the benchmarks:
Axman 4 data pins no RW 1207 milliseconds  |  nonAxman 1207
Axman 4 data pins  + RW  327 milliseconds  |  nonAxman  219
Axman 8 data pins no RW 1212 milliseconds  |  nonAxman 1212
Axman 8 data pins  + RW  361 milliseconds  |  nonAxman  296
Axman 4 pins + user busy 241 milliseconds  |  nonAxman  189
Logged

Minnesota USA
Offline Offline
Sr. Member
****
Karma: 1
Posts: 323
Made it mahself outta sand 'n wahr.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I realized as I was bicycling to work that the code base you started from has a long delay between nibbles when it uses the 4 bit mode. Don Weiman (floresta) pointed out that that is unnecessary. 1 usec is adequate there. I don't have your code here but that change is worth 30-40% improvement in speed with the 4 bit mode, if I recall.
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 36
Posts: 4326
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
1 usec is adequate there.
Correction - 0 usec is adequate there. 

The reason for the time delays (or reading the busy flag) is to allow the LCD controller time to complete acting on a command before you send it a new command.  In the four bit mode after it has received the first nibble it does not start doing anything, it just sits there waiting for the second nibble.  There's no need for any kind of delay because there is nothing going on that needs time to be completed.


Don
Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 41
Posts: 2182
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The objective of the library was NOT to make it faster than the current LiquidCrystal library.
The main objective was to have an extendable library that could: be compatible with what there is, be extendable, be the same library independently of the way you "talk" to the LCD.

This is achieved by having a "two layer" design: the first layer deals with the LCD control (LCD commands), in essence everything common to the particular LCD family, where as the second layer deals with how you talk to the LCD (the specifics of each connection).

By just having a LCD library class hierarchy, with one abstract common class, developers are able  to extend it and users are able to control the LCDs with just one common library . If you come up with a new communication mechanism (for example SPI, which is currently not supported), then simply extend the library. If users now use a pointer to the base LCD abstract class, their code would be able to support any type of LCD assembled into their project just by changing the constructor. The rest of the code would be exactly the same.

There are two main areas that I have modified to get the speed enhancemnets:
1. Remove the IO remaping from imput to output on every single 4/8 bit write transaction.
Since there is no part in the code that reads from the LCD, you simply configure the IOs during initialization and then simply write.

2. I have removed the 1us delay + 100us delay (in fast mode) from the pulse enable toggling.
This is based on the fact that the slow digitalWrites take longer than what the display needs. Since this could potentially be a problem for faster IO routines, I have added a compilation flag that enables it or disables it, i.e. active wait or nothing. Thanks to Bob for sharing his thoughts here.

So as a summary, the LCD library (including virtualization overhead) is 3.25 times faster
than the stock LiquidCrystal library.
The main contributor to these figures is 1, where the speed improvement is 2x.

Can it be optimised even further, yes by using a faster digitalWrite operation and correctly
adjusting the times. Will it be substantially faster? Surely a bit more.

As a conclusion I would like to highlight the LCD library architecture/design more than its performance. To have just one library to control LCDs as opposed to having libraries cropping up all over the place like mashrooms (to control and do exactly the same thing),
that is the main highlight. Extention not replacement nor reinvention. Performance is good, its great since, the MCU can can do more than just control the LCD but not the main KPI of this library concept. Anyway, this is just my thought when I designed this driver.

Current lines of activity:
   - backlight support - under consideration.
   - making the I2C be more generic by having a similar design as the LCD, i.e. a control "layer"
   and an "access" layer, i.e. the IO expansion chip.
   - improve documentation
   - SPI LiquidCrystal support
   - Serial support - under consideration since they tend to be more
   - make it usable to other platforms.

Sorry for the length of the post.
Logged

   

Dallas, TX USA
Offline Offline
Faraday Member
**
Karma: 67
Posts: 2702
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

nice work!

I have only quickly looked at the code. The standard LiquidCrystal initializes the display when you declare the LiquidCrystal object; you can dispense with the begin() statement if you have the size display it assumes. I don't see that in your code.

Well, it is interesting that the LIquidCrystal library did not follow the API recommendations in the LCD API:
Code:
http://www.arduino.cc/playground/Code/LCDAPI

If that were followed, then there would be no begin() and there would be an init() function
which does not take any parameters.
This would have pushed the LCD dimension information back into the constructor, which is where it
belongs IMHO.

I mean its not like the dimensions of an LCD are going to change so in my mind it would have made
much more sense to put it in with the constructor.
But the issue now is that is not backward compatible.
The new constructor with additional dimension parameters could be added to dispense with the begin()
call for those that use the new constructor. However to remain compatible with the existing LiquidCrystal
sketches, you still have to support begin().

-- bill
Logged

Dallas, TX USA
Offline Offline
Faraday Member
**
Karma: 67
Posts: 2702
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

fm,
I totally agree with your approach, which is one I often use.
First design it, then make it work. Then, if needed, make it work fast.


Current lines of activity:
   - backlight support - under consideration.
   - making the I2C be more generic by having a similar design as the LCD, i.e. a control "layer"
   and an "access" layer, i.e. the IO expansion chip.
   - improve documentation
   - SPI LiquidCrystal support
   - Serial support - under consideration since they tend to be more
   - make it usable to other platforms.


For backlight support, I kind of like the API definition in the LCD API:
http://www.arduino.cc/playground/Code/LCDAPI

I think as far as serial support goes, I think that will depend on what this library is meant to be.
Is is an intelligent LCD library or is a hd44780 library?

Today it is really more of a hd44780 library with a very thin LCD API on top of it
(as defined by the LCD API mentioned above)
I think that a more generic smarter API LCD library will require more layers that would sit on top
of this type of library. So if this library is a lower level lcd library for "dumb" devices,
it would require some layers above it to translate from the higher level interface to this interface.

--- bill
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 36
Posts: 4326
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Well, it is interesting that the LIquidCrystal library did not follow the API recommendations in the LCD API:
The LCDAPI in the playground is a proposal brought forth by one user around the time of Arduino v0014 or so.  The LiquidCrystal library was started long before that time and the likelyhood of getting any major change made is probably next to impossible.  As far as I know the API recommendations are not 'official' cast in stone Arduino policy.  As a matter of fact they actually fall in the category of NIH and may never be recognized no matter how logical they are. 

Don
Logged

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 36
Posts: 4326
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
1. Remove the IO remaping from imput to output on every single 4/8 bit write transaction.
Since there is no part in the code that reads from the LCD, you simply configure the IOs during initialization and then simply write.

Maybe I am misinterpreting this but it appears to me that:

(1) You are assuming that the pins used by the LCD will not be used for anything else.  That is really not a good assumption since there are applications involving a keypad and LCD for example that share the data lines.

(2) You are assuming that every other routine that may use these pins is going to return them to the configuration that they found the pins in.

(3) You are assuming that every other routine that may use these pins will not make the same assumption.


Don
Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 41
Posts: 2182
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

[edit]
bill,
[/edit]

For backlight support I was more thinking in the lines of "adding" or "attaching" a backlight control method. This would allow catering for LCDs that don't have a backlight at all. In any case, it is still under the thinking process.

Yes, I have seen the LCD API, as a matter of fact the last contribution is from me:
ST7036 Lib   Generic ST7036 LCD controller   Y   i2c

Again, it is a generic ST7036 library following the LCD API 1.0. and a subclass for a particular NHD LCD. The problem I find with this is that it is an API, i.e. a contract you must comply with when writing a compliant library LCD API library. More than a real driver class hierarchy.

The LCD Library or (New LiquidCrystal library), is meant to be similar to what there is with the current LiquidCrystal library: an LCD access driver to control LCDs based on the hd44780. Extendable, at least as performant as what there is, independent of the interface mechanism, ... The LCD library, nor the LiquidCrystal Library pretend to be an intelligent Display package. This would be something built on top, but with a common LCD driver hierarchy.

So for example, if you wanted to add a character based MMI, you would only have to use a reference to the LCD base class and use it throughout your code. During MMI construction you would simply pass on a reference to the particular LCD you are using. Therefore, any LCD with HD44780 would just use it. Another example could be a scroll widget, bargraph widget, etc. More or less some of the "gadgets" that I use on my projects. Another example is a remote LCD control.

From my point of view and IMHO, ideally there should be a base abstract LCD class that supports any type of character based LCD regardless of their driver, for HD44780 and compatible LCDs, ST7036, etc. Each LCD controller driver, would simply be a specialised subclasses of the LCD with different access mechanism (or maybe not, access could be aggregated). This would be the only way to have a real generic MMI, a pool of generic LCD widgets, a pool of generic LCD controllers, etc. Kind of a visual SDK or a generic building block.

An alternative is to use the poor man's inheritance: API contract that everybody adopts and you just pool in the particular library that your project uses.

The problem is that all the methods in this hypothetical LCD class would have to be virtual and hence would consume a lot of memory and a complete hierarchy would end up eating all the available memory. The library I posted in the LCD API uses this concept where the LCD is a virtual class. I left the class definition there without having the ST7036 inherit from it because of the amount of memory used.

As far as serial is concerned I have it in the cooler because I haven't seen a single module that acts as a "proxy" between the serial port and the LCD. They all have their own proprietary protocol on top.

Just some thoughts.
« Last Edit: November 10, 2011, 03:35:27 pm by fm » Logged

   

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 36
Posts: 4326
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm just a retired engineer with English as my native (and only) language.  Do you think you could rephrase your previous post without all the jargon so non programmers could have a shot at interpreting what you are trying to get across.  I know that in many cases you have to use specific technical language so that your intentions are unambiguious but in a forum such as this it just makes the post unintelligible to many of us.

Don
Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 41
Posts: 2182
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Don,

sorry, the previous post was answering Bill's suggestions, comments and questions. You might have posted the other one while I was writing it.

I'm a non-English speaker and trying my best to share my thoughts (and a bit of my work) the best that I can. Unfortunately most of the English I've learned are from books.

The point I was writing about was about a library that regardless of the LCD you use, you wouldn't have to change the  your application.

To answer your previous questions:

(1) Yes - you are implicitly initializing and allocating the pins to drive the LCD. What it is not a very good assumption is that you implicitly initialize a resource and then someone else changes it on your back.

(2) No - what I am assuming is that if any body is using a shared resource they are managed correctly. You can't give the "responsibility" to a driver to know that its resources are going to be used by someone else. If you follow that rule, then the Wire library would have to make that assumption too and reinitialize the TWI of the AVR every time you use it, for example. That line of thought would have to be rolled over to all "libraries/drivers".

(3) - Please refer to (1) and (2).

If your application uses a device (IO pin, UART, ...) it is the application that has to arbitrate how to use that shared resource (because your application made it a shared device). You can't assume that a dumb driver takes on that responsibility. This is common good practice in engineering and software.
So in the example that you have made about a LCD sharing IO pins with a keypad; it is the application the one that would have to use something to reconfigure the IOs before using those pins by the other device. You can't assume that in the driver. Assuming such thing in an application is not a very good policy at all. This becomes more obvious when you have an OS in between.

fm
Logged

   

Western New York, USA
Offline Offline
Faraday Member
**
Karma: 36
Posts: 4326
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I never did like using libraries.  Now I remember why!

Don
Logged

Pages: 1 2 [3] 4 5 ... 10   Go Up
Jump to: