Pages: 1 2 3 [4] 5 6 ... 9   Go Down
Author Topic: Enhanced LiquidCrystal  (Read 12921 times)
0 Members and 1 Guest are viewing this topic.
North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

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

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.
Logged

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

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.
Logged

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

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.
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
     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:
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:


Mowcius
Logged

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

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 ==.
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
You need to test >= 40 rather than ==.
Code:
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
Logged

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

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);
      }
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It seems that the server is down again...  is this library available anywhere else?

http://healthriskappraisal.org/LiquidCrystal440.zip

Thanks.
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley-sad[/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
Logged

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

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.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yeah I am always wise to look through user settings on every forum to check for that kinda stuff...

Mowcius
Logged

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

I 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:
//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.
Logged

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