Go Down

Topic: lcd.createChar before and after lcd.begin (Read 5441 times) previous topic - next topic

abdallah_bukhari

i noticed that, in the setup bloc, when you write
Code: [Select]
lcd.begin(16,2);
BEFORE the
Code: [Select]
lcd.createChar command it actually dosent display anything and the lcd remains blank ( but it does compile).
but when you put the lcd.begin AFTER the lcd.createChar it works just fine , so why?

johnwasser

I'm surprised that .createChar() works before .begin() sets the interface to 4-bit mode.  The .send() function uses the data set up by the constructor to know if the interface is supposed to be 8-bit or 4-bit (most people use 4-bit to save pins)  but until you actually call .begin() the interface mode isn't explicitly set.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

floresta

Disclaimer:  I have never used lcd.createChar

Without seeing all of your code we really can't tell what is going on but you should understand that creating the character and displaying the character are two different things.

When you create a custom character using lcd.createChar you are putting data into the CGRAM memory of the LCD controller chip. 

You can then display that character by using lcd.print which copies the data from the CGRAM memory to the DDRAM memory of the LCD controller.

If your program does not include an lcd.begin statement, or if the statement has not yet been executed, the lcd will still operate although it will be configured for the default configuration of a 16x1 display.  This means that the custom character data will be correctly stored in CGRAM.

It doesn't matter whether you create the character before or after the lcd.begin it but won't be displayed until after an lcd.print.   However if you expect the lcd.print to work correctly on a 16x2 display you had better do the printing after the lcd.begin statement.


Don

floresta

Quote
I'm surprised that .createChar() works before .begin() sets the interface to 4-bit mode.


That's because it's the constructor LiquidCrystal lcd(. . .) that sets the interface to 4-bit mode, but you knew that already!

Don

bperrybap


Quote
I'm surprised that .createChar() works before .begin() sets the interface to 4-bit mode.


That's because it's the constructor LiquidCrystal lcd(. . .) that sets the interface to 4-bit mode, but you knew that already!

Don

Actually we don't know enough to say either way since we don't know what library or interface abdallah is using.

If you look at the LiquidCrystal library that ships with the IDE it is begin() that sets up the interface
and initializes the LCD. However, (and I just noticed this), the constructor routines call the init() routine
and at the very bottom of init() there is a a call to  begin(16,1);
(never noticed that extra begin() call)
So while the constructor, ends up initializing the LCD, it is the begin() function that is actually doing it.

That said, nearly all various LCD libraries I've seen do the LCD initialization in begin()
(a few still use the init() function)  and some of them do not end up calling begin() from their constructors.

The LCD and interface will not be initialized by the constructor for i2c based interfaces since the Arduino code
does not properly set up all the hardware to allow i2c to work prior to calling the LCD constructors.
Because of this, a i2c LCD library can't call the Wire library code until later which is why it is deferred
until begin() and begin() is not called from the constructor.

The safest best thing to do is to never try to to talk to the LCD (which includes trying to set custom characters)
until after you have initialized the LCD using the appropriate begin() or init() function for the library.

I use custom characters all the time and have used them on every hd44780 library and interface
I've been able to get my hands on.
I have seen one issue that I've not bothered to track down and that is in some cases the LCD will not work properly
after sending custom characters until a home or clearscreen command is issued to the display.

The sequence that I've seen and use  that always seems to work  is
- initialize the LCD with begin()/init() as needed
- load your custom characters
- clear the screen

--- bill



johnwasser


If you look at the LiquidCrystal library that ships with the IDE it is begin() that sets up the interface
and initializes the LCD. However, (and I just noticed this), the constructor routines call the init() routine and at the very bottom of init() there is a a call to  begin(16,1);  (never noticed that extra begin() call).

I had missed that.  Sounds like it should works either way when using the built-in library.  Guess we need to see the sketch and possibly the hardware.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

floresta

I try not to look at the LiquidCrystal library code because it makes me ill every time I see it.

Quote
...and at the very bottom of init() there is a a call to  begin(16,1);
(never noticed that extra begin() call)...


I was aware of that extra begin() which is why I knew that you could 'talk' to the controller before implementing your own.

It is there to be used as a default value in case you don't include an lcd.begin() of your own.  Here's why (my opinion).

There have essentially been two versions of the LiquidCrystal library.  The version that was supplied through Arduino 0016 did not follow the recommended initialization procedure for the LCD and consequently did not work very well with the 8-bit inteface and was even worse with the four-bit interface.

The initialization procedure was (mostly) corrected in Arduino version 0017 and that was where lcd.begin() was introduced.  Since the many sketches that were already in existence did not include an lcd.begin() there was a need for some default values.  

Unfortunately the author (Lady Ada) chose to use the same parameters that the built-in initialization uses and the default is therefore set up for a 16x1 display.  This is unfortunate because there are very few displays that should be set up that way, including most 16x1s which are internally 8x2s.  A default value of 16x2 would function with virtually every display except the few true 16x1s.


Don

floresta

Quote
I use custom characters all the time and have used them on every hd44780 library and interface
I've been able to get my hands on.


Bill: Is the rest of stuff I mentioned in reply #2 essentially correct?

Don

bperrybap


Quote
I use custom characters all the time and have used them on every hd44780 library and interface
I've been able to get my hands on.


Bill: Is the rest of stuff I mentioned in reply #2 essentially correct?

Don

It depends on which library is being used, which we don't know.
The order of begin() vs createChar() can be significant depending on which
library is being used.
If it is the LiquidCrystal library that comes with the IDE then yes the comments
in your reply #2 are correct;
however, if it were an i2c version of the library then begin() must be called
before you can create custom characters since begin() is what initializes
the interface (i.e. Wire/i2c) as well as the LCD and begin() will not  not called by
the constructor.

That is why I'd recommend always calling begin() first, including
before creating custom characters since that sequence order will work on other libraries
and any interface.

If I had to guess, I'd say it is likely that this issue is the same issue that I've seen,
and that is that after sending a custom character using createChar() no characters
will show up on the LCD until a cursor repositioning is done.
I use home() to do this.
I've not taken the time to fully track down what the real issue is.

I'm assuming that  the LCD is stuck in CGRAM address mode until some sort
of command that sets the mode back to DDRAM address mode is done.
The createChar() function sets CGRAM address mode and then sends
the 8 bytes.
Code: [Select]
// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
}

I'm assuming that all futures writes to the LCD are now going to CGRAM and that is why
print() doesn't work .


I would suggest using begin() first, then create your custom char(s),
then do a home() and see if that fixes the problem.
This is the sequence that I use.

setCursor(0,0) instead of home() would also probably work, and would be alot faster.

--- bill

jan_huygh

Using the in the IDE 1.6.13 included LiquidCrystal library I did some testing on the influence of the order of createChar() and begin() in code.

The original comment of Abdalla_bukhari is still valid. You need to put createChar() before begin().

This is in contradiction with the documentation that states "begin() needs to be called before any other LCD library commands." here

I also tested the proposed solutions to insert clear(),  home() or setCursor(). They all work as indicated in the comments in the code here below.

Code: [Select]
#include <LiquidCrystal.h>
LiquidCrystal lcd_1(7 ,6 ,5 ,4 ,3 ,2);

byte byteA_MyLines[8] = {0,127,0,127,0,127,0,127};

void setup() {
  lcd_1.begin(20, 4);  
  lcd_1.createChar(0, byteA_MyLines);
  //lcd_1.clear();        //Uncomment this and it works
  //lcd_1.home();         //Uncomment this and it works
  //lcd_1.setCursor(0,2); //Uncomment this and it works
  lcd_1.write(byte(0));
}

void loop() {}


In the hope this post is usefull for others searching this forum.

Jan Huygh

bperrybap

There is no need to test the order of begin() vs other LiquidCrystal API functions.
begin() must be first and the the documentation stating that begin() must be called first is correct.
The issue being observed with createChar() could be considered a bug or an issue of documentation for the createChar() function depending on your point of view. See details below.

Calling createChar() *before* begin() is incorrect and does not solve the issue the OP was experiencing, since it is trying to work around a potential issue involving createChar() in a way that is not assured to work.
Calling createChar() before begin() will always fail to work correctly when things start from a coldstart power up when using 4 bit mode.
Yes, you may see a custom character written to the display, but it won't be the proper bitmap glyph definition since at powerup the display is not yet in 4 bit mode but the arduino is only presenting the custom character data nibble at time.

Don't be mislead just because you saw something show up on the display when begin() is done after createChar(). And keep in mind that for this type of testing you MUST test it from a cold start power up.
So make sure you check to see what happens immediately after a power up. (It fails)
Take a very close look at the character glyph printed and you will notice that the glyph is wrong because the pixel data was not correctly received by the LCD because the LCD was still in 8 bit mode.
If you run the code after the LCD has been previously initialized to 4 bit mode, then it will appear to work since the display is already in 4 bit mode when createChar() is called.
But if the display is not already in 4 bit mode - which is isn't at powerup, it will fail.
You cannot work around this CGRAM/DDRAM mode issue by simply calling createChar() before begin() as it doesn't really work.

What I sated in post #8 is the real reason. hd44770 displays have two modes CGRAM and DDRAM.
When in CGRAM mode, data writes go to CGRAM to define custom characters. In DDRAM they go to DDRAM which cause them to show up on the LCD display.
This is the way the h/w works and there is no changing this.
The library puts the display into CGRAM mode to send the custom character data to the display CGRAM but then leaves the display in CGRAM mode when createChar() returns.
Because of this, no characters will ever print to the display until you do something to put the display back into DDRAM mode. This is not just when printing custom characters.
You can try it by printing other "normal" characters after createChar() and see the same problem.

With the stock LiquidCrystal library (and all the other hd44780 based LCD libraries I've looked at)
you will see a createChar() function as I pointed out back in post #8.
That function leaves the LCD in CCGRAM mode.
Until you do something that causes the display to flip back to DDRAM mode, like calling clear(), home() or setCursor(), nothing you send through print() will display on the LCD since the character data will be written to CGRAM instead of DDRAM.

Calling begin() again does a full initialization including calling clear() which leaves the display in DDRAM mode when begin() returns.
Since the display will be in DDRAM mode after begin() is called, future calls to print() will work again.

The "issue" is that createChar() leaves the LCD in CGRAM mode.
I have no idea if this was intended or not but it isn't documented and it means that the application must do something to put the display back into DDRAM mode before characters can be printed to the display.

My view on this is that the existing createChar() behavior is not expected and I considered it a bug, so
my hd44780 library hd44780_pinIO class does not work the way all the other LiquidCrystal libraries work.
hd44780 will put the display back into DDRAM mode so that calls to print() immediately after creating a custom character will work.
One thing that might be considered an issue of doing this automatic return to DDRAM mode is that because of the way the hd4480 commands work, the only want to put the display back into DDRAM mode is to set the DDRAM address counter.
So that means the cursor position is potentially altered.
How hd44780 deals with this varies depending on if the r/w pin is used.
If hd44780 can control the r/w pin, the code reads the cursor position before it changes the mode to CGRAM mode and restore it when done. If there is no r/w control, then hd44780 will simply set the cursor position to 0,0

Long story short, I'd recommend using hd44780 over LiquidCrystal, as I believe it works more as people would expect with regards to createChar(), not to mention you pick up some other features like being much faster, backlight control, and automatic protection from broken backlight circuits on certain LCD keypad shields, to name a few.

--- bill

Go Up