[SOLVED] 24x4 LCD displays text on wrong line using setCursor

I have a 24x4 LCD, just like the DV-24200 (that is a 2 liner).

I attached it to a Nano and fired up the demo Hello World. This works like a charm.

When i lcd.print 96 characters all of them are in the excpected place (see picture)
When i use setCursor it switches 2 lines (see picture), the second and thirty line switch place.
(sorry for the somewhat bad pichture, the LCD has no backlight)

How can i correct this?

  • Peter
 lcd.begin(24, 4);
  // Print a message to the LCD.
  lcd.print("000000000000000000000000########################111111111111111111111111222222222222222222222222");
  
  lcd.setCursor(0, 0);
  lcd.print("row 0 line 0");
  
  lcd.setCursor(0, 1);
  lcd.print("row 0 line 1");

  lcd.setCursor(0, 2);
  lcd.print("row 0 line 2");


  lcd.setCursor(0, 3);
  lcd.print("row 0 line 3");

lcd-print-96chars_sm.jpg

lcd-wrong-lines_sm.jpg

maybe with an array if the behaviour is consistent:

byte myLine[4] = { 0, 2, 1, 3 } ;

// then you simply write:

lcd.setCursor(0, myLine[ 1 ] );

// instead of:

lcd.setCursor(0, 1);

It is not nice but it may save you from doing too much research or hacking the library.

Since your display has 96 characters it cannot be using the HD44780U type controller for which the LiquidCrystal library was written.

The fact that the LiquidCrystal library works at all means that whatever controller is on your device has a very similar instruction set.

The messages on your display are a little confusing since the device has rows and columns not rows and lines. I would have written 'column 0 row 0, column 0 row 1', etc.

To get back to your question:

One of the quirks of the ancient HD44780U controller is that in normal use it inherently writes to the horizontal rows in the order 0, 2, 1, 4. The LiquidCrystal library accounts for this quirk and 'corrects' the order when you use the cursor positioning commands.

Your display obviously doesn't have this quirk, so the library is correcting for a problem that doesn't exist thus introducing the problem you are experiencing.

Don

If you are using Arduino 1.6 or later, go to library manager and look for Extensible HD44780, see if that one works. I had an issue with a 1x16 LCD panel, it looks like 1x16 but behaves like a 2x8 and would have required splitting long string and starting the second half on line 1 to make it look normal. When I switched to Extensible and set it as 2x8 LCD display, it handled line splitting gracefully and made my 1x16 LCD look like a proper 1x16 LCD.

This looks like the display in question. Maybe the OP can post a link to the vendor's site.

LCD24x4D (KS0078 controller, Model Agena AA24411) found on TextLCD Enhanced | Mbed

It looks like that display is using a different memory mapping than typical hd44780 displays.
You can work around this by using the setRowOffset(row0, row1, row2, row3) function.
Each argument is the DDRAM memory address of column 0 of each row.

This function exists in the hd44780 library and in newer versions of LiquidCrystal. (around IDE 1.6.0 or newer)
From your tests it appears that the display either uses fully contiguous memory for the rows, or it autowraps to the next row.

A data sheet should show how memory is mapped.

Both hd44780 and LiquidCrystal use this mapping by default:
setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols)
which for 24x4 display would be:
setRowOffsets(0x00, 0x40, 0x18, 0x58)

If you need to swap rows 1 and 2 then it would be:
setRowOffsets(0x00, 0x18, 0x40, 0x58)

--- bill

6v6gt:
This looks like the display in question. Maybe the OP can post a link to the vendor's site.

LCD24x4D (KS0078 controller, Model Agena AA24411) found on TextLCD Enhanced | Mbed

This display come close. As this being my first post i forgot to include the make and model of the LCD.
it is Agena AS24401. I couldn't find a datasheet of this model, I only came across the 2 line version.

I read the post, being out of playing with MCU's for around 10 years i was overwelmed with the info on that page and overlooked the display hint including the controller type.

I will try the other example tonight and have a go with the suggestions.
Will come back with the results. many thanks for the reactions so far.

bperrybap:
It looks like that display is using a different memory mapping than typical hd44780 displays.
You can work around this by using the setRowOffset(row0, row1, row2, row3) function.
Each argument is the DDRAM memory address of column 0 of each row.

This function exists in the hd44780 library and in newer versions of LiquidCrystal. (around IDE 1.6.0 or newer)
From your tests it appears that the display either uses fully contiguous memory for the rows, or it autowraps to the next row.

A data sheet should show how memory is mapped.

Both hd44780 and LiquidCrystal use this mapping by default:
setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols)
which for 24x4 display would be:
setRowOffsets(0x00, 0x40, 0x18, 0x58)

If you need to swap rows 1 and 2 then it would be:
setRowOffsets(0x00, 0x18, 0x40, 0x58)

--- bill

Bill,

i use the latest IDE (1.8.5)

I found the memory mapping in the datasheet of the controller that might be on the display.
When i put the values in (00,20,40,60) as per data sheet the 2th and 4th row starts just before half way the display.

I looked a bit further and i can set the controller is a specifc 4 line mode using the function set.
I am new to the programming and can't find how to do this, i see a couple of init's asuming it the display's init where all the bits are set in a sequence.

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
			     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

I assume that LiquidCrystal::LiquidCrystal is defining a function to be called?
And that init() exucutes that function above?
(i also might be totally off here)

How can i tell what used to set the functionset?

i need to set:

DL=0
N=dont care
RE=0
DH=1
REV=0

As per datasheet they map to

DB0 = REV
DB1 = DH
DB2 = RE
DB3 = N
DB4 = DL

DB5 = 1
DB6 = 0
DB7 = 0
R/w = 0
RS = 0
your first bit (just before rs) is not specified even as enable

how can i tell what line the init contains the function set piece?

-Peter

Elmo:
I found the memory mapping in the datasheet of the controller that might be on the display.
When i put the values in (00,20,40,60) as per data sheet the 2th and 4th row starts just before half way the display.

I looked a bit further and i can set the controller is a specifc 4 line mode using the function set.
I am new to the programming and can't find how to do this, i see a couple of init's asuming it the display's init where all the bits are set in a sequence.

Can you provide us with a link to the datasheet?

You shouldn't need to mess with lcd interface programming at all. Use the API functions in the library to talk to the LCD.
If the column is wrong when you do a setCursor() then the addresses you provided to setRowOffsets() were incorrect.
Did you try the offset values I provided in post #5?
setRowOffsets(0x00, 0x18, 0x40, 0x58)
I recommended those values since (also from post #5):

From your tests it appears that the display either uses fully contiguous memory for the rows, or it autowraps to the next row.

If you do need to send some special commands to the LCD, that can be done using the command() function.

Since you can display on every character position when you print to the LCD and setCursor() seemed to work other than the row selected was incorrect, it is likely that all you need is to get the row offsets/addresses that you hand to setRowOffsets() correct.
Those offsets are the DDRAM address of where each line starts.
If you are off, just count the positions you are off and change the number for that row.

--- bill

Bill,

Thanks for the answer. I copied the datasheet from here: KS0063B datasheet - Description = KS0063B 80CH Segment Driver For Dot Matrix

after some digging into the datasheet i noticed that the display needs special commands to be put in 4 line mode. There is also an pin called IE that controls the control set register. I need to trace that back to the 16pins header to see where it maps to table 6 or table 10.

The addresses for the line are correct, i dubbel checked. my guess is that the display is still in 2-line mode as the special command is not send yet.

the init sequence is at the last page, step 5 puts in 4-line mode. details on page 36
(Extended function set)

I also changed the functionset from 0x20 to 0x22 to match the instructionset of the datasheet.

I will look in to the command function to see i i can send the additional instructions to the display.
(i figured out how the data is beiing prepared (what bit goes where) by the website of Don i found in another post)

Kind regards,

Peter

Elmo:
Bill,

Thanks for the answer. I copied the datasheet from here: DigChip IC database

The 9 page KS0063B datasheet that comes up for me on that page does not have any information on the host interface commands.
Can you provide a link to the one you are looking at and referring to?

The addresses for the line are correct, i dubbel checked. my guess is that the display is still in 2-line mode as the special command is not send yet.

I would bet that the addresses for the rows are incorrect as the setCursor() calls that you did put the cursor at column zero on each row but had rows 2 and 3 reversed according to the photos you posted.

I'd really like to see the datasheet that you are referring to.

The easy way to test the addressing is like I said do a setCursor() and see where the character show up.
Set the cursor position to the beginning of each row and then print the row number.
That will tell you if the addresses are correct and you can then adjust/fix them to be correct.

--- bill

Bill,

I attached the datasheet i use to avoid we are looking at different things.
Sorry for the delay, i am finding my way on the forum.

LCD - Controller KS0078.pdf (321 KB)

Why not just give a link (ebay, amazon etc.) to the product which you have purchased ?

6v6gt:
Why not just give a link (ebay, amazon etc.) to the product which you have purchased ?

I would if I could.

I have collected a bunch of LCD in the past years that had 'default' / "usable" 16 pin pinouts.
They came from old phones/faxes etc that where put aside.
Most are HD44780U ones (2x16, 2x20, 2x40), some are T6963.
i assumed then this was a HD44780U compatible one. now its turn out to be somewhat compatible and needs extra/other commands to work properly.

If i switch the rows in the code it will put the text in the expected place, this workaround will hit me later when i need to change the code. So i am now trying to figure out how to make it work as designed.

via this threat i have made progress, found a datasheet, got inside in the cristal lib.
now looking for the final pieces of the puzzel. I also came across more people having this controller with no idea what to do to get it to work. That made me curious if it can be done as that would help others as well.

When i am back home i am going to try to send the needed instructions to the display to put it in 4 line mode using the command() instruction as i now think the LCD uses a 2 line (or 1 line) mode and displays the text accordingly.

-Peter

Just my opinion but it seems that you may be overthinking this and creating more work for yourself than is necessary.
From your original photos you have a working LCD display that is able to display characters.
That means that all the wiring is correct, the initialization is working including using the proper font size.
You are also able display characters on every character position on the LCD.
The only thing that is off is the cursor positioning when calling setCursor() which is based on the internal address mapping to the pixels on the physical glass. The row positioning is off because it appears that the display you have does not map the pixels the same way as most of the hd44780 displays out there.
As a result, the default row memory addresses in the library are incorrect for your display.

And given you said this:

If i switch the rows in the code it will put the text in the expected place

So all that is necessary is to call setRowOffsets() to tell the library the address where each row starts.

You also said this:

this workaround will hit me later when i need to change the code. So i am now trying to figure out how to make it work as designed.

But the display is working as designed, it is just designed to work a bit differently than the typical hd44780 displays.
I don't see how this will create issues for you later as it is a single line in your sketch with zero modifications to the actual library.

From my initial read of the data sheet it was not clear what their 4 line mode actually does. It would take some experimentation with an actual display to see how it affects the memory to display pixel mapping.
It is likely that it just alters the row addressing, which means it really isn't necessary since you can correct for the row addressing differences by simply telling the library the addresses of each row.

I also changed the functionset from 0x20 to 0x22 to match the instructionset of the datasheet.

Setting their 4 line mode involves using the extended function set command which is more than just changing the function set from 0x20 to 0x22
The lower 2 in 0x22 is setting bit 1 of the function set command which is the DH bit to set "display shift enable". It is not affecting the use of the extended function set nor sending an extended function set.
Messing with low level initialization can be tricky and is a lot more work than simply setting the row offsets in the application.

My recommendation would be that if you can get the display to work correctly with only calling setRowOffsets() then that would be the direction to go rather than mess with the low level code in the library.
It is a single line addition to you sketch.

--- bill

Bill,

Thanks for the insides.

The setRowOffsets is in the LiquidCrystal.cpp file in the begin() instruction, can i call setRowOffsets in the sketch itself to (re)set it?

if yes my guess would be to do it after begin(24,4)?

Kind regards,

Peter

Yes you can call it from the sketch, that is what it was meant for.
You call it after begin() to set the row addresses to whatever you want/need.
I thought that is what you had already done.
It is being called in the library begin() code as well just to set the default addresses, which you can override later.

If you want the rows/lines to wrap around correctly to the next row on the display,
i.e. you are planning on writing more characters on a row than a row holds,

Then you may want to look at my hd44780 library.
It has the ability to enable enable automatic line/row wrapping which will wrap long lines to the next line on the display.
This is useful when a display does not have contiguous memory mapping.
Without this, long lines will not wrap correctly.
This capability is not available in LiquidCrystal.
The functions in the hd44780 library are lineWrap()/noLineWrap() to enable/disable automatic line wrapping.
The hd44780 library can be installed using the IDE library manager.
(Do not install it from a zip file).
You can read more about it on the github page: GitHub - duinoWitchery/hd44780: Extensible hd44780 LCD library
And the wiki: Home · duinoWitchery/hd44780 Wiki · GitHub

The i/o class for talking to the LCD using arduino pins in 4 bit mode is hd44780_pinIO

--- bill

Bill,

Thanks for the info. i downloaded that library a few day ago in search for a solution with the display.
Didn'r know it was yours :slight_smile:

Yes you can call it from the sketch, that is what it was meant for.
You call it after begin() to set the row addresses to whatever you want/need.
I thought that is what you had already done.

i took the liberty to copy the library, rename it and modify it in the 'new' one.
Then i corrected the includes so it would take the .ccp and .h file from the copied version.
This way i ca play without wracking the original source

I played with PIC16xxx and PIC18xxx about 8 years ago and used a compiler for C++ together with a separate programmer. Created my own circuitboards from scratch to put them on. This is where I build some knowledge (that is now rusty) to make the code do what i want. Back in those days it wasn't as easy as it is with Arduino today. Arduino give me a jump start to get going and start to retrieve the old knowledge back of reading datasheet and put the instructions into code.
(also played with i2c sensors and DAC's)

Nice to have people in the forum that are willing help out and point in the right direction

You have to be careful when renaming "libraries" or creating a "new" library from an existing library.
Arduino doesn't really use true libraries. What they call libraries are simply a directory of source code.
The IDE jumps through all kinds of hoops to try to make things simple for users, but because of the way they handle what they are calling libraries, you can run into issues when trying to play games with arduino libraries like renaming them or making a copy of them especially, if you don't rename all the source files.
Arduino has some search priorities when looking for their "libraries".
i.e. if you simply change the directory name and put the same files in it or only rename some of the source files, the IDE can screw up and pull some files from one "library" and some files from the other/new "library".

From what I have seen so far, I don't think you would actually need to modify the library.

--- bill

Good to know that!!

(i copied the dir and rename the 2 sourcefiles)

I am now using the suggested methode and got it working with

lcd.setRowOffsets(0x00, 0x18, 0x40, 0x58);

if i overload the characters the fall off the display at the end for the first 3 rows (at least a couple that i have tested). The fourth row immediately overflows onto row 1.

Will have a look at your library to see how that goes.

Thanks for the help, really appreciated

One last question about this:
Did you ever put backlight in a LCD that didnt have backlight?
This one has the space behind the glass and the wiring on the board in place, it just is missing the led and light transport medium. I found some instruction online how other did this, dont know how risky it is to pull the LCD apart.