A 16x1 character LCD can look misaligned when firmware assumes one continuous row of memory but the module uses two 8-character address blocks. On that layout, the left half starts at DDRAM address 0x00 and the right half at 0x40, not 0x08. Text written straight through the first block can disappear after the eighth character. For a module confirmed to use this layout, initialize Arduino LiquidCrystal with lcd.begin(8, 2) and write the right half with lcd.setCursor(0, 1) [1][2].
A missing right half is a clue, not proof. Run the two-half test below before changing wiring or replacing the display. If all characters are shifted, flickering, or garbled rather than breaking at the same position, investigate the corresponding symptom instead.
Match the Symptom Before Changing Code
| What you see | What to check next |
|---|---|
| The first 8 characters are readable; the next 8 are missing | Test the two-block address map with two-line initialization. |
| Writing the right half overwrites the left half | Check initialization and cursor coordinates. LiquidCrystal clamps row 1 to row 0 if only one line was declared [2]. |
| All text starts at the wrong position | Disable scrolling and autoscroll, return the display home, and set the cursor explicitly before printing [2]. |
| Blank glass, solid blocks, random symbols, or flicker | Check contrast, initialization, supply, ground, and signal connections. These symptoms do not establish an address-map fault. |
For parallel wiring, verify RS, E, and D4–D7 against your constructor arguments; connect R/W to ground when using the write-only constructor. The official Arduino LCD documentation and this character LCD integration walkthrough provide wiring and initialization context [3].
Run a Complete Two-Half Test
This sketch uses the standard Arduino LiquidCrystal library and a parallel-connected module. Its constructor arguments are RS, E, D4, D5, D6, and D7; change the pin numbers to match your circuit. Start with a powered-down display, then upload and run this sketch.
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 6, 7, 8);
void setup() {
lcd.begin(8, 2);
lcd.setCursor(0, 0);
lcd.print("ABCDEFGH");
lcd.setCursor(0, 1);
lcd.print("IJKLMNOP");
}
void loop() {
}
Expected result on an 8 + 8 module: ABCDEFGHIJKLMNOP appears across the single physical row. In this configuration, software row 0 is the left half and software row 1 is the right half.
The initialization is essential. In the reviewed LiquidCrystal source, begin() stores the line count, and setCursor() limits the requested row to that count. Calling setCursor(0, 1) after begin(16, 1) therefore targets row 0 rather than testing address 0x40 [2].
Record What the Test Actually Shows
Keep wiring, supply, and contrast unchanged while comparing your original sketch with the test. Record the module part number, controller marking, library name and version, initialization call, and the characters visible in each half. If the test joins all 16 characters but the application does not, compare the application's initialization, cursor moves, and scrolling calls first.
If the second half remains blank, do not conclude that the module is damaged or keep changing addresses at random. Check the exact module's DDRAM map and required initialization. A genuinely continuous 16-character module needs its own documented configuration, not an assumed 8 + 8 map.
If Your Module Has an I2C Backpack
The PCF8574 is an I2C-to-parallel I/O expander; it is not the LCD's character-memory controller [4]. On LCDs connected through an I2C backpack, the LCD's DDRAM layout still matters, but library constructors, initialization methods, expander pin mappings, and I2C addresses vary. Use your installed library's documented method to configure two logical lines for a confirmed split module; the parallel sketch above is not a drop-in I2C sketch.
Why the Ninth Character Needs Address 0x40
The HD44780U has 80 bytes of DDRAM. In two-line mode, its address ranges are 0x00–0x27 and 0x40–0x67; the second range does not immediately follow the first [1][5]. A split 16 × 1 module exposes eight positions from each range on one physical row.
| Visible positions | DDRAM addresses on the split layout | LiquidCrystal access after begin(8, 2) |
|---|---|---|
| First through eighth | 0x00–0x07 |
setCursor(0, 0), then write 8 characters |
| Ninth through sixteenth | 0x40–0x47 |
setCursor(0, 1), then write 8 characters |
| No visible positions in the initial, unshifted view | 0x08–0x0F |
setCursor(8, 0) targets this region, not the right half |
LiquidCrystal computes the DDRAM address from the column plus the stored row offset. Its default first two row offsets are 0x00 and 0x40, and declaring two lines enables two-line mode [2]. The issue is a mismatch between the module layout and the application's assumptions, not a library defect.
Keep Application Text Aligned Across Both Halves
After the test confirms the map, initialize once with lcd.begin(8, 2) and centralize writes in a helper. This version writes 16 positions, switches halves after the eighth, pads short strings with spaces, and truncates longer strings. It does not call clear() on every update.
void print16(const char* text) {
if (text == nullptr) return;
bool ended = false;
for (uint8_t pos = 0; pos < 16; ++pos) {
if (pos == 0) lcd.setCursor(0, 0);
if (pos == 8) lcd.setCursor(0, 1);
char ch = ' ';
if (!ended) {
if (*text == '\0') ended = true;
else ch = *text++;
}
lcd.write(static_cast<uint8_t>(ch));
}
}
Call it with print16("READY"); or another valid, null-terminated string. Padding overwrites old trailing text when the new message is shorter. This helper is for single-byte LCD character codes, not UTF-8 text; keep display shifting and autoscroll disabled while using it.
Software validation: the helper was checked with a host-side mock for input lengths from 0 through 20 bytes and a null pointer. Those checks covered padding, truncation, and the two cursor transitions. They validate the helper's string-handling logic, not electrical operation or compatibility with a particular LCD.
Why Changing Row Offsets Alone Does Not Fix It
setRowOffsets() changes where each software row begins; it does not insert a jump after the eighth character in one print() call. The controller continues incrementing its address counter. Even with custom offsets, the application must explicitly split the write at the module's address boundary [1][2].
If Text Is Still Misaligned
- The right half overwrites the left: confirm that the active initialization declares two lines and that no other function reinitializes the display as one line.
- Old letters remain after a shorter message: overwrite the unused positions with spaces, as the helper does.
-
The whole message is displaced: disable autoscroll and return the display home before retesting.
clear()erases DDRAM and resets the address counter;home()restores the home position without erasing DDRAM. Neither changes the physical address map [1][2]. - Blocks or unreadable characters persist: check initialization and wiring as well as contrast. The reviewed LiquidCrystal implementation includes a 50 ms initial wait; custom drivers must follow the module's own power-up sequence [2].
- Results differ between modules: compare exact part numbers, controller variants, supply conditions, and module datasheets before assuming the firmware applies to both.
Check compatibility at the controller and command-set level. A claim of HD44780 compatibility, including on HD44780-compatible character OLED modules, does not by itself establish an identical initialization sequence or visible address map.
Keep the Module or Choose Another Character LCD
If the two-half test works and 16 characters are enough, the address split alone is not a reason to replace the module. Keep the confirmed initialization and write helper together when migrating firmware.
If the application needs more text, a 16 × 2 provides 32 visible character positions and a 20 × 4 provides 80. Compare mounting dimensions, window size, voltage requirements, backlight current, and character LCD size and interface options before choosing a replacement; more positions do not guarantee a compatible pinout or address map.
Only move beyond character LCDs when the interface needs capabilities such as freely positioned graphics or additional fonts. The distinction between graphic and character LCDs concerns how content is represented, not a remedy for a misaddressed ninth character. If that change is genuinely needed, plan the firmware migration from character LCD to graphic LCD separately from this addressing fix.
Selecting a replacement character LCD?
Include your current part number, required character count, interface, and mounting dimensions with your inquiry.
Request a Display QuoteSources
- Hitachi, HD44780U Dot Matrix Liquid Crystal Display Controller/Driver. DDRAM address ranges, address-counter behavior, clear display, and return home. Manufacturer datasheet, distributor-hosted copy. Accessed November 5, 2026.
- Arduino, LiquidCrystal.cpp.
begin(), row offsets, row clamping insetCursor(), and initialization delay. Official library source. Accessed November 5, 2026. - Arduino, Liquid Crystal Displays. Official LCD wiring and usage documentation. Accessed November 5, 2026.
- Texas Instruments, PCF8574 Remote 8-Bit I/O Expander for I2C Bus, SCPS068K. Official datasheet. Accessed November 5, 2026.
- MIT Center for Bits and Atoms, university-hosted copy of the Hitachi HD44780 datasheet. Same manufacturer document as [1], corroborating copy. Accessed November 5, 2026.
En lire plus
To match a round touch screen, choose the display diameter and resolution for your controls, then check the touch area, module outline, cover lens, and host connections against the same assembly dr...
A 128×128 TFT is enough for a small embedded interface built around large readings, short labels, and simple icons. It is usually too restrictive for paragraph text, detailed maps, photo inspection...









Laisser un commentaire
Ce site est protégé par hCaptcha, et la Politique de confidentialité et les Conditions de service de hCaptcha s’appliquent.