1602 LCD/Inside the two chips/08. Two rows, forty columns
Inside the two chips · 08 of 11

Two rows, forty columns

The display has eighty characters of memory and thirty-two lit cells. Knowing where the other forty-eight went explains setCursor, scrolling, and why a long message vanishes instead of wrapping.

Eighty characters, thirty-two of them lit

The controller has eighty bytes of display memory laid out as two rows of forty. A 1602 lights the first sixteen of each. The rest is real memory: you can write to it, it remembers what you wrote, and nothing shows.

The window moves, the text does not
window starts at 0x00
scrollDisplayLeft(), this many times0
Window starts at
0x00
Memory per row
40 chars
Still off the right edge
24
Row 1 starts at 0x40, not 0x10. The two rows are forty characters apart in memory even though only sixteen of each are lit, and that gap is why setCursor(0, 1) exists rather than you counting characters. The twenty-four columns past the right edge are real memory: you can print into them, and scrolling is the window sliding over them. Print past column 39 and the characters are simply dropped — which is why a message longer than 40 characters has to be scrolled by your own code, a slice at a time, rather than by the display.

Row 1 is 0x40

The two rows are forty apart in memory, not sixteen. Row 0 runs from address 0x00 and row 1 from 0x40 — and that is the whole reason setCursor(column, row) exists rather than you counting characters from the top left. The library holds those two numbers in a lookup table and does the addition for you.

It is also why text does not wrap. A string that overruns column 15 keeps going into columns 16 to 39 of the same row, where nothing is lit. It has not been lost and it has not moved to row 1; it is sitting in memory, off the edge.

Scrolling moves the window

scrollDisplayLeft() does not move your text. It moves which sixteen of the forty columns are lit, one column at a time, over both rows at once. Print a message thirty characters long and scroll fourteen times and you have read all of it.

Past forty characters, the controller drops what it cannot store. A message longer than that has to be scrolled by your own code: keep the string in a normal array, print a sixteen-character slice of it, and print a different slice a moment later. That approach works for any length and is usually the one to reach for.

Two instructions worth knowing apart

home() returns the cursor to the top left and undoes any scrolling. It erases nothing.

clear() writes a space into all eighty cells and then goes home. The controller needs about 1.5 ms to do it — forty times longer than any other instruction — and the library sits and waits. That is the flicker people describe when they call it once per loop.

When it does not work

My text does not wrap onto the second row

It never will. The rows are separate stretches of memory forty characters apart, so a string that runs off the end of row 0 carries on into columns 16 to 39 of row 0 — which are not lit. Split the string yourself and setCursor(0, 1) for the second half.

Characters past the sixteenth just disappear

They are stored, not lost, until you pass column 39. Scroll the window and they come into view. Past column 39 the controller drops them, which is why a message longer than forty characters has to be scrolled by your own code a slice at a time.

scrollDisplayLeft() scrolls both rows together

Yes — the window is one window over both rows. There is no way to scroll one row and hold the other. If you need that, print a moving slice of your own string into the row you want and leave the other alone.

home() does not clear the screen

It is not meant to. home() puts the cursor back at the top left and undoes any scrolling; clear() wipes all eighty cells and takes about 1.5 ms doing it, which is forty times longer than any other instruction.

Where this goes next

The ghost digit, and the one-line fix that is not clear().

Numbers that change

Edit this page — content/books/lcd1602/two-rows-forty-columns.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

3-Pack 1602 LCD Display Module, I2C 16x2 Blue

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum