[Solved]Custom Chars not being printed on LCD

Hello... I'm a new comer tot he forum and I need some help to try to understand what is wrong with my codes because for some situations I can print custom chars on the LCD and for some other situations I cannot!

I have two codes that are running... One was made by me and the other was done by someone that tried to help me.

Some says that the "lcd.createChar()" function MUST be used before the "lcd.begin()" command have been issued! Some says that the "lcd.createChar()" function can be anywhere, either before or after the "lcd.begin()" commad.

Then someone told me something about "lcd.write(uint8_t('\0'))". What is this what what is this used for? I only know '\0' as a NULL terminator character and I cannot understand it's usage in the code of this friend of mine!

This is the code of my friend that is working and printing one custom char on the LCD. He uses the "lcd.write(uint8_t ('\0')) function.

My code that is not working is this:

Can anyone explain me what is wrong with the code?

I for one am not inclined to visit a website recommended by someone with an ominous username and/or avatar. Please post your code here.

There are some issues with using location '0' for custom characters. Get them working at the other locations first and then deal with '0'.

If you use the stealth search box at the upper right corner of this page and search for lcd.createChar() the very first hit will answer your question about where the command can or cannot go.

Don

Here is a code snippet from an application I wrote. It worked fine in the order shown.

byte           deg[8] =            //creating a special LCD character for the "degree" symbol
{
  B01110,
  B01010,
  B01110,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};


void setup()
{
  lcd.begin(20, 4);               //set LCD screen dimension
  lcd.createChar(1, deg);

floresta:
I for one am not inclined to visit a website recommended by someone with an ominous username and/or avatar. Please post your code here.

There are some issues with using location '0' for custom characters. Get them working at the other locations first and then deal with '0'.

If you use the stealth search box at the upper right corner of this page and search for lcd.createChar() the very first hit will answer your question about where the command can or cannot go.

Don

Excuse me? :confused: Ominous??? :o ... ahahah. Ok. I'm pretty sure you know about ideone.com site!
Anyway, I searched the term you said and the first link I found only talks about the limit of 8 chars! I says nothing about any possible reasons or solutions to make my chars print on the LCD.

I posted the code at ideone because I think the forum here has no syntax highlight feature and it's not very appealing to read code! It was with the best of my intentions!

If you take a look at those to codes of mine, you'll see that simply by changing the lcd.createChar() function or the lcd.begin() functions of place in the code, it works! But if there issues with the very first internal address memory of the LCD, I couldn't find any answer on that first search result you suggested me!

jrdoner:
Here is a code snippet from an application I wrote. It worked fine in the order shown.

byte           deg[8] =            //creating a special LCD character for the "degree" symbol

{
  B01110,
  B01010,
  B01110,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};

void setup()
{
  lcd.begin(20, 4);              //set LCD screen dimension
  lcd.createChar(1, deg);

I know that code works and it also works to me! But Have you checked my codes? One of them works, the other doesn't! That's what I need to figure out... Why one works and the other doesn't! I'll post the two codes here as suggested above!

NOT working code

#include <time.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

uint8_t dir = 1, col = 0, lin = 0;
byte customChar[8] = {0};
int8_t memOffset = 0;

void CreatePrintCustomChars(){
   for(uint8_t j = 0; j < 8; j++){
      for(uint8_t i = 0; i < 8; i++)
         customChar[i] = rand() % 32;
      lcd.createChar(memOffset++, customChar);
      lcd.write(uint8_t(memOffset));
   }
}

void setup(){
   srand(time(NULL));
   Serial.begin(9600);

   lcd.begin(20, 4);
   lcd.setCursor(lin, col);
}

void loop(){

   for(uint8_t i = 0; i < 80; i++){
      lcd.setCursor(lin, col);
      CreatePrintCustomChars();
      if(col == 19){
         col = 0;
         lin = (lin + 1) % 4;
      }else
         col++;

 if(dir)
   memOffset += 1;
 else
   memOffset -= 1;

      if (memOffset < 0) {
         dir ^= 1;
         memOffset = 1;
      }else if (memOffset > 7) {
         dir ^= 1;
         memOffset = 6;
      }

      delay(100);
  }
}

Working code:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);  // SainSmart LCD Keypad Shield

void setup() {
  lcd.clear();
  lcd.print(F("Moving Bar -> "));
  lcd.write(uint8_t('\1'));
}

void loop(){

  static unsigned char uc;
  if (uc == 0){
    uc = 1;
  }else{
    uc <<= 1;
  }


  static byte customChar[8] = {0};
  for (uint8_t i = 0; i < 8; i++){
    customChar[i] = uc;
  }

  lcd.createChar(1, customChar);

  delay(250);
}

I’m pretty sure you know about ideone.com site!

I must be culturally deprived.

Anyway, I searched the term you said and the first link I found only talks about the limit of 8 chars!

You may be getting different results from Google if you are in a different country. In the US the topic about the 8 character limit comes up second. Look here → http://forum.arduino.cc/index.php?topic=262753.0. Note that I used the ‘link’ button (to the left of the two ‘x’ buttons) to create an active link.

But if there issues with the very first internal address memory of the LCD, I couldn’t find any answer on that first search result you suggested me!

This problem has been around since the release of Arduino 1.0 and I thought it might be fixed by now. There are two easy workarounds that I know of.

(1) Use location 8 instead of 0. They are effectively the same.

(2) Replace … lcd.write(0);
with … lcd.print((char)0);

Don

floresta:
I must be culturally deprived.

Whatever…!

floresta:
You may be getting different results from Google if you are in a different country. In the US the topic about the 8 character limit comes up second. Look here → http://forum.arduino.cc/index.php?topic=262753.0. Note that I used the ‘link’ button (to the left of the two ‘x’ buttons) to create an active link.

I think you told me to use the search bar here in the forum, not Google Search.

floresta:
This problem has been around since the release of Arduino 1.0 and I thought it might be fixed by now. There are two easy workarounds that I know of.

(1) Use location 8 instead of 0. They are effectively the same.

(2) Replace … lcd.write(0);
with … lcd.print((char)0);

Don

But if you think it might be solved, is the workaround needed? I managed to write the code a little bit different and although it was not exactly what I wanted, but it is probably close enough.

This is the code I came up with:

#include <LiquidCrystal.h>
#include <time.h>

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

byte customChar[8];
uint8_t lin = 0, col = 0, memOffset = 0, memPos = 0, LED = 13, count = 0;

byte* createCustomChars(){
   for(uint8_t i = 0; i < 8; i++)
      customChar[i] = rand() % 32;
   return customChar;
}

void storeCustomChars(){
   for(uint8_t i = 0; i < 8; i++)
      lcd.createChar(memOffset++, createCustomChars());
}

void setup(){
   srand(time(NULL));
   pinMode(LED, OUTPUT);
   lcd.begin(20, 4);
   lcd.clear();
   Serial.begin(9600);
   lcd.setCursor(0, 0);
   storeCustomChars();
}

void loop(){
   digitalWrite(LED, HIGH);
   lcd.write(memPos);
   memPos == 7 ? memPos = 0 : memPos++;
   //delay(100);
   if(col == 19){
      col = 0;
      lin = (lin + 1) % 4;
   }else
      col++;
   lcd.setCursor(col, lin);
   if(col == 19 && lin == 3){
      lcd.clear();
      delay(20);
      lcd.setCursor(0, 0);
   }
   if(count++ == 80){
      digitalWrite(LED, LOW);
      //delay(10);
   }
}

My goal was to check how much time the LCD
would take to create all the chars and fill itself with those created chars! So I added a flag, pin 13, to let me know when a complete cycle was finished! Plugged in the scope and it gave me about 70ms…

You are being burned by a hd44780 interface issue, LiquidCrystal documentation issue, or long standing LiquidCrystal library bug depending on your point of view.
There is no “fix” for this to work the way you are wanting/expecting it to work.
(you can work around it)

It is amazing that this issue does not come up more often. I believe it is due to shear luck on the part of the sketch authors that they are not seeing this issue. i.e. they just happen to write their code in a way that does not trigger the issue.
You were unlucky in that your code happens to expect an impossible behavior so it fails.

A hd44780 display has two types of RAM. Display data ram (DDRAM) and Character Gernarator RAM (CGRAM)
IMO, the hd44780 interface is sloppy with how you switch between.
The only way to switch between them is to issue a command that sets the target RAM type AND target ram address.
What this means is that you can’t simply switch RAMs, do some writes, then flip back to the other ram and continue on at the same previous ram location.
This can be an issue when loading custom characters since the LCD display remains in CGRAM write mode until you either explicitly set a DDRAM address, or do something like a clear or home command.

Now consider what happens with the LiquidCrystal library.
When you call createChar(charval, *ptr) the library uses charval to calculate the memory location in CGRAM, then it sends the command to set the display to accept all future writes to write to CGRAM starting at that memory location.
Then it writes 8 bytes to the display which writes the custom character data to CGRAM.
It then returns back to the sketch. This leaves the LCD display in CGRAM mode.
Until you do something to put the display back into DDRAM mode, all writes will continue to go to CGRAM.

The only way to get the display back into DDRAM mode, is to call setCursor(), home(), or clear().
setCursor() explicitly sets the DDRAM address which also sets the display to DDRAM mode, and home() and clear() commands cause the display to revert back to DDRAM mode as well.
But there is no way to get the display back to DDRAM mode at the same DDRAM location where it was.

In my view this is a complete oversight on the LiquidCrystal authors part, and I’d bet that they are completely unaware of the issue.

However, fixing this is not as easy as it would seem.

So here is the dilemma: the only way to put the display back to DDRAM mode and put it back to the exact same DDRAM address, is to explicitly set it to that previous address.
If the library supported reads, it could have read the address before switching to CGRAM mode and then put it back in the createChar() function.
But since reads are not supported, it can’t do this.
The library also is not tracking the DDRAM location either.
As a result, the library has no way of explicitly setting the DDRAM address back to where it was prior to updating CGRAM in createChar().

There is no way to fix it when reads are not supported.

The other alternative would be to have createChar() do a setCursor(0,0), home(), or clear() before returning to force the display back to DDRAM mode.
But this is not returning the display back to where it was.

Personally, I think that issuing a setCursor(0,0) (which is not the same as home()) command before returning would be better than leaving the display in CGRAM mode as the display would be able to display characters even if they were not in the correct position.
(I have a hd44780 library that does this)

setCursor(0,0), is the least intrusive thing that can be done to get the display back into DDRAM mode.
home() and clear() alter more than just the DDRAM location.

Any, to summarize, you can’t do this:

    lcd.createChar(memOffset++, customChar);
      lcd.write(uint8_t(memOffset));

The write() will write the data to CGRAM which is not what is intended/expected.

You will need to modify you code to do some sort of cursor positioning after setting the custom character.
You may want to modify you loop() code to create and write characters in separate routines.
But at a minimum, you must do a setCursor(), home(), or clear() after you create any custom character before you can write any new characters to the display.

— bill

I'm glad Bill showed up to bail me out of the custom character explanation.

I think you told me to use the search bar here in the forum, not Google Search.

I did. Did you look even superficially at the resulting display? It invokes Google but restricts the search to the Arduino site.

Don

bperrybap, I think I understand what your extensive post tries to explain! I already worked with LCD's before in school. But I was using 8051 based uC. We had to set up every single pin and configuration for it to work properly. I have never read the LiquidCrystal.h library and probably I would ever be able to fix the problem, but working woth 8051 based uC in the past, gives my some enlightenment about what the problem could be! HD44780 controller has a lot of timings and configs to attend to so that it works properly. Probably there is a misconfiguration of the HD44780 controller within the library somewhere!

But is it possible that the programmers that created the library are unaware of the problem?

Anyway, I was able to do what I wanted with satisfactory results!

floresta, I opened the fisrt link and read about the 8 custom chars limitations!

PsySc0rpi0n:
HD44780 controller has a lot of timings and configs to attend to so that it works properly. Probably there is a misconfiguration of the HD44780 controller within the library somewhere!

Uh..... No.
You have not understood the issue at all.
The issue has absolutely nothing to do with low level timing or configuration.
The issue is exactly the same regardless of using Arduino, LiquidCrystal library or any other processor or library.
I suggest that you re-read my post and really take your time to fully understand it.

--- bill

bperrybap:
Uh..... No.
You have not understood the issue at all.
The issue has absolutely nothing to do with low level timing or configuration.
The issue is exactly the same regardless of using Arduino, LiquidCrystal library or any other processor or library.
I suggest that you re-read my post and really take your time to fully understand it.

--- bill

I think I understood what you said! From what I understood, the library lacks of code to explicitly switch between both CGRAM and DDRAM modes! It as to be done by issuing one of those 3 commands/functions you said!

And what I said was based on that! Why those 3 functions are able to switch between memory modes and the other functions like create.Char() cannot!

I also mentioned that I worked with this same controller in the past but using an 8051. After the code was properly written, I had no problems with switching between DDRAM and CGRAM mode! That's why I said that it might be some misconfiguration in the functions or LiquidCrystal.h library!

Again, no. I never said that the library can not switch between CGRAM and DDRAM modes. That isn't what I said.
The LiquidCrystal library can write to DDRAM, as characters show up on the display, and can write to CGRAM as it can create custom characters.

The issue you are running into is not related to any particularly library.
It has nothing to do with timing or configuration.
It will be an issue with any library and any processor.

It appears that you still do not understand the issue. It relates to how the hd44780 set DDRAM address and set CGRAM address commands work. and in particular how that works when a library does not know the DDRAM address or have the ability to read it.

So again, I suggest that you re-read the post as it goes into the details as to what is happening and why it is an issue for any library running on any processor.

--- bill

Ok... I understood the problem... It has to do with the way HD44780 deals with writing into DDRAM and CGRAM. And if you say it has nothing to do with the software, I believe it!

Anyway, my last code is doing what I need close enough. I measured the time of one loop() cycle and I got about 70ms. I know that probably it could be less if the code was optimised over and over again, but it's good enough to me!

PsySc0rpi0n:
Ok... I understood the problem... It has to do with the way HD44780 deals with writing into DDRAM and CGRAM. And if you say it has nothing to do with the software, I believe it!

I didn't say it had nothing to do with the s/w.
I said it wasn't caused by a low level timing or configuration issue in the LiquidCrystal library and that the same potential issue of restoring the DDRAM address after updating CGRAM with a new custom character would exist regardless of the processor or library used.

It sounds like you have come up with a work around solution to your issue so I think I'll drop out of the thread.

--- bill

bperrybap:
I didn't say it had nothing to do with the s/w.
I said it wasn't caused by a low level timing or configuration issue in the LiquidCrystal library and that the same potential issue of restoring the DDRAM address after updating CGRAM with a new custom character would exist regardless of the processor or library used.

It sounds like you have come up with a work around solution to your issue so I think I'll drop out of the thread.

--- bill

Ok, anyway, you are a little bit difficult to deal with! I didn't say this, I didn't say that, I didn't say that I didn't say this!!!
I also said that I had no problems with dealing with the LCD when I used an 8051 based uC. So I assume that it could have something to do with software or something like that, because if I was able to work with the LCD without any issues using an 8051 based uC, it was reasonable to assume that the LCD was perfectly ok!

Anyway, thanks for the help... The thread can be marked as solved!

PsySc0rpi0n:
Ok, anyway, you are a little bit difficult to deal with! I didn't say this, I didn't say that, I didn't say that I didn't say this!!!

I keep correcting you because you kept saying that I said things that I did not say.

I also said that I had no problems with dealing with the LCD when I used an 8051 based uC. So I assume that it could have something to do with software or something like that, because if I was able to work with the LCD without any issues using an 8051 based uC, it was reasonable to assume that the LCD was perfectly ok!

Sure the LCD is ok.
Your issue is not an LCD problem.
Your issue not an issue that will show up on one processor and not another.

I refer back to the very first thing I said in post #8 just before I fully explained in great detail what was causing the issue you were experiencing and then how to work around it.

You are being burned by a hd44780 interface issue, LiquidCrystal documentation issue, or long standing LiquidCrystal library bug depending on your point of view.
There is no "fix" for this to work the way you are wanting/expecting it to work.
(you can work around it)

The only reason you didn't have the same issue on an 8051 is that you were not doing the same thing.
i.e you were not creating a custom character, and then trying to immediately display that character by doing a write to the display when the display is still in CCGRAM mode.

Had you done that same set of operations on the 8051, you would have seen the exact same results.

It is just that simple.

--- bill

Ok bperrybap... I appreciate the help and the thorough explanation about the issue!