Arduino Forum

Forum 2005-2010 (read only) => Software => Bugs & Suggestions => Topic started by: jrraines on Jan 30, 2010, 04:50 am

Title: Enhanced LiquidCrystal
Post by: jrraines on Jan 30, 2010, 04:50 am
Modifications to LiquidCrystal for the Arduino

I made several modifications to the LiquidCrystal library module from Arduino17:

40x4 LCDs
I added support for an LCD of 4 LInes and 40 characters. I think that if 24x4, 32x4 LCDs exist, they would also work with the software as I have modified it although I have not had the opportunity to test that. The 40x4 LCD (and any HD44780 based LCD with between 81 and 160 characters) will have 2 enable lines. To use an LCD with 4 lines and 40 columns you would declare your LiquidCrystal object as:
LiquidCrystal lcd(RS,RW,Enable1,Enable2,  data3,data2,data1,data0); at this time I don't support 8 data lines nor do I support eliminating the RW line in this option.[media][/media]Then in the setup function you would call:
lcd.begin(40,4);


Linewrap
When you declare the dimensions of the LCD in your begin call, the LiquidCrystal library remembers how long the lines are. Now when it reaches the end of line 1, text wraps onto line 2 (not line 3 as previously).

16x4 LCDs
The begin statement also correctly positions text at the beginning of the line on 16x4 (and 40x4) LCDs, which were not correctly handled before.

setCursor
In the past setCursor selected a location in the HD44780's RAM not actually a screen location. If you use any of the commands that shift the display left or right with the previous routines, then setCursor and print, text appears in an unexpected location on the screen. With the new software,  if you call either scrollDisplayLeft() or scrollDisplayRight(), the LiquidCrystal package keeps track of the relationship between RAM and the LCD so that setCursor coordinates are pegged to a specific spot on the screen, rather than a spot in RAM. The sotware does not handle autoScroll, however. Call home() after autoScroll to restore the expected relationship between setCursor and the LCD screen.

Testing the LCD Busy Flag
Previous versions of LiquidCrystal always used timed delays on the Arduino side of the interface to give the LCD module enough time to complete its operation. This version still does that if you tie the RW pin to ground and do not tell LiquidCrystal what that pin number is. If you do specify RW now, however, the software will poll the busy flag on the LCD module. Arduino operations may thus overlap LCD operations and potentially things may go a little faster.

Crazy 8 Addressing
16x1 LCDs often have an unusual address layout; these modules often have two 8 character halves and work best with this software if you declare them as lcd.begin(8,2); if you do that, then you can print("abcdefghilklmno"); and have all the characters appear as you would like across the screen. If you use any of the scrolling commands, the bizarre addressing of these modules will manifest itself. For details follow the _LCD Addressing_ link at web.alfredstate.edu/weimandn

Disadvantages
The two real disadvantages I can see to the changes I have made are:

The code is longer than before. Much of the increase is in checkLcdBusyFlag() and this could be fairly easily replaced with delayMicroseconds(100);

2. The possibility that someone with a little 16x2 LCD is using the scrollDisplayLeft() or scrollDisplayRight() instructions to move data across the screen, but wants to write the data 40 characters at a time with a print statement. This version really does not let the user write data to the HD44780 DDRAM which is not visible. To accomplish a scrolling display with 40 characters per line, you would now need to write 16 characters, scroll the display, write a little more and so on.

There are going to be some incompatibilities between code that assumed that line 1 wrapped onto line 3, etc.

Directions for the future
I see no purpose to retaining the 8 pin interface options. In theory, by sending 8 bits at a time, things would proceed faster. However, even in the slowest situation output to the LCD goes faster than the eye can follow. The slowest situation would be using the 40x4 LCD, doing a scrollDisplayLeft() or scrollDisplayRight() and then printing 160 characters. When the display has been scrolled, the software actually calls setCursor internally with every character sent to the LCD, so you would be communicating 480 items to the LCD in that instance. As I have tested the various interfaces with the various shapes of LCDs, I have reflected many times on the likelihood that no one else has tried the 8 bit interfaces for some time. Don Weiman argues that inclusion of both 4 and 8 bit interfaces makes the code harder to read. I am more motivated by the idea that the 8 bit interface takes up Arduino RAM and pins but contributes essentially no additional function.

Thanks
Certainly my efforts would not have been possible without the help and prior efforts of David Mellis, Limor Friede, and Donald Weiman. Don was particularly patient in guiding me through the idiosyncracies of the HD44780 based LCDs and especially in supplying an example of how the busy flag could be tested.

available at    healthriskappraisal.org/LiquidCrystal440.zip
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jan 30, 2010, 01:41 pm
http://healthriskappraisal.org/LiquidCrystal.zip
Title: Re: Enhanced LiquidCrystal
Post by: JaggedToaster on Jan 30, 2010, 04:08 pm
I would like to try this out, but the .zip is not found when I click the link!

Should it be available there now?
Title: Re: Enhanced LiquidCrystal
Post by: JaggedToaster on Jan 30, 2010, 04:17 pm
Nevermind, I noticed in the original post that the file is named LiquidCrystal440.zip.  I was able to download that one.

http://healthriskappraisal.org/LiquidCrystal440.zip
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jan 31, 2010, 03:14 pm
whew. I was afraid the computer I use for a server in my basement had gone down. I'm on vacation and there would be no way to reboot for a week. It usually runs without problems for months.

The original link (in the first post) wasn't a bona fide link because of a restriction on first posts to the Arduino forums. for some reason, the first time you post you can't include a link. So lurk, ready, study and finally post when you have something to say is deprecated behavior.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 04, 2010, 01:13 pm
It does look like the server will be down until 2/7 when I get home.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 04, 2010, 09:38 pm
Arghh, damn, you couldn't send me the library could you? I am feeling impatient! PM me if you could.

I have just picked up a 20x4 LCD and would like to get it working.

Thanks,

Mowcius  ;)
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Feb 05, 2010, 01:52 am
John:

I will send him a copy.

Don
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 05, 2010, 09:34 am
Yeah to earlier post I meant 40x4 not 20x4... :P

Thanks.  ;)
 
Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 05, 2010, 10:08 pm
Ok, I have got hold of a 40x4 LCD. It was apparently made by Nan Ya Plastics Corp (well it says that on it), but after a long google search I found nothing for any part numbers or other info.

Well I then looked around for displays that looked similar and came across this one: http://www.lumex.com/products/detail/190mm_x_54mm2 which looks very similar if not identical. Anyway I connected up the rw, rs, en1, en2 and four data lines (pins 1-4) and using the library, defining it as 4 rows and 40 columns I got it to display something. At first I just got a load of random characters (well 1/4, 1/2, ? etc) and two flashing cursors. After removing power and resetting I just got two flashing cursors in random positions, changing each time I reset.

These pictures will explain better:
(http://farm5.static.flickr.com/4031/4332666387_24b7e4236b.jpg)
(http://farm5.static.flickr.com/4055/4332666621_9fb934c0ef.jpg)

Anyone got any idea of why this is happening/anything I can try?

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Feb 06, 2010, 01:48 am
Quote
... and four data lines (pins 1-4) ...

It looks like pin 1 = D7, Pin 2 = D6, Pin 3 = D5 and Pin 4 = D4.  Is that the way you interpreted (and connected) it?  Are you sure?  Did you doublecheck it?  Did you rip all the wires out and put them back?

Don
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 06, 2010, 11:31 am
I connected 1,2,3,4 on the display to 1,2,3,4 data on my arduino (pins 8,7,6,5 just to be confusing).

I then adjusted the code for 20x4 and set the pins to what I had connected. I will have a play around with different configurations today and I will see what I can come up with.

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 06, 2010, 10:55 pm
Ok, I got it sorted. Yeah I had the 4 data lines the wrong way round. It is now working like a dream!

I have however tried to connect it up to my duino644 board and it will work on one 'port' but not the one I want to use it on. I have checked (about 4 times at least) to make sure that the connections are right and the pins are not being used for anything else but I can't get it to work. The library obviously does not need PWM pins (it worked on the other port with no PWM pins) but are there any specific types of pins it requires?

Or has anyone got any other ideas?  ;)

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 07, 2010, 06:21 pm
The server is back up. I constructed a tiny benchmark to compare speeds of the different interfaces. I wrote 80 characters, then 80 blanks, looped through it 20 times. Of course it goes too fast to read, but here is what I measured:
8 data pins + RW 432 milliseconds
8 data pins - RW 641 milliseconds
4 data pins + RW 719 milliseconds
4 data pins - RW 1038 milliseconds
The options with RW are testing the LCD busy flag, of course, so testing the flag is about 1/3 faster. Again all of the options go faster than you can read. And if complex calculations were interspersed with sending the characters to the LCD you would see even bigger speed increases.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 07, 2010, 06:31 pm
Yeah thanks a load for the library. My 40x4 has been a great new (free) toy!

I haven't got an immediate use for it but I am thinking of a 4 line, big number clock with email alerts and other info...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Feb 07, 2010, 08:10 pm
John:
Quote
8 data pins + RW 432 milliseconds
8 data pins - RW 641 milliseconds
4 data pins + RW 719 milliseconds
4 data pins - RW 1038 milliseconds

I don't think there should be that big a difference between the 8-bit and the 4-bit speeds for the same RW configuration.  I haven't checked your code but I hope you took into account the fact that there is no delay (or busy check) required between the sending of the high nibble and the sending of the low nibble.  

Don
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 07, 2010, 11:31 pm
One more option for connecting the 40x4 LCD that I realized was there--not a change to the code but just using the fact that internally the flag for the RW line not having been passed is 255.

LiquidCrystal lcd(RS,255,Enable1,Enable2,  data3,data2,data1,data0);

which saves one pin in the interface. Again it runs about 1/3 slower this way.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 07, 2010, 11:45 pm
I AM checking the busy flag between nibbles. When I change that, it does speed things up by about 1/3. I have seen a couple of odd characters on the screen though. I will run this through the complete test procedure.

I'm impressed once again at how perceptive you are about problems with interfacing these LCDs!  

Thanks
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 08, 2010, 02:44 am
Don Weiman pointed out that the benchmark was slower than it should be for the 4 data line options and correctly intuited that I was checking the busy flag between nibbles. He was exactly right. I have updated the zip file and the new benchmark data is:
8 data pins + RW   431 milliseconds
4 data pins + RW   532 milliseconds
8 data pins - RW    641 milliseconds
4 data pins - RW    687 milliseconds

The standard library routine also includes an unnecessary delay between nibbles (calling pulseEnable for each nibble includes the 100 microseconds delay there).

Thanks again to Don
Title: Re: Enhanced LiquidCrystal
Post by: wortelsoft on Feb 09, 2010, 09:45 am
Looks like your site is down again.
I'm not able to download tried yesterday and today.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 09, 2010, 12:26 pm
http://healthriskappraisal.org/LiquidCrystal440.zip
seems to work, as does the main site.
Title: Re: Enhanced LiquidCrystal
Post by: wortelsoft on Feb 09, 2010, 12:40 pm
Ok, it seems to be a problem with the firewall you are using port 8000.
Will have to try it from a different pc.


[edit]it was the firewall[/edit]
Title: Re: Enhanced LiquidCrystal
Post by: borref on Feb 10, 2010, 01:43 pm
jrraines:

I was curious as to how you checked for the busy flag and looked at your code. It seems to me you have a bus issue that may damage the LCD and/or the Arduino when checking the busy flag.

The way I read the HD44780 datasheet is that the full bus (8 datalines) will be reversed when you pulse EN and RW is low. In your code however you only reverse the Arduino busy bit (B7) to input. The other bits (B0 to B3 for 4-bit and B0-B6 for 8-bit) will then be low impedance output as will the Arduino pins. If the LCD/Arduino outputs are at opposing logic levels you will have a dangerous short.

Did I miss something or are you reading the datasheet differently?
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Feb 10, 2010, 03:17 pm
John:
Quote
... seems to work, as does the main site.
I'm having trouble getting past the second page on my Health Risk Appraisal.  After I click on "Ask me the questions..." I get an almost blank screen with the message "no age,sex cookie found.<BR>Quest1:  {}"  This happens with Firefox 3.5.7 and IE 7.0.x on Windows XP.  Maybe it has a premonition that my appraisal will be too bleak.

Back to the real topic...  
Quote
It seems to me you have a bus issue ...

It seems to me that Ben is correct.  I think I reversed all of the bits on the code I sent you.  I may even have reassembled the byte. You can never tell when you might want to use the address information that is provided on those bits.

Don
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 11, 2010, 02:44 am
Yikes.
I was reluctant to set all the pins to input because I thought it would take a long time. I have not seen a problem here after running quite a lot of data through the interface but if Ben and Don think there's an electrical issue, they are probably right. I will work on this; in the meantime stick with the options that don't pass RW since those don't test the busy flag, leave RW low and data in one direction.

We'll see how timing comes out if I switch all of those pins' mode.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 11, 2010, 03:31 am
quickly ballparking performance -- I'd added a couple of things since the previous benchmark to make println work properly so this isn't quite the same as what was benchmarked before:
8 data pins +RW 619
8 data pins - RW 650
4 data pins +RW 678
4 data pins -RW 734

So I'm thinking that pulling out all of the busy flag testing stuff makes a lot of sense; there are several hundred bytes of code and the performance advantage is less than 10% if I switch all the data pins to INPUT during the busy flag test and then back to OUTPUT when that is done.

It will probably be the end of the weekend before I do all of that and test it thoroughly; it'll be shorter than what I have now, slightly faster than the non-busy flag test above, still work with 40x4, fix linewrap, fix println, fix scroll preceding setCursor, fix the 16x4 thing.

I really have run an awful lot of characters through this thing without seeing an electrical problem but I certainly have to defer to the engineering guys; the logic diagram describing the arduino digital pins, pullup resistors etc is considerably beyond me.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 12, 2010, 01:24 am
I did get the code modified and reposted tonight.
http://HealthRiskAppraisal.org/LiquidCrystal440.zip

Modifications to LiquidCrystal for the Arduino

I made several modifications to the LiquidCrystal library module from Arduino17:

40x4 LCDs
I added support for an LCD of 4 LInes and 40 characters. I think that if 24x4, 32x4 LCDs exist, they would also work with the software as I have modified it although I have not had the opportunity to test that. The 40x4 LCD (and any HD44780 based LCD with between 81 and 160 characters) will have 2 enable lines. To use an LCD with 4 lines and 40 columns you would declare your LiquidCrystal object as:
LiquidCrystal lcd(RS,RW,Enable1,Enable2,  data3,data2,data1,data0); at this time I don't support 8 data lines. (You can pass 255 as the RW item, ground RW and save an Arduino pin.)
Then in the setup function you would call:
lcd.begin(40,4);

Linewrap
When you declare the dimensions of the LCD in your begin call, the LiquidCrystal library remembers how long the lines are. Now when it reaches the end of line 1, text wraps onto line 2 (not line 3 as previously).

println
Although print has worked properly in the past, println has not. Now the '\r' and '\n' characters are not sent to the screen as though they were visible characters and the '\r' resets the character position to the top of the next line.

16x4 LCDs
The begin statement also correctly positions text at the beginning of the line on 16x4 (and 40x4) LCDs, which were not correctly handled before.

setCursor
In the past setCursor selected a location in the HD44780's RAM not actually a screen location. If you use any of the commands that shift the display left or right with the previous routines, then setCursor and print, text appears in an unexpected location on the screen. With the new software,  if you call either scrollDisplayLeft() or scrollDisplayRight(), the LiquidCrystal package keeps track of the relationship between RAM and the LCD so that setCursor coordinates are pegged to a specific spot on the screen, rather than a spot in RAM. The sotware does not handle autoScroll, however. Call home() after autoScroll to restore the expected relationship between setCursor and the LCD screen.Speed testing
All of the interface modes go faster than the eye can follow. 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 are:
4 data pins 727 milliseconds
8 data pins 644 milliseconds
The 4 data pin option is significantly faster than the previous LIquidCrystal which takes 1076 milliseconds because of an unnecessary delay between sending the high and low data nibbles.

Crazy 8 Addressing
16x1 LCDs often have an unusual address layout; these modules often have two 8 character halves and work best with this software if you declare them as lcd.begin(8,2); if you do that, then you can print("abcdefghilklmno"); and have all the characters appear as you would like across the screen. If you use any of the scrolling commands, the bizarre addressing of these modules will manifest itself. For details follow the _LCD Addressing_ link at web.alfredstate.edu/weimandn

Disadvantages
The two real disadvantages I can see to the changes I have made are:

The code is a little longer than before.

2. The possibility that someone with a little 16x2 LCD is using the scrollDisplayLeft() or scrollDisplayRight() instructions to move data across the screen, but wants to write the data 40 characters at a time with a print statement. This version really does not let the user write data to the HD44780 DDRAM which is not visible. To accomplish a scrolling display with 40 characters per line, you would now need to write 16 characters, scroll the display, write a little more and so on.

There are going to be some incompatibilities between code that assumed that line 1 wrapped onto line 3, etc.

Directions for the future
I see little purpose to retaining the 8 pin interface or RW pin options. Even in the slowest situation output to the LCD goes faster than the eye can follow. The slowest situation would be using the 40x4 LCD, doing a scrollDisplayLeft() or scrollDisplayRight() and then printing 160 characters. When the display has been scrolled, the software actually calls setCursor internally with every character sent to the LCD, so you would be communicating 480 items to the LCD in that instance. As I have tested the various interfaces with the various shapes of LCDs, I have reflected many times on the likelihood that no one else has tried the 8 bit interfaces for some time. Don Weiman argues that inclusion of both 4 and 8 bit interfaces makes the code harder to read. I am more motivated by the idea that the 8 bit interface takes up Arduino RAM and pins but contributes little additional function.

Bug
The one bug I am aware of is that autoscroll() on a 40x4 LCD only scrolls 2 lines despite my best efforts.

Thanks
Certainly my efforts would not have been possible without the help and prior efforts of David Mellis, Limor Friede, and Donald Weimann. Don was particularly patient in guiding me through the idiosyncracies of the HD44780 based LCDs.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 12, 2010, 09:30 am
Quote
The one bug I am aware of is that autoscroll() on a 40x4 LCD only scrolls 2 lines despite my best efforts.

Yeah... :( I don't need that for anything though. Thanks for the updated code.

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 12, 2010, 12:42 pm
I've spent several hours trying to fix it and have absolutely no use for autoscroll myself. Interestingly I can get it to be either group of 2 lines depending on where on the screen I was writing last. the LCDtest suite (included in the download) demos this. It of course doesn't affect smaller LCDs with only one HD44780. As far as I can tell it doesn't affect the other commands that need to go to both HD44780 chips on the 40x4--clear, home, righttoleft text mode, scrolling under program control, display on/off etc.  >:(
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 12, 2010, 01:30 pm
one more clue, perhaps, is that the while which 2 lines autoscroll seems to depend on where I wrote last (the 2 lines I didn't write last are the ones that scroll), the autoscroll commands are always sent first to the upper 2 lines then to the lower 2 lines.

I think I am finished with the project at this point, unless some smart person can figure out that last bug. I've done a lot more than I set out to do at the beginning, which was just to get it working with the 40x4 display. I've learned a lot and had a lot of fun doing it.
Title: Re: Enhanced LiquidCrystal
Post by: TBAr on Feb 13, 2010, 03:24 am
Quote
I've learned a lot and had a lot of fun doing it.

... and provided a very useful contribution to the community on your first try. [smiley=dankk2.gif]
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 13, 2010, 10:17 am
Quote
... and provided a very useful contribution to the community on your first try.

Agreed  [smiley=thumbsup.gif]

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 13, 2010, 10:50 am
Hey is the server down again. I have downloaded your new code but appear to have lost it and I don't seem to be able to download it again...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 13, 2010, 04:17 pm
Thanks to Ben and Don for all their help. I think the server is OK now.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 13, 2010, 08:15 pm
It did download again, eventually (took like 5 minutes) but thanks again for the code.

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 18, 2010, 12:14 pm
I've known about the bug with 40x4 displays and autoscroll now for 3 weeks and it finally occurs to me that there is surely an UGLY rather stupid work around for it. I haven't tested it actually but I'm sure it would work. it would involve calling autoscroll twice in succession. I suppose it might be necessary to reposition the cursor between the 2 calls. Even if for some reason the hd44780 controlling the 2 lines that scrolled after the first command received the second one, it would just keep scrolling. It could even be built into the library as a special case only with 40x4 displays and I could pretend I'd fixed the bug. But it would be ugly and stupid. :-/
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 18, 2010, 12:42 pm
Well ugly and stupid would be better than not working...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 19, 2010, 01:25 pm
I posted the idea so people could use the work around. And perhaps to inspire someone with a different perspective to see a real fix.

Scrolling gets blown out of proportion when I work on the library, because its the complicated bunch of things the display can do and so is involved in many of the tests in my test program (which is included with the download). In real code, I have no use for it. I knew about the bug for at least 2 weeks before I posted anything about it; I was pretty sure no one would find it in a short period of time; I wouldn't have found it except that I started putting together a thorough test procedure when I started working on the library, adding tests as I added features and found problems to demonstrate. Then before I post code I run 4 interfaces on 4 different shapes of LCDs, 2 interfaces on the 40x4.
That takes almost an hour but it leaves me pretty confident I've found everything I ever knew could be a problem.
Title: Re: Enhanced LiquidCrystal
Post by: brtech on Feb 19, 2010, 04:51 pm
Great work.

I've been wondering about the RW pin.  

I have a project in development that needs a very fast bar graph.  I was going to use the programmable characters to make the last segment variable length to get a one dot wide bar resolution.

There are two, possibly academic questions:


I was thinking of an experiment that involves getting the bar graph working, and then using something like an adjustable sine wave synthed signal to drive it.  Walk the frequency up until you can't really get the bar graph to follow the signal.  Try that with and without the RW.

I'm in the middle of building a shield for this project and can't try this experiment right now, but I'm planning to.  Do you have an old version of the code around I could try?
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 19, 2010, 08:51 pm
Well I would like to do a bar graph on my 40x4 but it seems very slow at refreshing. When I write one character over another it is visibly taking a long time. Any ways of speeding it up?

I did see something on fast refresh on LCDs some time ago but I can't for the life of me find it. Anyone else know where it is?
[edit]I can't have been looking very hard before, here it is:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1264215873/0[/edit]

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 20, 2010, 03:51 am
"writing 80 characters to the screen then 80 blanks and looping through that 20 times. The results are:
4 data pins 727 milliseconds
8 data pins 644 milliseconds"
so (80+80)*20 = 3200 characters in less than 3/4 of a second. your bar graph is one line long so either 20 or 40 characters. Lets say you have 40 chars. 727 *40/3200 =  9 milliseconds. Probably the rest of your code will be the limiting factor.

in the benchmark I print 80 character long strings. If you use a for loop and call once per character, of course it will go slower because of all the loop and call overhead. If you need maximum speed you might create an array of strings of different lengths for the different bar lengths.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 20, 2010, 12:14 pm
Yeah it was the other code. After having a play around it is much faster now :)

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 20, 2010, 12:40 pm
other thoughts about bar graphs that may be obvious:

if you're using an array of strings they should follow the pattern "x    ", "xx   ","xxx  ",.. so that trailing spaces overwrite what was left over from last time. If you use the for loop and single character print you want to do the same thing--print spaces over the old bar.  Using clear takes 2 milliseconds and then you have to reconstruct the whole screen.

A program that does bar graphs is likely to want to divide somewhere and if you can work it out so that >> will do, that is a LOT faster. so putting a short label on the line and having 16 or 32 segments in the bar may have a big payoff in speed.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 20, 2010, 12:47 pm
Yes, I will be sticking a bargraph in my media player project. It has an rMP3 and a 40x4 LCD showing lyrics, time, track etc. I will have a bargraph for the volume and for the track length (probably) but they don't need to be very fast...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: brtech on Feb 20, 2010, 05:36 pm
I'm probably overthinking this.  I had thought that the display had something like a 200Hz refresh cycle, but that appears to be the refresh of one row, not the whole display.  The datasheet I'm looking at shows a 64hz frame rate.  at 64 Hz, I only need to get the bar graph into the LCD in 15 ms more or less, which clearly works without the RW.  I don't really want to use the RW, because I want to use the pin for something else :)
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 20, 2010, 10:58 pm
Ok, for my media player thing (mentioned above) I have been experimenting with code.

I have a for command measuring the volume (mapped from 0-39) and then it writes the boxes on the LCD line accordingly. I tried getting it to write spaces over the unused places but it was very slow. I am currently getting it to write 40 spaces over the whole line before the write and this is just visible (when the display is doing nothing else).

Anyone got any ideas of what I can do to speed it up?

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 21, 2010, 12:51 pm
The media player sounds like a neat project!
How do you map the volume to 40 segments? Integer division is slow, but not a disaster. Floating point IS a disaster in terms of performance, of course. I suggested 32 segments and >>. There may be a way to think in terms of 64 segments and >> with the result ending up in only 40 real segments; I'm usually too dull to work tricks like that out.

If you were really willing to dig into Liquid Crystal you could take what I had posted with the busy flag, stop making it a library but pull it into your own program. At that point you know which pins are used and you can combine those 2 things with issue 140 in the google code forum for Arduino to use AVR ports instead of digitalRead and digitalWrite. This would make checking the busy flag feasible, faster than ever, and hugely speed up some other small places. You would need to use RW, so you'd lose that pin. There were some delayMicroseconds(1) lines that I commented out but never actually removed from the source which would have to be reactivated. That would be a lot of work but the Liquid crystal speed up would be roughly 40% over what it is now, I think. As long as its a library routine, the AVR ports/issue 140 thing seems unavailable.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 21, 2010, 12:54 pm
One of the tricks to using AVR ports is that you can reverse the 4 data pins from write to read during the busy flag check with 1 instruction rather than 4 separate slow calls to functions that have a complex job mapping them; you and the compiler would now know at compile time how that mapping took place. That's where the big saving comes from.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 21, 2010, 02:17 pm
As I was walking the dog I realized that one way to implement this would be to leave liquid crystal as a library routine, but when you call it pass in a function name for it to call to check the busy flag. then you would have to write that one routine. I REALLY like this idea. I will probably get to it in the next couple of weeks.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 21, 2010, 03:12 pm
With the media player, using the rMP3 board gives me a reverse volume of 0-254 (0 being the highest)
I am ignoring any data below 40 (it is pretty quiet then) and then turning the data the opposite way to get it as 40 being the highest (then taking away 1 to write to the corresponding segments). I am however mapping a potentiometer input to control that volume. Anyway here is that bit of code. Not great but it works ok.

Code: [Select]
     volume = map(analogRead(4), 0, 1020, 40, 0);
     if(volume == 40)
     rmp3.setvolume(254);
     else
     rmp3.setvolume(volume);
     vol = 40-volume;

then slightly further down there is this:
Code: [Select]
lcd.setCursor(0,3);
     lcd.print("                                        "); //40 spaces - seemed like the fastest way
     for(int i=0; i<=vol-1; i++){
     lcd.setCursor(i,3);
     lcd.print(255, BYTE);
     }


Here is an image before I got the volume graph going:
(http://farm5.static.flickr.com/4057/4349744630_c7976d814d.jpg)

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 21, 2010, 04:24 pm
you don't need to setCursor every time; just once above the loop. It should run about 3 times as fast then; x and y each take about as much time as a character.

You need to test >= 40 rather than ==.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 21, 2010, 04:35 pm
Quote
You need to test >= 40 rather than ==.

Code: [Select]
volume = map(analogRead(4), 0, 1020, 40, 0);

It should never go above 40 due to the map command. The analog read will never go below 0. I'm sure I put a constrain in there somewhere though...

Regarding the setCursor, it needs to be there due to it using i to set the position. If it was outside then it would not work.

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 21, 2010, 09:05 pm
try this:
      lcd.setCursor(0,3);
     lcd.print("                                        "); //40 spaces - seemed like the fastest way
     lcd.setCursor(0,3);

     for(int i=0; i<=vol-1; i++){
     lcd.print(255, BYTE);
     }
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 21, 2010, 09:18 pm
Quote
try this:
     lcd.setCursor(0,3);
    lcd.print("                                        "); //40 spaces - seemed like the fastest way
    lcd.setCursor(0,3);

    for(int i=0; i<=vol-1; i++){
    lcd.print(255, BYTE);
    }

No cos that does not tell it where to put the 255s, the code fills up the 'i' positions with '255' (black boxes) to show the volume in a bar...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: Fandu on Feb 22, 2010, 04:34 am
It seems that the server is down again...  is this library available anywhere else?

http://healthriskappraisal.org/LiquidCrystal440.zip

Thanks.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 22, 2010, 09:35 am
Quote
is this library available anywhere else?

Not that I know of. The link seems to work if you wait long enough.

[edit]Maybe it's not going to work today :([/edit]
I will email it to you. Word of advice though, I recommend you hide your email address in the user CP as it is very visible to spammers at the moment...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Feb 22, 2010, 12:34 pm
Well that was different; the GFI wall socket had tripped. usually its one of the routers (I think they both have memory leaks that bring them down about monthly).
Thanks, Mowcius, for emailing him the software.
Title: Re: Enhanced LiquidCrystal
Post by: Fandu on Feb 22, 2010, 06:04 pm
Quote
I will email it to you. Word of advice though, I recommend you hide your email address in the user CP as it is very visible to spammers at the moment...

Mowcius


Thanks for the email, and thanks for the heads up on the contact info.  Took me a while to figure out how to even turn it off.   :-?

-Fandu
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Feb 22, 2010, 10:34 pm
Yeah I am always wise to look through user settings on every forum to check for that kinda stuff...

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Mar 08, 2010, 12:53 am
I did add 2 more calling modes with allow a user-written test of the LCD busy flag. I tried to keep using it simple but it is necessarily more complex than the previous version, I put the new version on the server at:
http://healthriskappraisal.org/LiquidCrystalBusy.zip
The previous version is still available.

Modifications to LiquidCrystal with callback busy test

I made several modifications to the LiquidCrystal library module from Arduino17:

40x4 LCDs
I added support for an LCD of 4 LInes and 40 characters. I think that if 24x4, 32x4 LCDs exist, they would also work with the software as I have modified it although I have not had the opportunity to test that. The 40x4 LCD (and any HD44780 based LCD with between 81 and 160 characters) will have 2 enable lines. To use an LCD with 4 lines and 40 columns you would declare your LiquidCrystal object as:
LiquidCrystal lcd(RS,RW,Enable1,Enable2,  data3,data2,data1,data0); at this time I don't support 8 data lines. (You can pass 255 as the RW item, ground RW and save an Arduino pin.)
Then in the setup function you would call:
lcd.begin(40,4);

Linewrap
When you declare the dimensions of the LCD in your begin call, the LiquidCrystal library remembers how long the lines are. Now when it reaches the end of line 1, text wraps onto line 2 (not line 3 as previously).

println
Although print has worked properly in the past, println has not. Now the '\r' and '\n' characters are not sent to the screen as though they were visible characters and the '\r' resets the character position to the top of the next line.

16x4 LCDs
The begin statement also correctly positions text at the beginning of the line on 16x4 (and 40x4) LCDs, which were not correctly handled before.

setCursor
In the past setCursor selected a location in the HD44780's RAM not actually a screen location. If you use any of the commands that shift the display left or right with the previous routines, then setCursor and print, text appears in an unexpected location on the screen. With the new software,  if you call either scrollDisplayLeft() or scrollDisplayRight(), the LiquidCrystal package keeps track of the relationship between RAM and the LCD so that setCursor coordinates are pegged to a specific spot on the screen, rather than a spot in RAM. The sotware does not handle autoScroll, however. Call home() after autoScroll to restore the expected relationship between setCursor and the LCD screen.Speed testing
All of the interface modes go faster than the eye can follow. 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 are:
4 data pins 732 milliseconds
8 data pins 647 milliseconds
busy test    432 milliseconds
The 4 data pin option is significantly faster than the previous LIquidCrystal which takes 1076 milliseconds because of an unnecessary delay between sending the high and low data nibbles.

Crazy 8 Addressing
16x1 LCDs often have an unusual address layout; these modules often have two 8 character halves and work best with this software if you declare them as lcd.begin(8,2); if you do that, then you can print("abcdefghilklmno"); and have all the characters appear as you would like across the screen. If you use any of the scrolling commands, the bizarre addressing of these modules will manifest itself. For details follow the _LCD Addressing_ link at web.alfredstate.edu/weimandn

User callback busy test
Get LiquidCrystal running without this first; setting up this complicated option is error-prone and this should be left for last, if implemented at all.

The LCD has a busy flag which is intended to notify the processor when it is ready to accept a new command. Testing it involves manipulating almost all the pins in the interface and is not really practical inside the library routine because of the overhead of pinMode,digitalRead and digitalWrite. If the pin and ports were always the same, faster manipulation would be possible. In fact, the port to pin correspondence hasn't remained constant across boards, so even if everyone used the same pin numbers, code that manipulated ports would fail:
                       Arduino 8         Arduino 168/328             Arduino Mega
Digital Pin            Port                     Port                                 Port
      0                   PD0                     PD 0                               PE 0
      1                   PD1                     PD 1                               PE 1
      2                   PD2                     PD 2                               PE 4
      3                   PD3                     PD 3                               PE 5
      4                   PD4                     PD 4                               PG 5
      5                   PD5                     PD 5                               PE 3
      6                    PD6                     PD 6                               PH 3
      7                    PD7                     PD 7                               PH 4
      8                   PB0                     PB 0                                PH 5
      9                   PB1                     PB 1                                PH 6
     10                  PB2                     PB 2                                PB 4
     11                  PB3                    PB 3                              PB 5
     12                  PB4                     PB 4                                PB 6
     13                  PB5                     PB 5                                PB 7


The way to be able to test the busy flag and know the pin/port numbers is to use a callback function and let the testing be done in the user's code rather than inside the library.

if you've ever used attachInterrupt on the Arduino, you've used a callback function. You write a function and pass the address of the function to another routine in a function call. In this case, I've added a couple more forms of the (very overloaded) LiquidCrystal object. Now you can specify your callback function like this for the common case of displays up to 20x4:
LiquidCrystal lcd(49,45, 35,33,31,29,&checkBusyFlag);//RS,EN,D0,D2,D1,D3

Notice that in this case, RW must be attached to a digital output pin and the user code must set that pin LOW before calling LiquidCrystal. The busy routine will be manipulating the status of RW but the main LiquidCrystal routine doesn't manipulate it or know which pin it is. (These pin numbers will likely look unfamiliar to you; on my Mega, I like to plug the LCD into the socket on the board directly with pin 1 in a ground, pin 2  into socket 53, pin 3 in socket 51 and so on.)

You would declare your LiquidCrystal object like this for a 40x4 LCD :
LiquidCrystal lcd(48,47,46,52, 41,40,39,38,&checkBusyFlag);//RS,RW,EN,EN2,D0-D3

Then you can modify the macro definitions which are immediately below (busy is the data3 item in your call to LiquidCrystal):

Code: [Select]
//LiquidCrystal lcd(49,45, 35,33,31,29,&checkBusyFlag)  //RW is pin 47
#define set_data_pins_to_read DDRC&=~0b01010100,DDRA&=~0x80;//d0,d1,d2,d3 pins35,33,31,29
#define set_data_pins_to_write DDRC|=0b01010100,DDRA|=0x80;    // C2,C4,C6,A7
#define set_EN_high PORTL |= 0b00010000;    //port L4 pin45
#define set_EN_low PORTL &= ~(1<<4);
#define set_EN2_high PORTL |= (1<<4);    //portL4 the EN2 if you have a 40x4 LCD
#define set_EN2_low PORTL &= ~(1<<4);
#define set_RW_high PORTL |= (1<<2);
#define set_RW_low PORTL &= ~(1<<2);    //port L bit 2 pin47
#define set_RS_high PORTL |= (1<<0);    //port L bit 0  pin 49
#define set_RS_low PORTL &= ~(1<<0);
#define read_busy PINA & (1<<7);         //port A7  pin29   the last pin number spec'd to LiquidCrystal

//You DON'T have to modify the subroutine itself:
void checkBusyFlag(int8_t chip) {
uint8_t busy; // = 0x04;
set_data_pins_to_read;
set_RW_high;             //RW to read
set_RS_low;
if (chip == 0) {  //the if and else can be eliminated if only one hd44780 chip eg 20x4
do {
  set_EN_high;
  delayMicroseconds(1);
  busy = read_busy;    // read busy flag
  set_EN_low;
  delayMicroseconds(1);
  set_EN_high;
  delayMicroseconds(1);  //pulse the second nibble--discard it;
  set_EN_low;
}while (busy);
} else {
  do {
  set_EN2_high;
  delayMicroseconds(1);
  busy = read_busy;    // read busy flag
  set_EN2_low;
  delayMicroseconds(1);
  set_EN2_high;
  delayMicroseconds(1);  //pulse the second nibble--discard it;
  set_EN2_low;
} while (busy);
}
set_data_pins_to_write; // data pins to write
set_RW_low;              //RW to write
set_RS_high;
}

Again, this is complicated and you can expect to make some mistakes in the define statements at first. The payoff is about 37%-40% faster operation than with the 4 data pin option. It does cost you one Arduino pin for RW. I have not created 8 data pin versions of these. I am in favor of deprecating 8 pin mode.

Thanks
Certainly my efforts would not have been possible without the help and prior efforts of David Mellis, Limor Friede, and Donald Weiman. Don was particularly patient in guiding me through the idiosyncracies of the HD44780 based LCDs and especially in supplying an example of how the busy flag could be tested.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Mar 08, 2010, 12:57 am
Appendix: A more common pin configuration for the busy test
The more common pin usage for interfacing an LCD is probably that given at
http://www.arduino.cc/en/Tutorial/LiquidCrystal
to use the busy test, you will have to connect RW, in this example, to pin 10. To achieve enough board independence for me to test this, download digitalWriteFast (see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811/0); it will take care of the pin to port issues without ALL of the speed disadvantages of digitalWrite, etc. I can test this code on my mega, whereas I can't test the equivalent port version.
Use this code, then:
Code: [Select]
#include <digitalWriteFast.h>
pinModeFast(10, OUTPUT);
digitalWriteFast(10,LOW);
LiquidCrystal lcd(12,11, 5,4,3,2,&checkBusyFlag); //pins from tutorial

#define set_data_pins_to_read pinModeFast(5,INPUT),pinModeFast(4,INPUT),pinModeFast(3,INPUT),pinModeFast                           (2,INPUT);   //data pins 5,4,3,2
#define set_data_pins_to_write
pinModeFast(5,OUTPUT), pinModeFast(4,OUTPUT), pinModeFast(3,OUTPUT), pinModeFast(2,OUTPUT); // digitalWritefast etc gives some board independence
#define set_EN_high digitalWriteFast(11,HIGH);    //port B3 pin 11
#define set_EN_low digitalWriteFast(11,LOW);
#define set_EN2_high digitalWriteFast(11,HIGH);    //port B3 pin 11
#define set_EN2_low digitalWriteFast(11,LOW);
#define set_RW_high digitalWriteFast(10,HIGH);
#define set_RW_low digitalWriteFast(10,LOW);    //port B bit 2 pin10
#define set_RS_high digitalWriteFast(12,HIGH);    //port B bit 4  pin 12
#define set_RS_low digitalWriteFast(12,LOW);
#define read_busy digitalReadFast(2);        //portD2, pin 2

//You DON'T have to modify the subroutine itself:
void checkBusyFlag(int8_t chip) {
uint8_t busy; // = 0x04;
set_data_pins_to_read;
set_RW_high;             //RW to read
set_RS_low;
if (chip == 0) {  //the if and else can be eliminated if only one hd44780 chip eg 20x4
do {
  set_EN_high;
  delayMicroseconds(1);
  busy = read_busy;    // read busy flag
  set_EN_low;
  delayMicroseconds(1);
  set_EN_high;
  delayMicroseconds(1);  //pulse the second nibble--discard it;
  set_EN_low;
}while (busy);
} else {
  do {
  set_EN2_high;
  delayMicroseconds(1);
  busy = read_busy;    // read busy flag
  set_EN2_low;
  delayMicroseconds(1);
  set_EN2_high;
  delayMicroseconds(1);  //pulse the second nibble--discard it;
  set_EN2_low;
} while (busy);
}
set_data_pins_to_write; // data pins to write
set_RW_low;              //RW to write
set_RS_high;
}


Finally here is my idea of the equivalent defines for the Mega8 or 168/328 based Arduinos using ports; I expect there are errors here:
Code: [Select]
//LiquidCrystal lcd(12,11, 5,4,3,2,&checkBusyFlag); The is the example configuration from the Arduino tutorial for LiquidCrystal BUT hook RW to pin 10
#define set_data_pins_to_read DDRD&=~0b00111100;   //data pins 5,4,3,2 on Arduino 168,328
#define set_data_pins_to_write DDRD|=0b00111100;    // D5,D4,D3,D2
#define set_EN_high PORTB |= 0b00001000;    //port B3 pin 11
#define set_EN_low PORTB &= ~(1<<3);
#define set_EN2_high PORTB |= (1<<3);    //port B3 pin 11
#define set_EN2_low PORTB &= ~(1<<3);
#define set_RW_high PORTB |= (1<<2);
#define set_RW_low PORTB &= ~(1<<2);    //port B bit 2 pin10
#define set_RS_high PORTB |= (1<<4);    //port B bit 4  pin 12
#define set_RS_low PORTB &= ~(1<<4);
#define read_busy PIND & (1<<2);        //portD2, pin 2
Title: Re: Enhanced LiquidCrystal
Post by: gary_white on Mar 17, 2010, 09:57 pm
Okay, I'm confused.  I downloaded the library zip, but I think to the wrong folder.  When I open it, I have two of everything.   I saved it in doc>arduino 18>libraries>liquidcrystal>examples.
What would be the correct folder to put it in and why do I have two two of of everything everything?? Also what do I need to "include" in the program i.e. LiquidCrystal440.h ?
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Apr 03, 2010, 01:53 pm
At this point there are 2 possible downloads: if you downloaded LiquidCrystal440.zip, make a new folder for it at doc>arduino 18>libraries>LiquidCrystal440. Then include LiquidCrystal440.h.

LiquidCrystal.zip is just an alias for LiquidCrystal440.zip that I created because of typos earlier in this thread.


LiquidCrystalBusy.zip goes in the doc>arduino 18>libraries>LiquidCrystal folder and replaces the standard library routines there. include LiquidCrystal.h

All versions are called in your code with the name
LiquidCrystal lcd(....

sorry for the confusion
Title: Re: Enhanced LiquidCrystal
Post by: bingo2000 on Apr 11, 2010, 02:28 pm
Just wanted to thank jrraines for his libary!!
It works fine on a 27x4 LCD (4Bit mode).

How about putting it to the Playground??? I've started to make modifications by myself before I found your lib.
If you don't know how to do that, I can do it for you ;)
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Apr 11, 2010, 02:53 pm
I don't know how to put it on the playground. feel free.. I'm glad to hear that it works on a 27x4; I didn't even know there was such a size but I was pretty sure it worked for all the 2 chip/4 line designs; their addressing will always be 0x0,0x40,0x0,0x40, I think.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Apr 11, 2010, 02:56 pm
My opinion, as of today would be that what would go on the playground would be the older version without the user busy flag stuff. Paul Stoffregen pointed out that just having that complex option available may scare off some users and similar thoughts on my part lead me to keep the older version available for download.
Title: Re: Enhanced LiquidCrystal
Post by: bingo2000 on Apr 11, 2010, 04:27 pm
Done!
http://www.arduino.cc/playground/Code/LCD <- second row
http://www.arduino.cc/playground/LCD/EnhancedLiquidCrystal

Anybody feel free to add more text!
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Apr 11, 2010, 05:49 pm
bingo:

You've got a letter missing on the playground page.
Quote
# EnhancedLiquidCrystal for lager LCDs with two Enable (Enable1, Enable2) LiquidCrystal440.zip


Don
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Apr 18, 2010, 02:51 pm
http://code.google.com/p/liquidcrystal440/downloads/list is an alternate place to get the downloads
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Apr 27, 2010, 12:24 pm
I found an LCD that needed longer delays than were in the previous versions of the code. Several of the delays are a lot longer. see:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1271515975/0

I have a new version of the code that works with this LCD. It is, of course, slower than the previous version. It is so much slower that it made sense to reinstate the busy flag testing when the rw pin number is specified.

http://www.healthriskappraisal.org/LiquidCrystalBulletproof.zip
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Apr 27, 2010, 12:31 pm
Modifications to LiquidCrystal for the Arduino with callback busy test

I made several modifications to the LiquidCrystal library module from Arduino17:

40x4 LCDs
I added support for an LCD of 4 LInes and 40 characters. It worked with a 27x4 LCD. The 40x4 LCD (and any HD44780 based LCD with between 81 and 160 characters) will have 2 enable lines. To use an LCD with 4 lines and 40 columns you would declare your LiquidCrystal object as:
LiquidCrystal lcd(RS,RW,Enable1,Enable2,  data3,data2,data1,data0); at this time I don't support 8 data lines. (You can pass 255 as the RW item, ground RW and save an Arduino pin.)
Then in the setup function you would call:
lcd.begin(40,4);

Linewrap
When you declare the dimensions of the LCD in your begin call, the LiquidCrystal library remembers how long the lines are. Now when it reaches the end of line 1, text wraps onto line 2 (not line 3 as previously).

println
Although print has worked properly in the past, println has not. Now the '\r' and '\n' characters are not sent to the screen as though they were visible characters and the '\r' resets the character position to the top of the next line.

16x4 LCDs
The begin statement also correctly positions text at the beginning of the line on 16x4 (and 40x4) LCDs, which were not correctly handled before.

setCursor
In the past setCursor selected a location in the HD44780's RAM not actually a screen location. If you use any of the commands that shift the display left or right with the previous routines, then setCursor and print, text appears in an unexpected location on the screen. With the new software,  if you call either scrollDisplayLeft() or scrollDisplayRight(), the LiquidCrystal package keeps track of the relationship between RAM and the LCD so that setCursor coordinates are pegged to a specific spot on the screen, rather than a spot in RAM. The sotware does not handle autoScroll, however. Call home() after autoScroll to restore the expected relationship between setCursor and the LCD screen.

Testing the LCD Busy Flag
Previous versions of LiquidCrystal always used timed delays on the Arduino side of the interface to give the LCD module enough time to complete its operation. This version still does that if you tie the RW pin to ground and do not tell LiquidCrystal what that pin number is or pass it the address of a user routine to test the busy flag. If you do specify RW now, however, the software will poll the busy flag on the LCD module. Arduino operations may thus overlap LCD operations and potentially things may go a little faster.
Syntactic Sugar
#include <Streaming.h> from http://arduiniana.org/2009/04/new-streaming-library/
Then you can combine that with an overloading of the () operator in this code. This lets you  specify screen location and chain print commands together by writing: lcd(column,line)<<"a="<<a;
Streaming.h is so efficient you may actually save a few bytes of memory!
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 than LCD work. 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 1491 milliseconds  |  nonAxman 1491
Axman 4 data pins  + RW  774 milliseconds  |  nonAxman  679
Axman 8 data pins no RW 1407 milliseconds  |  nonAxman 1407
Axman 8 data pins  + RW  633 milliseconds  |  nonAxman  620
Axman 4 pins + user busy 510 milliseconds  |  nonAxman  441

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 1289 milliseconds  |  nonAxman 1289
Axman 4 data pins  + RW  369 milliseconds  |  nonAxman  331
Axman 8 data pins no RW 1251 milliseconds  |  nonAxman 1251
Axman 8 data pins  + RW  423 milliseconds  |  nonAxman  394
Axman 4 pins + user busy 361 milliseconds  |  nonAxman  252


Crazy 8 Addressing
16x1 LCDs often have an unusual address layout; these modules often have two 8 character halves and work best with this software if you declare them as lcd.begin(8,2); if you do that, then you can print("abcdefghilklmno"); and have all the characters appear as you would like across the screen. If you use any of the scrolling commands, the bizarre addressing of these modules will manifest itself. For details follow the _LCD Addressing_ link at web.alfredstate.edu/weimandn

User callback busy test
Get LiquidCrystal running without this first; setting up this complicated option is error-prone and this should be left for last, if implemented at all.
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 13, 2010, 03:22 am
Just a quick idea... would rwBusy() be faster if you unrolled the two loops for the pinMode.  For example, instead of this:

Code: [Select]

   for (uint8_t i = 0; i < _data_pinNumber; i++) pinMode(_data_pins[i],INPUT);


maybe something like this could be faster?

Code: [Select]

   pinMode(_data_pins[0],INPUT);
   pinMode(_data_pins[1],INPUT);
   pinMode(_data_pins[2],INPUT);
   pinMode(_data_pins[3],INPUT);
   if (_data_pinNumber > 4) {
       pinMode(_data_pins[4],INPUT);
       pinMode(_data_pins[5],INPUT);
       pinMode(_data_pins[6],INPUT);
       pinMode(_data_pins[7],INPUT);
   }


If the _data_pins were static, the compiler would likely just insert direct memory addresses.  But maybe it'll be smart and use the Y pointer with displacement instruction?  Maybe not?
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 13, 2010, 03:41 am
Or if the compiler doesn't generate efficient code for that, this might coax it into using the single LD with post inc instruction for the first arg, and a single instruction for the second, so each line should compile to only 2 instructions plus the call.

Code: [Select]

   const uint8_t *pinptr = _data_pins;
   pinMode(*pinptr++,INPUT);
   pinMode(*pinptr++,INPUT);
   pinMode(*pinptr++,INPUT);
   pinMode(*pinptr++,INPUT);
   if (_data_pinNumber > 4) {
       pinMode(*pinptr++,INPUT);
       pinMode(*pinptr++,INPUT);
       pinMode(*pinptr++,INPUT);
       pinMode(*pinptr++,INPUT);
   }


It really should allocate "pinptr" to Y (r28 & r29).  If it doesn't, there an asm syntax that can be added to force allocating to Y, which isn't clobbered by the call to pinMode.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 13, 2010, 12:27 pm
I will play with those ideas. I think the problem will be that the library might get any pin number passed to it. It would be great if the optimizer looked across the call from user code to library code and saw what the passed pin numbers were, but I have assumed that was way too much to expect of the optimizer. It would be GREAT if my assumption has been wrong!

You are right that there is something different going on in the case of the Teensy++ in the rw busy test and the issue is almost certainly the time consumed doing 8 pinModes on the way in and 8 pinModes on the way out.
This shows up as surprising relative speeds in the 4 bit and 8 bit versions with rw on the Teensy++.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 14, 2010, 01:35 am
both of these resulted in the same time results for the non-Axman LCD on the Teensy++2.0, about 10% faster by unrolling that loop:

316 4 bit
368 8 bit

of course, pinptr can't be const and incremented.

the disassembly for setting the pinmodes back to OUTPUT at the end of rw_busylooks like this for the pinptr version
Code: [Select]
    3e8:      89 89             ldd      r24, Y+17      ; 0x11
    3ea:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
pinptr = _data_pins;
   pinMode(*pinptr++,OUTPUT);
   pinMode(*pinptr++,OUTPUT);
   pinMode(*pinptr++,OUTPUT);
   pinMode(*pinptr++,OUTPUT);
   if (_data_pinNumber > 4) {
    3ee:      8a 89             ldd      r24, Y+18      ; 0x12
    3f0:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
    3f4:      8b 89             ldd      r24, Y+19      ; 0x13
    3f6:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
    3fa:      8c 89             ldd      r24, Y+20      ; 0x14
    3fc:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
    400:      89 8d             ldd      r24, Y+25      ; 0x19
    402:      85 30             cpi      r24, 0x05      ; 5
    404:      a0 f0             brcs      .+40           ; 0x42e <_ZN13LiquidCrystal6rwBusyEv+0xda>
       pinMode(*pinptr++,OUTPUT);
    406:      87 01             movw      r16, r14
    408:      0f 5f             subi      r16, 0xFF      ; 255
    40a:      1f 4f             sbci      r17, 0xFF      ; 255
    40c:      8d 89             ldd      r24, Y+21      ; 0x15
    40e:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
       pinMode(*pinptr++,OUTPUT);
    412:      f7 01             movw      r30, r14
    414:      81 81             ldd      r24, Z+1      ; 0x01
    416:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
    41a:      f8 01             movw      r30, r16
    41c:      81 81             ldd      r24, Z+1      ; 0x01
    41e:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
    422:      0f 5f             subi      r16, 0xFF      ; 255
    424:      1f 4f             sbci      r17, 0xFF      ; 255
    426:      f8 01             movw      r30, r16
    428:      81 81             ldd      r24, Z+1      ; 0x01
    42a:      0e 94 73 0c       call      0x18e6      ; 0x18e6 <_pinMode_output>
                             #else
                             "call _digitalWrite_LOW"
                             #endif
                             : "+z" (tmp)
                             : "I" (CORE_NUM_TOTAL_PINS)
                       );
    42e:      8f 81             ldd      r24, Y+7      ; 0x07
    430:      e8 2f             mov      r30, r24
    432:      0e 94 b6 0c       call      0x196c      ; 0x196c <_digitalWrite_LOW>
                             #else
                             "call _digitalWrite_HIGH"
                             #endif
                             : "+z" (tmp)
                             : "I" (CORE_NUM_TOTAL_PINS)
                       );
    436:      8e 81             ldd      r24, Y+6      ; 0x06
    438:      e8 2f             mov      r30, r24
    43a:      0e 94 af 0c       call      0x195e      ; 0x195e <_digitalWrite_HIGH>
       pinMode(*pinptr++,OUTPUT);
       pinMode(*pinptr++,OUTPUT);
   }  
  digitalWrite(_rw_pin,LOW);   //set RW back to 'Write'
  digitalWrite(_rs_pin,HIGH);
}
    43e:      df 91             pop      r29
    440:      cf 91             pop      r28
    442:      1f 91             pop      r17
    444:      0f 91             pop      r16
    446:      ff 90             pop      r15
    448:      ef 90             pop      r14
    44a:      08 95             ret

0000044c <_ZN13LiquidCrystal16checkLcdBusyFlagEv>:
digitalWrite(en, HIGH);   // enable pulse must be >450ns
// delayMicroseconds(1);    // but this is unnecessary see:
digitalWrite(en, LOW);    //http://billgrundmann.wordpress.com/2009/03/03/to-use-or-not-use-writedigital/
//  delayMicroseconds(DELAYPERCHAR);   // commands need > 37us to settle       not needed if using checkLcdBusyFlag
}

Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 22, 2010, 12:58 pm
I finally got back to this. I tried unrolling the loops in write4bits and write8bits using the same idea, too.
Code: [Select]
void LiquidCrystal::write4bits(uint8_t value, uint8_t mode) {

digitalWrite(_rs_pin,mode);
// for (uint8_t i = 0; i < 4; i++) {   digitalWrite(_data_pins[i], (value >> i) & 0x01); }
 register uint8_t v=value;
 uint8_t *pinptr = _data_pins;
 digitalWrite(*pinptr++, v & 01 );
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
pulseEnable();
}


on the teensy++2.0 with the nonAxman LCD :

4bits       290
8bits       314
userbusy 192
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 22, 2010, 03:32 pm
digitalWrite takes 0 for low and any non-zero value for high, so instead of "( v >>= 1) & 01", you could use "v & 2", "v & 4" and "v & 8".
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 22, 2010, 06:00 pm
I tried that with disappointing results: it is about 60 bytes longer (that seems odd; it should only need 7 more constants for the 8 bit version and should be able to save a rotate each time), and not much faster.

4bit         288
8bit         311
userbusy 192

I wonder if the userbusy version has actually maxed out speed for sending 3200 characters.
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 22, 2010, 08:37 pm
I believe might be more opportunities for speed improvement.

I downloaded the "LiquidCrystalBulletproof.zip", which appears to be the code before unrolling those loops.  Any chance you could upload the latest?  Could you also upload the benchmark program, or maybe even add it into the examples directory?

Here's a few more ideas...

Why are 3 digitalWrite calls necessary in pulseEnable?  Would it make more sense to write en LOW inside init, and then require only 2 calls to digitalWrite?  Especially on Arduino where digitalWrite is so slow, this could really help.

Perhaps writing to the rs_pin could be moved out of write4bits and write8bits, one level up in call hierarchy?  In 4 bit mode, that will avoid needlessly writing it twice, plus the extra overhead of passing it into another function, twice.  It looks like send and init are the only places that call write4bits, and multiple calls all pass the same value.  Who knows, this might even reduce code size?

Maybe pulseEnable could be folded into write4bits?  However, I'd expect the improvement may be minor, as the compiler is already doing optimizations there.

It might even make sense to create 2 send functions, like send_data() and send_cmd().  Every call to send() passes either HIGH or LOW constants.  Together with the idea of moving the rs_pin write up into send(), it would eliminate lots of parameter passing that is only constants.

In the the 1st line of write(), is _setCursFlag more likely to be zero?  If so, testing it first would be a win.  Then again, I'm not easily seeing how _scroll_count changes, but if either of these is usually zero, you want to test the one more likely to be zero first, since the compiler skips the 2nd test if the first indicates the condition won't call setCursor.

Also perhaps worth trying might be calling setCursor() in the only 2 places where _setCursFlag is set to 1.  Then again, if the user is writing strings that they know fit, which seems likely, this could be slower?

Ok, that's all the ideas I have right now, but I'll probably think of more.....


Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 22, 2010, 08:55 pm
If the userFunc check were dropped, inlining checkLcdBusyFlag or just folding that code into send() might speed things up too?  It writes to rs_pin, so it'd go before the digitialWrite to _rs_pin at the beginning of send... fitting together with the idea of moving the rs_pin wiring out of write4bits and eliminating passing it as a constant all the way through the call hierarchy.  That would get rid of a lot of redundant writes to rs_pin as well, since there be only 1 in each copy of send (send_data, send_cmd) right after the busy check/wait.  If the checkLcdBusyFlag code is folded into send, the case where _rw_pin is used, in send_cmd it won't be necessary to re-write rs_pin because the busy check leaves it low.  Unfortunately, the speed critical data case will still need it written to high after the busy check, but this should still be a net reduction in digitalWrite calls, plus savings in function calls and parameter passing.

Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 22, 2010, 10:59 pm
The benchmark is in the middle of my LCDtest program. The numbers I post are always for a 20x4 LCD.
Code: [Select]
   lcd.home();
   int length = nRows * nColumns;
   lcd.setCursor(0,0);
   char text[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
   text[length] = '\0';
     
   lcd.print(text);  //here we are printing the entire screen with one long string  lets you be sure that linewrap happens exactly as it should
   delay(2000);
  uint8_t a =0;
 if ((nColumns == 8) && (nRows == 2) ) a=1;  //This is a trick to make a 'crazy 8' 16x1 LCD work reasonably in a right to left language; I suppose it could be added to the library routine
                                             // as a conditional but  that seems like too much support. what happens is that we write the 2nd line first; when that fills with (8) characters
                                             // it wraps onto the first line. This means that lcd.print("abcdefghijklmno"); will print from right to left as you desire.
   lcd.clear();
   lcd.rightToLeft();
   lcd.setCursor(nColumns-1,a); //now we print it in right to left mode to test the same thing
   lcd.print (text);
   delay(3000);
   lcd.leftToRight();
//BENCHMARK actually starts here but uses the string set up just above:    
   long startTime=millis();    //let's try to benchmark how fast we can go; this will give us an idea about speed of the various interfaces 4/8 bit and checking busy flag or not:
   uint8_t repetitions = 20;
   char blanks[]="                                                                                                                                                                                                                                                                                                                                                 ";
   blanks[length] = '\0';
   while (repetitions--) {  //fill every screen pixel with text, then fill every pixel with blanks and repeat.
     lcd.setCursor(0,0);
     lcd.print(text);
     lcd.setCursor(0,0);
     lcd.print(blanks);
   }
   long endTime = millis();
   lcd.clear();
   lcd.setCursor(0,0);
   lcd.print("Benchmark took ");
   lcd.setCursor(0,1);
   lcd.print(endTime - startTime);
   lcd.print(" millisecs.");
   delay(5000);
   
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 22, 2010, 11:07 pm
scroll count is used to peg the columns,line values to the screen rather than the LCD's RAM. it is modified in scroll left and right and zeroed in clear and home. Since sensible code (as opposed to test code) has little use for scrolling, it should be zero almost always.

the set cursor flag is used to fix line wrap. when I write a character to the screen, I check to see if I'm at the end of a line, so that the next character might need to be preceded by a setCursor to position it at the start of the next line rather than the line1,line3,line2,line4 order that otherwise occurs. That happens in write. An intervening user generated setCursor clears it.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 22, 2010, 11:18 pm
its been a while, but I THINK the reason I seem to do a redundant digitalWrite(en,LOW) in pulse enable has to do with the fact that 40x4 and 27x4 LCDs have 2 HD44780 chips and 2 enable lines.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 23, 2010, 01:07 pm
I actually thought I'd posted this yesterday. Puzzling over why using &02,&04 didn't gain much I created a version with (v>>=1) in write4bits and the &'s in write8bits and looked at the disassembly
Code: [Select]

void LiquidCrystal::write4bits(uint8_t value, uint8_t mode) {
    308:      dc 01             movw      r26, r24
                       "mov __tmp_reg__, %1"  "\n\t"
                       "call _digitalWrite"
                       : "+z" (tmp1)
                       : "r" (tmp2)
                       #endif
                 );
    30a:      16 96             adiw      r26, 0x06      ; 6
    30c:      8c 91             ld      r24, X
    30e:      16 97             sbiw      r26, 0x06      ; 6
    310:      e8 2f             mov      r30, r24
    312:      04 2e             mov      r0, r20
    314:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
digitalWrite(_rs_pin,mode);
// for (uint8_t i = 0; i < 4; i++) {   digitalWrite(_data_pins[i], (value >> i) & 0x01); }
 register uint8_t v=value;
 uint8_t *pinptr = _data_pins;
 digitalWrite(*pinptr++, v & 01 );
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
    318:      96 2f             mov      r25, r22
    31a:      91 70             andi      r25, 0x01      ; 1
    31c:      51 96             adiw      r26, 0x11      ; 17
    31e:      8c 91             ld      r24, X
    320:      51 97             sbiw      r26, 0x11      ; 17
    322:      e8 2f             mov      r30, r24
    324:      09 2e             mov      r0, r25
    326:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    32a:      66 95             lsr      r22
    32c:      96 2f             mov      r25, r22
    32e:      91 70             andi      r25, 0x01      ; 1
    330:      52 96             adiw      r26, 0x12      ; 18
    332:      8c 91             ld      r24, X
    334:      52 97             sbiw      r26, 0x12      ; 18
    336:      e8 2f             mov      r30, r24
    338:      09 2e             mov      r0, r25
    33a:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
    33e:      66 95             lsr      r22
    340:      96 2f             mov      r25, r22
    342:      91 70             andi      r25, 0x01      ; 1
    344:      53 96             adiw      r26, 0x13      ; 19
    346:      8c 91             ld      r24, X
    348:      53 97             sbiw      r26, 0x13      ; 19
    34a:      e8 2f             mov      r30, r24
    34c:      09 2e             mov      r0, r25
    34e:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
    352:      70 e0             ldi      r23, 0x00      ; 0
    354:      75 95             asr      r23
    356:      67 95             ror      r22
    358:      61 70             andi      r22, 0x01      ; 1
digitalWrite(_rs_pin,mode);
// for (uint8_t i = 0; i < 4; i++) {   digitalWrite(_data_pins[i], (value >> i) & 0x01); }
 register uint8_t v=value;
 uint8_t *pinptr = _data_pins;
 digitalWrite(*pinptr++, v & 01 );
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
    35a:      53 96             adiw      r26, 0x13      ; 19
    35c:      11 96             adiw      r26, 0x01      ; 1
    35e:      8c 91             ld      r24, X
    360:      11 97             sbiw      r26, 0x01      ; 1
    362:      53 97             sbiw      r26, 0x13      ; 19
    364:      e8 2f             mov      r30, r24
    366:      06 2e             mov      r0, r22
    368:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
 digitalWrite(*pinptr++,( v >>= 1) & 01 );
pulseEnable();
    36c:      cd 01             movw      r24, r26
}
    36e:      bb cf             rjmp      .-138          ; 0x2e6 <_ZN13LiquidCrystal11pulseEnableEv>


Code: [Select]
void LiquidCrystal::write8bits(uint8_t value, uint8_t mode) {
    490:      ef 92             push      r14
    492:      ff 92             push      r15
    494:      0f 93             push      r16
    496:      1f 93             push      r17
    498:      8c 01             movw      r16, r24
    49a:      f6 2e             mov      r15, r22
    49c:      e4 2e             mov      r14, r20
checkLcdBusyFlag();
    49e:      e4 df             rcall      .-56           ; 0x468 <_ZN13LiquidCrystal16checkLcdBusyFlagEv>
                       "mov __tmp_reg__, %1"  "\n\t"
                       "call _digitalWrite"
                       : "+z" (tmp1)
                       : "r" (tmp2)
                       #endif
                 );
    4a0:      f8 01             movw      r30, r16
    4a2:      86 81             ldd      r24, Z+6      ; 0x06
    4a4:      e8 2f             mov      r30, r24
    4a6:      0e 2c             mov      r0, r14
    4a8:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
 digitalWrite(*pinptr++, value & 01 );
 digitalWrite(*pinptr++,value & 0x2 );
 digitalWrite(*pinptr++,value & 0x4 );
 digitalWrite(*pinptr++,value & 0x8 );
 digitalWrite(*pinptr++,value & 0x10 );
 digitalWrite(*pinptr++,value & 0x20 );
    4ac:      9f 2d             mov      r25, r15
    4ae:      91 70             andi      r25, 0x01      ; 1
    4b0:      f8 01             movw      r30, r16
    4b2:      81 89             ldd      r24, Z+17      ; 0x11
    4b4:      e8 2f             mov      r30, r24
    4b6:      09 2e             mov      r0, r25
    4b8:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    4bc:      9f 2d             mov      r25, r15
    4be:      92 70             andi      r25, 0x02      ; 2
    4c0:      f8 01             movw      r30, r16
    4c2:      82 89             ldd      r24, Z+18      ; 0x12
    4c4:      e8 2f             mov      r30, r24
    4c6:      09 2e             mov      r0, r25
    4c8:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    4cc:      9f 2d             mov      r25, r15
    4ce:      94 70             andi      r25, 0x04      ; 4
    4d0:      f8 01             movw      r30, r16
    4d2:      83 89             ldd      r24, Z+19      ; 0x13
    4d4:      e8 2f             mov      r30, r24
    4d6:      09 2e             mov      r0, r25
    4d8:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    4dc:      9f 2d             mov      r25, r15
    4de:      98 70             andi      r25, 0x08      ; 8
    4e0:      f8 01             movw      r30, r16
    4e2:      84 89             ldd      r24, Z+20      ; 0x14
    4e4:      e8 2f             mov      r30, r24
    4e6:      09 2e             mov      r0, r25
    4e8:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    4ec:      9f 2d             mov      r25, r15
    4ee:      90 71             andi      r25, 0x10      ; 16
    4f0:      f8 01             movw      r30, r16
    4f2:      85 89             ldd      r24, Z+21      ; 0x15
    4f4:      e8 2f             mov      r30, r24
    4f6:      09 2e             mov      r0, r25
    4f8:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    4fc:      9f 2d             mov      r25, r15
    4fe:      90 72             andi      r25, 0x20      ; 32
    500:      f8 01             movw      r30, r16
    502:      86 89             ldd      r24, Z+22      ; 0x16
    504:      e8 2f             mov      r30, r24
    506:      09 2e             mov      r0, r25
    508:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    50c:      9f 2d             mov      r25, r15
    50e:      90 74             andi      r25, 0x40      ; 64
    510:      f8 01             movw      r30, r16
    512:      87 89             ldd      r24, Z+23      ; 0x17
    514:      e8 2f             mov      r30, r24
    516:      09 2e             mov      r0, r25
    518:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
    51c:      f0 e8             ldi      r31, 0x80      ; 128
    51e:      ff 22             and      r15, r31
    520:      09 5e             subi      r16, 0xE9      ; 233
    522:      1f 4f             sbci      r17, 0xFF      ; 255
    524:      f8 01             movw      r30, r16
    526:      81 81             ldd      r24, Z+1      ; 0x01
    528:      07 51             subi      r16, 0x17      ; 23
    52a:      10 40             sbci      r17, 0x00      ; 0
    52c:      e8 2f             mov      r30, r24
    52e:      0f 2c             mov      r0, r15
    530:      0e 94 e6 0c       call      0x19cc      ; 0x19cc <_digitalWrite>
 digitalWrite(*pinptr++,value & 0x40 );
 digitalWrite(*pinptr++,value & 0x80 );
pulseEnable();
    534:      c8 01             movw      r24, r16
    536:      d7 de             rcall      .-594          ; 0x2e6 <_ZN13LiquidCrystal11pulseEnableEv>
}
    538:      1f 91             pop      r17
    53a:      0f 91             pop      r16
    53c:      ff 90             pop      r15
    53e:      ef 90             pop      r14
    540:      08 95             ret

00000542 <_ZN13LiquidCrystal4sendEhh>:
}


I don't really understand avr assembler in detail but I'm pleased to see that the compiler understood that (v >>=1) is a single shift instruction. I'm puzzled at the symmetrical add/subtract in the 4bit version; it seems like it must be pulling something off the stack frame and resetting the stack pointer. And I'm amazed that the compiler is smart/tricky enough to turn the call to pulse enable at the end of write4bits into a jump, letting pulse enable do the return, popping the return address that was pushed when write4bits was called !

That bit of trickiness does not get used in write8bits. I suppose that implies that the size of the routine has gotten beyond what the optimizer looks at. Maybe it would be vice versa if I'd reversed which method was in 4bits and which one was in 8bits.

The 8bit routine seems to suffer from more setup/cleanup than the 4bit routine. the reason for that is not obvious to me.
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 23, 2010, 04:04 pm
Quote
That bit of trickiness does not get used in write8bits. I suppose that implies that the size of the routine has gotten beyond what the optimizer looks at. Maybe it would be vice versa if I'd reversed which method was in 4bits and which one was in 8bits.


Most likely it's the call to pulseEnable that prevents the compiler from applying the same optimizations.  If you try moving it outside write8bits, there's a pretty good chance you'll see similar optimization as write4bits.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 30, 2010, 04:50 am
Paul continued to work on this and considerably speeded things up. It seems like we hit a limit for speed at about 190 on the teensy with userBusy. 190 millisecs / 3200 characters works out to about 58 usec per character. The HD44780 spec sheet indicates about 43 usec per character. I think the arduino gets ready for the next character, switches all the data pins to input, finds the busy flag during the 43 usec.

Then it has to switch the pinModes back, discard the second nibble, put the new character on the data pins and clock that in by pulsing enable. That part doesn't overlap the 43 usec and must add about 15 usec. With this API (as opposed to one that specified the pin numbers in some other fashion) I don't think it can go much faster.

Anyway, in the next 24-48 hrs I will finish the full test sequence on Paul's version and post it. for the non-Axman LCD what i see now is:
Teensy
userBusy    189
4 + RW       219
8 + RW       296
4  no RW   1207
8  no RW   1212

Mega
userBusy    316
4 + RW       468
8 + RW       500
4  no RW   1349
8  no RW   1314

I think what I will do is test and post this, then promptly begin to remove much of the 8-bit specific code; preserve the 8 bit forms of the constructor for compatibility but actually use 4 bits internally. this should save some space and not really hurt functionality for anyone as far as I can see. Paul suggested this and with no real advantage to 8 bit mode in any way, that direction makes sense to me. If anyone sees a downside to that i would be interested to know.
Title: Re: Enhanced LiquidCrystal
Post by: floresta on May 30, 2010, 05:19 am
Quote
preserve the 8 bit forms of the constructor for compatibility but actually use 4 bits internally

I thought about this a while ago but thought it was a bit sneaky.

Don
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 30, 2010, 12:19 pm
When there was a significant performance advantage for 8 bit in some situations it would have been sneaky. Increasingly the situation is that the 8 bit code:
1. is rarely used
2. adds length, wastes resources
3. adds complexity to make reading the code harder.
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on May 30, 2010, 06:00 pm
Yes, I would agree, this version is probably about as fast as it's ever going to get, at least without ugly tricks like hard coded pins or assembly language.

You've already incorporated so much feedback from me and others, and I can understand wanting to do one last round of final testing and wrap everything up.  But if I could talk you into reconsidering the userbusy API again, I just hope there's a simpler way.

Perhaps instead of userbusy, the same goals could be accomplished by making send() public?  Then an example could be crafted which creates a subclass where send() overridden?  That would probably be even faster than userbusy, since the 8 digitalWrite() and enable pulse could use digitalWriteFast, as well as reading the busy bit and switching pin modes.

Regarding not using 8 bit mode, perhaps it could be perceived as "sneaky", but if it's well documented that the 4 extra wires aren't actually used, that seems perfectly fair.  I believe pretty much anybody would appreciate the code being much smaller and simpler.  If they care about speed, I think they'll really appreciate all your hard work that's gone into benchmarking that proves using only 7 pins is actually fastest.

It's really great work you've done here.  A lot of users are going to benefit once this gets merged into an official Arduino release!
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 30, 2010, 07:58 pm
Making send public rather than the user busy callback fcn is an interesting idea.

After all, every pin in the interface gets manipulated to test the busy flag. An example of how that could be overridden with digitalWriteFast and another example with port commands would make it perhaps more approachable than the callback fcn is.

I don't know if anyone else has actually used the user defined busy routine and its kind of ugly, so I haven't been motivated to fiddle with it.
User callback fcns are the basis for the arduino's approach to user supplied interrupt routines, but the way it works here may actually be harder to understand.

I have thought I was done several times already. I think introducing the little benchmark (which after all, is not all that important; all the interfaces go faster than the eye can follow) has kept this fun and interesting much longer than it otherwise might have been. The project has served to teach me quite a lot about C++ and the arduino that I wouldn't have run across otherwise and provides a practical focus when I read about the language that is much more useful than the umpteenth discussion of circles, squares, and triangles inheriting from shape.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 30, 2010, 08:21 pm
I am starting through the test process with each of the different size LCDs and each of the interfaces; I do see one change in the way this version works on the 40x4: with the 40x4 you have always had to specify an argument for RW. If you want to ground the RW pin, rather than omitting the argument, you specify 255. In the past if you specified a userBusy argument and a (non-255) RW argument, it used userBusy.

With the version I'm testing now you will need to specify 255 for RW on the 40x4 (and 27x4) LCDs if you want it to use a userBusy routine. You will also have to digitalWrite LOW to the RW pin before you call lcd.begin(40,4).

We've changed the order of testing for the different options and in every other case, the new order is what makes sense. It is a little faster for the more common and easy to use option.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 31, 2010, 01:10 am
Other than the change described in my last post there are no new features in this version:

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

This version is available at:  
http://www.healthriskappraisal.org/LiquidCrystalFastWith8bit.zip
Title: Re: Enhanced LiquidCrystal
Post by: leppie on May 31, 2010, 09:50 am
Interesting speeds.

I made an i2c backpack for my LCD* this weekend. After some tweaking, and boosting the TWI speed to 300kHz, I can do a single command in 384us.

That translates to 1229 ms for a (80 chars + 80 blanks) * 20 reps.

I could take the TWI speed higher, but then I would have to start adding delays, and separate commands.

Currently, I can send an LCD command with single TWI  transmission. Just 3 bytes (excluding address).


* Used a MCP23016 and 8-bit LCD interface. With no RW, although it is connected, not used.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 31, 2010, 12:48 pm
Does the I2C controller check the busy flag or just use a timed delay or is all of the delay on the arduino side?
Title: Re: Enhanced LiquidCrystal
Post by: leppie on May 31, 2010, 01:53 pm
Quote
Does the I2C controller check the busy flag or just use a timed delay or is all of the delay on the arduino side?


Due to the slow speed of I2C, I dont even need a single delay/busy check except at initialization  :)

From my testing it is stable without using delay up to 300kHz i2c speed, but it works ok @ 100kHz (the default in Arduino) too. Time per command at that speed is just under 700us.

I am going to see if I can get higher rates (eg 1mHz) using delays and multiple commands to to if it is worth it. I dont think using the busy check will be worth it due to the overhead of i2c.

Edit: I'll also do a proper bench timing to make sure my calculations are in fact correct :)  I am bit surprised at the good speed I am getting.
Title: Re: Enhanced LiquidCrystal
Post by: leppie on May 31, 2010, 03:18 pm
Quote
That translates to 1229 ms for a (80 chars + 80 blanks) * 20 reps.

Quote

I'll also do a proper bench timing to make sure my calculations are in fact correct Smiley  I am bit surprised at the good speed I am getting.


Good thing I did!  Your benchmark code posted on the previous page runs in 611 ms!  Half the expected time... Something is probably not right...

Gonna check the code, I am also getting some unexpected output.  :(

Edit: Found the bug! Was running out RAM on the 328p. Shortened strings, running again now.

Edit 2: Now getting 1229ms just as initially expected  :)

I looked at the MCP23016 datasheet, and it only does up to 400kHz. Not sure if it will be worth boosting it more.
Title: Re: Enhanced LiquidCrystal
Post by: leppie on May 31, 2010, 03:48 pm
Quote
Your benchmark code posted on the previous page


It does not seem to cause instability issues like the following does.

Code: [Select]
void loop()
{
 static int line = 0;
 static int col = 0;
 static char c = 'a';
 
 lcd.write(c);
 
 col++;
 
 if (col == 20)
 {
   col = 0;
   line++;
   lcd.setCursor(col, line);
   
   if (line == 4)
   {
     line = 0;
     
     c++;
   }
   
 }
}


Anything above a 300kHz TWI frequency and it crashes after a few minutes.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on May 31, 2010, 05:12 pm
deleting code goes much faster than writing code.

It seems like I already have something working, about 700 bytes less flash memory usage. At least 5 bytes less RAM usage.

If I do sizeof(lcd) it gives me a number (30 for the 4 bit version I'm working on today) that includes the RAM used for instance variables. The compiler gives a size for a compiled sketch's total flash memory usage. Is there an easy way to see the amount of flash memory LiquidCrystal itself uses? I honestly didn't make note yesterday of sketch sizes for the various options, it seems excessive to reinstall yesterday's code just to measure those, but it would be interesting to see a percentage difference in static memory usage.
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jun 02, 2010, 01:34 am
I found that I needed to increase the per character delay a little more to get the Axman LCD to work using timed delays. When the delay was 320 usec I was seeing maybe one error in 100 characters. quite posibly the vesion I posted on 5/30 needed a longer delay, too. I did the Axman LCD last that time and I was tired.

When I get a chance I think I will compare the length of this version of LiquidCrystal with Arduino-17/18. I suspect the length is pretty comparable now even with the bug fixes and additional modes and features.

I did change all of the items that were 'private' to 'protected'. I think that will allow what Paul was suggesting; writing a class that inherits from LiquidCrystal and then providing a new send()  that has the pin numbers hard coded so that PORT type instructions or digitalWriteFast can be used to speed things up.

http://www.healthriskappraisal.org/LiquidCrystal4Bit.zip
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Jun 02, 2010, 01:29 pm
I have a quick quesion... I did PM you but maybe you didn't get it...

With the 40x4 that has dual HD44780 chips, does that mean that you could have 16 custom characters, just 8 in the top half and 8 in the bottom half?

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: pjrc on Jun 02, 2010, 01:39 pm
Very nice.  The code's looking very good.  I'll try to come up with a subclass example on send().

Here's a list of minor little things I noticed looking at the latest code.  Nothing's critical, just little things.


_busyPin seems to always be _data_pins[3].  Eliminating it might reduce code size, and save an extra byte of per-instance RAM.

rwSave save in init() appears to be unused now.

Inside init(), should en2 be checked for 255 instead of 0 to see if it's unused?

Inside init2(), it would be advisable to call delayMicroseconds with only 15000 and do the loop 9 times instead of 3.  Even though delayMicroseconds takes a 16 bit input, it doesn't actually work properly beyond 16383.  In fact, limiting your call to 8191 us might be a good idea, in anticipation of a 32 MHz AVR (eg, the xmega chips).

Can _displayfunction become only a local variable inside begin2(), possibly saving code side and one more byte of per-instance allocated RAM?
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jun 03, 2010, 02:25 am
Mowcius: the hardware would let you do that. My software, however, sends the user defined characters to both hd44780s. My philosophy has been to try to make the software for the 40x4 act like its one device. You'd have to change the API if you wanted to be able to tell the software which lines you were defining the characters for. I suppose you could write your own code outside LiquidCrystal (or subclass it!) to LOAD the user definitions and LiquidCrystal would never know the difference.

Paul: thanks for the tips I will look at those, probably in a few days.

I will spend a little time poking into the Axman timings. something seems fishy.
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Jun 03, 2010, 11:37 am
Quote
Mowcius: the hardware would let you do that. My software, however, sends the user defined characters to both hd44780s. My philosophy has been to try to make the software for the 40x4 act like its one device. You'd have to change the API if you wanted to be able to tell the software which lines you were defining the characters for. I suppose you could write your own code outside LiquidCrystal (or subclass it!) to LOAD the user definitions and LiquidCrystal would never know the difference.


I knew you would have the answer :P
Right. I might look into it. I think it might be useful for a few projects I have in mind.

Thanks,

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jun 05, 2010, 04:33 am
Quote
Inside init(), should en2 be checked for 255 instead of 0 to see if it's unused?


That one looks like an actual bug to me. It would only show up if someone passed pin 0 for en2, but a bug nonetheless.

Thanks for pointing this out!!
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jun 08, 2010, 01:47 am
I updated the zip file with the changes Paul pointed out:
http://www.healthriskappraisal.org/LiquidCrystal4Bit.zip
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Jun 09, 2010, 11:45 pm
I realized a day or two ago that the advice I gave Mowcius is needlessly complex. The 40x4 display can be thought of as two 40x2 displays each with its own hd44780 controller chip and with all the pins in common except the enable line.

Here is some completely untested code to illustrate the concept:
Code: [Select]

#include <LiquidCrystal.h>
LiquidCrystal lcdwhole(rs,rw,enable1,enable2,d0,d1,d2,d3);  //this refers to the whole 40 x4
void setup (void){
lcdwhole.begin(40,4); //initialize the whole LCD

LiquidCrystal lcdBottomHalf(rs,rw,enable2,d0,d1,d2,d3); //define a temporary LCD object that only has 2 lines
lcdBottomHalf.begin(40,2);  //initialize the 2 line object


//now load custom characters into the whole LCD:
     uint8_t bell[8] = {0x4,0xe,0xe,0xe,0x1f,0x0,0x4};
     uint8_t note[8] = {0x2,0x3,0x2,0xe,0x1e,0xc,0x0};
     uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0};
     uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0};
     uint8_t duck[8] = {0x0,0xc,0x1d,0xf,0xf,0x6,0x0};
     uint8_t check[8] = {0x0,0x1,0x3,0x16,0x1c,0x8,0x0};
     uint8_t cross[8] = {0x0,0x1b,0xe,0x4,0xe,0x1b,0x0};
     uint8_t retarrow[8] = { 0x1,0x1,0x5,0x9,0x1f,0x8,0x4};      
     lcdwhole.createChar(0, bell);
     lcdwhole.createChar(1, note);
     lcdwhole.createChar(2, clock);
     lcdwhole.createChar(3, heart);
     lcdwhole.createChar(4, duck);
     lcdwhole.createChar(5, check);
     lcdwhole.createChar(6, cross);
     lcdwhole.createChar(7, retarrow);

//now redefine custom char 0 for the lower 2 lines:
lcdBottomHalf.createChar(0,retarrow);

}  //end setup(); the temporary LCD object expires.

void loop (void){

     i = 0;
     lcdwhole.clear();
     while (i<4) {
           lcdwhole.setCursor(0,i);
           lcdwhole.print("user:");
           for (int j=0; j<7; j++) {
                 lcdwhole.print(j, BYTE);
           }
           
           i++;
}

Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Jun 09, 2010, 11:49 pm
Quote
I realized a day or two ago that the advice I gave Mowcius is needlessly complex. The 40x4 display can be thought of as two 40x2 displays each with its own hd44780 controller chip and with all the pins in common except the enable line.

Yeah. I had not had time to look into it in more detail so you saved me some time :)

I will try that code later if my 40x4 wants to play ball.

Mowcius
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Aug 14, 2010, 09:55 am
Way back in my first post on this thread, under "Disadvantages" I wrote:

"The possibility that someone with a little 16x2 LCD is using the scrollDisplayLeft() or scrollDisplayRight() instructions to move data across the screen, but wants to write the data 40 characters at a time with a print statement. This version really does not let the user write data to the HD44780 DDRAM which is not visible. To accomplish a scrolling display with 40 characters per line, you would now need to write 16 characters, scroll the display, write a little more and so on."

Yesterday in the course of answering a very interesting question what came to me is that if you declare the little 16x2 LCD to the software as being 40 characters wide then you defeat the linewrap feature and can write to all of the DDRAM on the LCD. see:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1281666158/9#9
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Aug 14, 2010, 02:50 pm
Thinking about my last 2 posts on this thread, both about ways to trick the software into doing something it really was not designed to do, reminds me of a famous story which all programmers should read and more users should know as well:

http://ifacethoughts.net/2007/12/15/levine-the-best-teacher-for-programmers/

enjoy.
Title: Re: Enhanced LiquidCrystal
Post by: oryan on Oct 04, 2010, 05:32 pm
Hi,

I'm using Arduino 18. I've successfully written to a 20x4  LCD and am trying to write to a 40x4 Powertip PC4004A. I've used the LiquidCrystal4bit and LiquidCrystal404 libraries (and a simple 'hello world' program) but always end up with the same thing on the screen, that is: all characters on the first and third lines are all blocks, while lines 2 and 4 are blank.

I've read through as many posts as possible but haven't found anything extra to help me.  I'm confident my wiring is sound but wonder if there's a problem with the LCD itself? Has anyone had a similar issue?

Thanks for any input and ideas.
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Oct 04, 2010, 10:19 pm
Quote
all characters on the first and third lines are all blocks, while lines 2 and 4 are blank.
This means that your LCD controller is not being initialized properly.  Since the LiquidCrystal and LiquidCrystal440 libraries are correct this means that one or more of the six (or 7 for a 40x4) connections to your Arduino are bad.  You did connect the LCD R/W pin (pin 5) to GND didn't you?

Don
Title: Re: Enhanced LiquidCrystal
Post by: jrraines on Oct 05, 2010, 01:01 am
when lines 1 and 3 are all blocks you know that the power, ground and contrast pins are OK. the LCD has not initialized, which could be a result of a bad connection/confused specification of pins  on any of the other lines, as Don said.
Title: Re: Enhanced LiquidCrystal
Post by: oryan on Oct 05, 2010, 10:13 am
I'll recheck all connections, thanks.  It's good to be pointed in the right direction and to know where to look.
Title: WH4004A test sketch:40x4 LCD w/ LiquidCrysta440.h
Post by: magorian on Oct 19, 2010, 08:54 pm
Code: [Select]

// WH4004A Test Program: WinStar 40x4 LCD
// using Enhanced LiquidCrystal440.h
// Since the pinouts are different from Forum example 40x4 LCDs
// Available nkcelectronics.  Data sheet WH4004A-YYH-JT.pdf
// To adjust contract, 10K pot between GND and +5V, wiper W to lcd 12
// LCD Func Arduino Desc pins          Dan Magorian 10/19/2010
// 1    DB7  12      Data bus line
// 2    DB6  11      Data bus line
// 3    DB5  10      Data bus line
// 4    DB4   9      Data bus line
// 5    DB3     Data bus line
// 6    DB2     Data bus line
// 7    DB1     Data bus line
// 8    DB0     Data bus line
// 9    E1   4  Chip enable signal, lcd lines 1 & 3
// 10   RW   3      H: Read(MPU?Module) L: Write(MPU?Module)
// 11   RS   2  H: DATA, L: Instruction code
// 12   V0   W  Contrast, gnd = full
// 13   VSS GND Ground for logic
// 14   VDD +5V Supply Voltage for logic
// 15   E2   5      Chip enable signal, lcd lines 2 & 4
// 16      NC
// 17      LED+      Ext +5V Supply for fluor LED+ optional turn fluor on
// 18      LED-      Ext GND Supply for fluor LED- optional turn fluor on
//
// If char blocks show black but nothing prints, adjust contrast pot,
// and check lcd pins 13 and 14: display may not be initialized.  
// If alternate blocks and blank lines show, check E1 or E2.

#include <LiquidCrystal440.h>
// LiquidCrystal lcd(rs,rw,enable1,enable2,d4,d5,d6,d7);
// Note: some Forum examples using LiquidCrystal440.h have wrong DB pins,
// eg DB0-DB3, or in wrong order.  The top 4 work, in this order.

LiquidCrystal lcd(2, 3, 4, 5, 9, 10, 11, 12);
void setup(){
 lcd.begin (40, 4);
 lcd.clear();
   for (int j=18; j<129; j++) {
     lcd.write(j);
 } // print English chars in rom lower 4 bits, upper 4 bits Japanese
}
void loop() {}
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Oct 19, 2010, 09:17 pm
Dan:

Are we supposed to evaluate this code?  Does it work or does it have problems?

Quote
// 9    E1   4  Chip enable signal, lcd lines 1 & 3
 .
 .
 .
// 15   E2   5      Chip enable signal, lcd lines 2 & 4
I have never used a 40x4 device but I don't think that this is the way things work.  The 40x4 is like two stacked 40x2 devices so one of the enables should deal with lines 1 and 2 and the other should deal with lines 3 and 4.

Don
Title: Re: Enhanced LiquidCrystal
Post by: mowcius on Oct 19, 2010, 09:40 pm
Quote
The 40x4 is like two stacked 40x2 devices so one of the enables should deal with lines 1 and 2 and the other should deal with lines 3 and 4.

Most 40x4 HD44780 LCDs...

There is always the exception to the rule so I wouldn't be too sure  ;)
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Oct 19, 2010, 09:55 pm
Quote
There is always the exception to the rule so I wouldn't be too sure
Anything is possible, but in this case not at all likely.  This behavior is not a function of the interconnections between the LCD controller and the LCD PC board pins, it is a function of the internal structure of the HD44780 and the relationship between the two HD44780's on the 40x4 board.  I don't think any LCD module manufacturer is going to add the necessary external circuitry to achieve the interlaced action so I stand by my statement.

Don
Title: Re: Enhanced LiquidCrystal
Post by: floresta on Oct 19, 2010, 10:15 pm
Dan:

Some more thoughts:

If you look at the end of the section labeled '8. Contour Drawing & Block Diagram' in the data sheet you referenced you will see that my interpretation of the Enable pin operation is correct.  It is still not clear to me how to interpret 'E1=High' and 'E2=High' on that diagram since the enable pin does it's work on the transition between high and low.  It will be easier to experiment and find out once you get your device functioning than to try and figure out what is meant by this translation.

Quote
// If alternate blocks and blank lines show, check E1 or E2.
When a two line LCD module is powered up but not properly initialized then the top line has blocks and the bottom line is blank.  So this is an indication that neither of the controllers is properly initialized.  This can be caused by a problem with any of the connections between the LCD module and the Arduino, not just E1 or E2.  The usual culprit is the failure to deal with RW properly.

Don
Title: Re: Enhanced LiquidCrystal
Post by: magorian on Oct 19, 2010, 10:40 pm
You guys are, of course, correct (as well as fast), thanks for catching that comment labelling error of mine.  It came from when I wasn't hitting E2 and saw black blocks on the 1st and 3rd lines, and neglected to correct it after I read the datasheet and saw it wasn't interlaced.  

Other than that, the sketch works and I put it out there b/c not only are the pinouts different, but there was some confusion about which databus pins work with that 440 lib version, and (after trying them all)  these worked in this order.  I would have put it in the example playground instead of this thread but as a newbie I don't seem to have permissions to do that yet.

Dan
Title: Re: Enhanced LiquidCrystal
Post by: magorian on Oct 19, 2010, 11:23 pm
Here's a version with corrected comments (thanks guys!) for other newbies
fishing around (like I was yesterday) for a working example 40x4 lcd sketch.  
Hope it helps someone save some hours.  Dan

Code: [Select]

// WH4004A Test Program: WinStar 40x4 LCD
// using Enhanced LiquidCrystal440.h
// Since the pinouts are different from Forum sample 40x4 LCDs
// Available nkcelectronics.com. Data sheet WH4004A-YYH-JT.pdf
// To adjust contrast, 10K pot between GND and +5V, wiper W to lcd 12
// LCD Func Arduino Desc pins               Dan Magorian 10/19/2010
// 1    DB7  12    Data bus line
// 2    DB6  11    Data bus line
// 3    DB5  10    Data bus line
// 4    DB4   9    Data bus line
// 5    DB3        Data bus line
// 6    DB2        Data bus line
// 7    DB1        Data bus line
// 8    DB0        Data bus line
// 9    E1   4     Chip enable signal, lcd lines 1 & 2
// 10   RW   3     H: Read L: Write
// 11   RS   2     H: DATA, L: Instruction code
// 12   V0   W     Contrast, gnd = full, too dark
// 13   VSS GND    Ground for logic
// 14   VDD +5V    Supply Voltage for logic
// 15   E2   5     Chip enable signal, lcd lines 3 & 4
// 16    NC
// 17    LED+      Ext +5V Supply for fluor LED+ optional turn fluor on
// 18    LED-      Ext GND Supply for fluor LED- optional turn fluor on
//
// If char blocks show black but nothing prints, adjust contrast pot,
// and check lcd pins 13 and 14: display may not be initialized.  
// If alternate blocks and blank lines show, then neither of the controllers
// is properly initialized, check RW, E1, E2, or any of the connections

#include <LiquidCrystal440.h>
// LiquidCrystal lcd(rs,rw,enable1,enable2,d4,d5,d6,d7);
// Note: some Forum examples using LiquidCrystal440.h have wrong DB pins,
// eg DB0-DB3, or in wrong order.  The top 4 work, in this order.

LiquidCrystal lcd(2, 3, 4, 5, 9, 10, 11, 12);
void setup(){
 lcd.begin (40, 4);
 lcd.clear();
   for (int j=18; j<129; j++) {
     lcd.write(j);
 } // print English chars in rom lower 4 bits, upper 4 bits Japanese
}
void loop() {}
Title: Re: Enhanced LiquidCrystal
Post by: oryan on Oct 20, 2010, 01:21 pm
This last piece of code from Dan works for me and my Powertip PC4004A 40x4 LCD -- the pinouts are the same too.  Thanks for putting the code here!
Ollie.