Can't figure out how to use special segments at bottom of LCD 8X1 display

Display: http://www.newhavendisplay.com/nhd0108hzfswgbw-p-212.html

I have been using the normal characters on this display with success. This particular display has 4 segments at the bottom, that would point down if they were selected. However, I can't seem to figure out how to use them.

The display datasheet is at: http://www.newhavendisplay.com/specs/NHD-0108HZ-FSW-GBW.pdf If you look at page 5, it describes the address for the segments at 40H-A1 through 40H-A4. I would think that means the address is 0X40. However, I have no idea what the A1...A4 means. It also mentions that the A1..A4 is somehow associated with COM9 - whatever that is.

I also looked at the driver datasheet: http://www.newhavendisplay.com/app_notes/ST7066U.pdf and had no success, except for the mention of COM9 on page 8, where it tells what COM1 - COM16 is. The explanation is beyond my experience.

I contacted NewHaven's "help", and all they seem to be able to tell me is exactly what's on the datasheet.

If I recall, even if I am using an 8X1, I should be able to say I'm using a 16X1 in the IDE, since the 9th character is at 0X40. So I'm thinking I should be able to use the special segments, but have no idea what to write to the 0X40 address.

Any thoughts or experiences are appreciated....

The datasheet is pretty cryptic and the DISPLAY DATA ADDRESS table on page 3 is a poor format.

My guess is that the datasheet is trying to say the icons (A1, A1, A3, A4 - the arrows) turn on when COM9 and the matching segment are turned on.

Memory address 0x40 is where the second line of text would start and the first character in the 2nd line has COM9 to COM16 are rows with COM9 at the top. SEG1 to SEG5 are columns with SEG1 on the left . Look at the diagram on page 41/41 of the ST7066U document.

If this is correct then the upper pixels of the character written to memory location 0x40 control those icons starting left to right with the left most pixel being A1 (arrow 1).

To test this you need to write a character to memory location 0x40. You can do this by using the setCursor() function to set the memory location then use print() to send the character.

lcd.setCursor(0x40, 0); // column 0x40, of row 0 which will be memory location 0x40

(or you could use lcd.setCursor(0,1) but I don't think it is as obvious since the memory address mapping is hidden) Then output a capital T which as all the top row pixels on.

lcd.print('T');

If my guess is correct, that will turn on all the icons.

Once you know how it works, a routine that controls the icons can be written. Given the way it works, I don't think it is possible to come up with existing characters that can create every combination of the 4 pixels needed. So the way to do it will be create use a single custom character say at location 0x8 (same as 0 but avoids some C compiler issues) and then write that character to memory location 0x40. Then when ever you need to change the icons, modify that custom character with the appropriate pixel bits set in each column of the glyph to match the pixels that enable the needed icon bits. All you have to do is create the character bit map data in memory, then call createChar() to modify the custom character. Since the character is already written to memory location 0x40, it will "magically" change when the custom character is changed/updated with the new glyph data. It is a bit of pain, but not too hard, and once you write the routine, it won't be a big deal to use.

--- bill

Bill,

Greatly appreciate your experience. Thanks for pointing out the diagram on the driver datasheet. Your suggestions look like they should work. I tried them, but still no success. I'll fight with it some more, then I'll post the code I'm using.

Thanks again, Mike

OK, in your code, define the display as 8 by 2, then setCursor(0,1) and print character 255.

Paul__B: OK, in your code, define the display as 8 by 2, then setCursor(0,1) and print character 255.

Good point. That setCursor(0,1) will only work if you have configured the lcd for 2 or more rows.

Using: setCursor(0x40,0); will always result in sending the proper SETDDRRAMADDR command to the LCD to set the address to 0x40

The 255 character is great idea as that has all the pixels turned on.

If sending 0xff to address 0x40 fails, then I'd set the cursor position to 0,0 and then create a loop and call lcd.write(0xff); about 200 times and see if the icons turn on. If they do, now it becomes a game of trying to figure out which address(s) and which bits control them.

--- bill

Guys,
You’ve been a tremendous help. I have it working, but I don’t understand why it’s working.

In the code, I can use the normal characters if I configure the display as (8,2), but set the cursor at (0,0). The text displays fine.
To get to those arrows, I set the cursor at (0,2). Should’t it be (0,1)?

Regardless, once the correct address is found, it’s a matter of writing 0b10000 for the first arrow, 0b11000 for the second, etc.

I would call this one solved, though I don’t understand what’s going on with the addressing scheme as noted above.

//This works for NewHaven Display NHD-0108HZ-FSW-GBW
// 8 x 1 with arrows at bottom.

#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

byte one[1]={  //FIRST ARROW
  0b10000
};

byte two[1]={   //SECOND ARROW
  0b11000
};

byte three [1]={  //THIRD ARROW
  0b11100
};

byte four [1]={  //FOURTH ARROW
  0b11110
};

byte off [1]={  //CLEAR ALL ARROWS
  0b00000
};

void setup()
  {
  lcd.createChar(0,one); //testing arrows
  lcd.createChar(1,two);
  lcd.createChar(2,three);
  lcd.createChar(3,four);
  lcd.createChar(4,off);
  
  lcd.begin(8, 2); //This means 8 char, 2 lines
 
  }

void loop()
  {
  //This works on the normal 8 characters.
 lcd.setCursor (0,0); 
 lcd.print ("TESTING!");
    
  //This makes the first arrow come on.  
 lcd.setCursor (0,2);
 lcd.write (byte (0));
 
 delay (500);
 
 //...and the second arrow comes on
 lcd.setCursor (0,2);
 lcd.write (byte (1));
 
 delay (500);
 
 //...and the third
 lcd.setCursor (0,2);
 lcd.write (byte (2));
 
 delay (500);
 
 //...fourth
 lcd.setCursor (0,2);
 lcd.write (byte (3));
 
 delay (500);
 
 // Clear the arrows
 lcd.setCursor (0,2);
 lcd.write (byte (4));
 
 delay (500);
 
}

There is probably a more efficient way to code all that - feel free to point out improvements.
Do you guys understand why the addressing is working the way it does?

Thanks for your efforts!
Mike

This is kind of a "working by accident" situation. From looking your code, it looks like it works as I had guessed. So I'm assuming:

lcd.setCursor(0x40,0);
lcd.write(0xff); // or lcd.print('T');

Turns on all the arrows?

You have a few issues/errors in your code.

You initialized the display to be 8,2 but are attempting to use the 3rd line with setCursor(0,2); The library will ignore what you asked since it is too large and convert back to setCursor(0,1) Here is the actual down in the library setCursor() function:

if ( row >= _numlines ) {
    row = _numlines-1;    // we count rows starting w/0

So to write to address 0x40, you can use

setCursor(0x40, 0);

which should always work (even when initialized to 8,1) or if you lie to the library and tell it you have 2 lines you can use

setCursor(0,1);

Now for the custom characters. Have a read of this page: http://arduino.cc/en/Reference/LiquidCrystalCreateChar

The createChar() function creates a custom glyph. You pass it a pointer to 8 bytes to define the full character. You are passing in a pointer to 1 byte and 7 bytes of "trash". While in this case the trash won't be seen it isn't strictly correct.

If I understand how the arrows work correctly, the issue you will have with your custom characters, is that you can not turn on/off the individual arrows. you have defined characters that allow turning on arrow1 arrow1 and arrow 2 arrow1, arrow2, and arrow3 arrow1, arrow3, arrow3, and arrow4 turn off all arrows.

But you can't turn/off any individual arrow that you want. i.e. try turning on only arrow #3. I'm guessing that custom character three will also turn on arrow #2 and #1.

If you want individual arrow control you could go back to what I mentioned earlier and use a single custom character and then change the top row of the custom character to reflect the bits to turn on the desired arrows. Essentially you write this single custom character to memory location 0x40. Then redefine the single custom character to have the top row pixels indicate which arrows you want turned on and then send it to the LCD whenever you want/need to change which arrows are on.

--- bill

With regards to the addressing, I'll give that a try tomorrow. I can't recall for sure, but it seems that if I addressed (0,1), it wouldn't work - prompting me to try (0,2) out of desperation, and then it worked. Hence my confusion. I'll reply back with "this works, this doesn't" tomorrow.

With regards to turning on each arrow: It's counter intuitive, but 0b11110 turns on only the 4th arrow. My example code demonstrates that poorly.

I can write 0b11110 for the fourth arrow, then write 0b10000 for the first arrow - resulting in the first and fourth only being on. I'll try a few more things tomorrow morning and report back.

Mike

Did more testing - structured this time. I was in a hurry and fooled myself earlier. So here is what I found regarding the addressing:

These are the things that allow access to the arrows:

  lcd.begin(8, 2);
void loop()  
   lcd.setCursor (0,1);
   lcd.write (byte (3));
 ************************* THESE TWO WORK *****
   lcd.begin(8, 2);
void loop()  
   lcd.setCursor (0x40,0);
   lcd.write (byte (3));

These methods do not work:

   lcd.begin(8, 2);
void loop()  
   lcd.setCursor (0x40,1);
   lcd.write (byte (3));
 ************************* THESE DO NOT WORK *****
   lcd.begin(8, 1);
void loop()  
   lcd.setCursor (0x40,1);
   lcd.write (byte (3));
 *********************************************
    lcd.begin(8, 1);
void loop()  
   lcd.setCursor (0x40,0);
   lcd.write (byte (3));

With regards to the arrows themselves, you are correct in that the bits in the string (correct nomenclature?) determine which arrow comes on. 0b10000 is for the first arrow only, and 0b00010 is for the fourth arrow only, and 0b01100 is for the second and third arrow together. Does the call [lcd.write] write out 8 strings even if you don't define them? I understand I haven't fully defined a glyph using just one string - didn't know if it hurts anything, aside from looking like sloppy programming.

I'm pretty new to Arduino. I've done some programming on a few PICs in assembly for a 16x2 display without libraries before. It was painful - this is much easier. Thanks for your help and direction. Greatly appreciated.

Should I post a working documented example of code once I get the code cleaned up?

Thanks for your time, Mike

ih55t: Should I post a working documented example of code once I get the code cleaned up?

Absolutely. That's the sort of thing we want here!

For indexing purposes for the benefit of others, you perhaps should alter the topic of your original posting.

I tracked down why you have to tell the LCD library it has two lines.
The issue is that if you initialize the library with only 1 line it sends
a command to the LCD chip indicating that it is a single line LCD and
that disables the pixel signals for the second line where the arrow icons are.
The arrow icons are essentially 4 GIANT pixels on the top row of pixels starting
on the left of the second line
Therefore, to use the arrow icons you have tell the chip it has more than 1 line.

I took a stab at creating some sample code.
I attached it.
You can ignore/remove my commented out hd44780 lcd headers and lcd constructor
that is used for testing.

I numbered the arrows starting at 0 vs 1 but if that bothers you, you can change the code.
Not sure about your C experience but it if you are new to C
it may use a few things that may be a bit more advanced than you have seen like
a ternary operator (the ? mark) which works like an if statement.

There are two functions, one to set individual arrows, and the other sets
all the arrows at once.

To simplify the coding it only works on single LCD display and requires
that you name your lcd object “lcd”.

if it doesn’t work or you have questions about the code, just ask.

— bill

LCDarrows.zip (1.26 KB)

That code works just fine. Does just what you meant it to. Really appreciate your efforts.

Yes, I figured the arrows were just the top pixels. I followed your code until the function. I have no idea how you did what you did. It will take time, but I'll dig into it to find out.

I will ask this question though: When you said:

To simplify the coding it only works on single LCD display and requires that you name your lcd object "lcd".

What is meant by naming the lcd object "lcd"? I've Googled object oriented programming, and the more I read, the more confused I become. My perspective is that of a 45+ year old who, up until 3 years ago designed every hardware control device in analog. Could you point me to a resource that might cover such topics? I see such things being discussed, and assume I'm lacking some basic fundamentals to be really effective in this.

I have almost no C experience except what I've read so far in a college text book. Wife & kids = no time for formal courses. It's an uphill struggle learning this, but will grasp it eventually. Having done some assembly, it seems that all the libraries and functions in Arduino have wrapped all the complexities up in a neat little package so most programmers don't worry / see them. Makes is really easy (most of the time) for those with little experience.

Thanks again, Mike

ih55t: I will ask this question though: When you said:

To simplify the coding it only works on single LCD display and requires that you name your lcd object "lcd".

What is meant by naming the lcd object "lcd"?

That is just some fancy C++ words for the code expects a global variable named "lcd" to be used to control the lcd library. The line at the top:

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

Is called a C++ constructor. It is creating a global "object" from the LiquidCrystal class called lcd and the parameters are being passed to the LiquidCrystal library and are used to fill in other data fields inside that object. The library allows controlling more than a single instance of the hardware. (multiple LCDs) Each instance would get its own object. The code I did makes an assumption that you have one of these LCD library objects and that it is called lcd. The code would need to be changed a bit if you wanted to be able to call it anything you wanted or wanted to be able control more than one of them.

The LCDsetArrows() function creates a single custom character. A custom character is 8 bytes. Each byte represents a horizontal row of pixels starting at the top. Only the lower 5 bits are used. The code declares an 8 byte data array and asks it to be initalized to all zeros. (In this case it really isn't necessary to zero out the memory) The code only modifies the first byte since that is where bits that control the arrows are. It calls createChar() to send the custom character bytes to the LCD. Then sets the address to 0x40, then writes the custom character.

The ternary in the function is a bit obtuse. The line:

           (a0 ? 0b10000 : 0) | \

means If a0 is nonzero use the value of 0b10000 else use a value of 0 All the values are ORd together then ORd into arrowData[0]

It probably would have been much simpler and clearer if this code:

        arrowData[0] = \
                (a0 ? 0b10000 : 0) | \
                (a1 ? 0b01000 : 0) | \
                (a2 ? 0b00100 : 0) | \
                (a3 ? 0b00010 : 0);

was replaced with this:

        if(a0) arrowData[0] |= 0b10000;
        if(a1) arrowData[0] |= 0b01000;
        if(a2) arrowData[0] |= 0b00100;
        if(a3) arrowData[0] |= 0b00010;

--- bill

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

Is called a C++ constructor. It is creating a global "object" from the LiquidCrystal class called lcd and the parameters are being passed to the LiquidCrystal library and are used to fill in other data fields inside that object.

The library allows controlling more than a single instance of the hardware. (multiple LCDs) Each instance would get its own object.

Let me try this without using any C jargon. (Bill: Correct me if this simplification is incorrect)

The 'lcd' part of the statement is really what we are discussing here. This is the name that the programmer has arbitrarily given to his display. He could have just as well called it 'mario' or 'plugh'.

If your application calls for more than one display you can control any or all of them using the same library. You would differentiate between them by giving each a display different name.

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LiquidCrystal mario(12, 10, 5, 4, 3, 2);
LiquidCrystal plugh(12, 9, 5, 4, 3, 2);

For a complete example see this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265969050

What Bill is saying is that his code snippet only works with one display and you must use the name lcd for that display. It's as simple as that.

Don

Edit: If you wind up in a cave it isn't my fault. If you don't understand this disclaimer then don't worry about it, your hair probably isn't grey either.

Thanks guys, you taught me something today already (it's 2:10 AM at the moment over here).

You guys gave me the most reasonable explanation of OOP I’ve read so far. Beginning to make good sense. Thanks again for your help.

Mike