ElectronicsInfoline

Displays

16×2 LCD interfacing: wiring an HD44780 module, its timing, custom characters and I2C

By Bulan Sarkar, founder of ElectronicsInfoline (since 2000) · About a 13-minute read

The 16×2 character LCD is still the cheapest way to give a microcontroller project a readout, and nearly every one of them speaks the command set of Hitachi's HD44780 controller. Learn that controller once and the same code drives 16×2, 20×4 and 8×1 modules from an Arduino, an 8051, a PIC or a Raspberry Pi.

This page wires one to an Arduino UNO in 4-bit mode, then works through the parts that cause trouble: contrast, the address map, timing, the start-up sequence, custom characters, I2C backpacks and 3.3 V boards. Each number is either taken from a data sheet or library source or worked out here, and the page says which.

To drive a 16×2 HD44780 LCD, connect VSS to ground, VDD to 5 V, V0 to the wiper of a 10 kΩ pot, R/W to ground, and RS, E and D4 to D7 to six pins of the microcontroller. Send the 4-bit start-up sequence, then write characters: RS = 1 for text, RS = 0 for commands, each byte as two nibbles latched by a pulse on E.

Each command takes 37 µs at the nominal 270 kHz clock and up to 52.6 µs on a slow part. Line 1 starts at address 0x00 and line 2 at 0x40, so the cursor command for the start of the second line is 0xC0 (Hitachi).

The 16 pins

A standard 16×2 module has one row of 16 pins. Two power the logic, one sets the contrast, three control the bus, eight carry data and two feed the backlight LEDs. In 4-bit mode, which almost everyone uses, D0 to D3 stay unconnected.

16 characters × 2 rows, each 5 × 8 dots1VSS2VDD3V04RS5R/W6E7D08D19D210D311D412D513D614D715A16Kpowercontrastcontroldatabacklightpin 1 is usually marked on the board; check before wiring
Figure 1. The 16-pin header of a 16×2 character module, coloured by job. Pins 7 to 10 are unused in 4-bit mode; pins 15 and 16 are the backlight.
What each pin does
PinNameJob
1VSSGround
2VDD+5 V logic supply
3V0Contrast voltage
4RS0 = command, 1 = character
5R/W0 = write, 1 = read
6EEnable: data latched on the falling edge
7D0Unused in 4-bit mode
8D1Unused in 4-bit mode
9D2Unused in 4-bit mode
10D3Unused in 4-bit mode
11D4Data, low bit of each nibble
12D5Data
13D6Data
14D7Data; also the busy flag when reading
15ABacklight LED anode
16KBacklight LED cathode

Wiring it to an Arduino UNO

We use the pins from the Arduino LiquidCrystal library's own example: RS to D12, E to D11, D4 to D5, D5 to D4, D6 to D3, D7 to D2 (Arduino). R/W goes straight to ground, so the module is only ever written to. That costs nothing: the library waits a fixed time after each command instead of reading the busy flag.

16×2 module1 VSS2 VDD3 V04 RS5 R/W6 E7 D08 D19 D210 D311 D412 D513 D614 D715 A16 KArduino UNO5VGNDD12D11D5D4D3D210 kΩ pot, wiper to V0
Figure 2. Arduino UNO to a 16×2 module in 4-bit mode: six signal wires, 5 V and ground to the module, R/W tied to ground, and a 10 kΩ pot across the supply with its wiper on V0.

Connect the backlight last, and only once you know its current (the backlight section below). Many modules have a resistor for it already on the board; some do not, and an LED array straight across 5 V without one will burn out.

Contrast: the voltage across the glass

The liquid crystal is driven by the difference between VDD and V0, not by V0 itself. Newhaven's NHD-0216K1Z, a typical 5 V module, wants 3.5 to 3.9 V across the glass, 3.7 V typical (Newhaven Display). From a 5 V supply that puts V0 at 1.3 V, anywhere from 1.1 V to 1.5 V. Too high and the screen is blank; too low and every dot shows as a dark block.

A 10 kΩ pot covers the whole range and copes with the spread between modules and with temperature. Once you have found the setting, you can replace it with two fixed resistors. For 1.3 V, the closest E12 pair we found is 15 kΩ from 5 V to V0 and 5.6 kΩ from V0 to ground, which gives 1.36 V and draws 0.24 mA. Measure your own pot setting first; it may differ from the typical figure.

Where characters go: the DDRAM address map

Every character on the screen is one byte in the controller's display RAM (DDRAM). On a two-line module, line 1 occupies addresses 0x00 to 0x27 and line 2 occupies 0x40 to 0x67, 40 positions each (Hitachi). A 16×2 module shows only the first 16 of each, so the visible addresses jump from 0F to 40 between the lines.

visible window: columns 1–16line 1000102030405060708090A0B0C0D0E0F10–27line 2404142434445464748494A4B4C4D4E4F50–67hiddenline 2 does not follow line 1: 0F → 40, not 1020 × 4 module, first address of each row:row 1: 0x00row 2: 0x40row 3: 0x14row 4: 0x54
Figure 3. DDRAM addresses (hex) under each visible cell of a 16×2 module. Each line runs on to 40 positions out of sight; on a 20×4 module, rows 3 and 4 are the continuations of rows 1 and 2.

To move the cursor, send the Set DDRAM address command, 80H plus the address. Column 0 of line 1 is 0x80 and column 0 of line 2 is 0xC0; column 5 of line 2 is 0xC5. On a 20×4 module the rows start at 0x00, 0x40, 0x14, 0x54, which is why the LiquidCrystal library stores four row offsets and adds the column to them (Arduino).

Our take: always position with a Set DDRAM address command at the start of each line, rather than printing 16 characters and expecting the 17th to wrap. It doesn't: it goes to address 10, which is off the screen.

Timing: the write cycle and the execution time

A write is simple. Set RS (0 for a command, 1 for a character), keep R/W low, put the data on the bus, then pulse E high and low; the controller latches the data on the falling edge. At 4.5 to 5.5 V the HD44780U needs RS stable 40 ns before E rises, E high for at least 230 ns, the data stable 80 ns before E falls and held 10 ns after, and 500 ns from one E pulse to the next (Hitachi).

RSR/WED4–D70 = command, 1 = dataheld low (write)validtAS ≥ 40 nsPWEH ≥ 230 nstDSW ≥ 80 nstH ≥ 10 nsfalling edge latchesE high to the next E high: tcycE ≥ 500 ns
Figure 4. One write cycle with the HD44780U's minimum times at 4.5–5.5 V. At 2.7–4.5 V every limit is longer: E high 450 ns, data set-up 195 ns, cycle 1000 ns.

Those nanoseconds are not the slow part. After the falling edge the controller is busy carrying the instruction out, and the data sheet gives 37 µs for almost every one at a 270 kHz oscillator, 1.52 ms for Return home, and no figure at all for Clear display. The times scale with the clock, and the clock of a part with the standard resistor can be as slow as 190 kHz. At that end, 37 µs becomes 52.6 µs and 1.52 ms becomes 2.16 ms (Hitachi).

The LiquidCrystal library waits 100 µs after every enable pulse, which covers 52.6 µs with room to spare. For clear() and home() it waits 2.0 ms (Arduino), which is a little short of 2.16 ms on the slowest permitted part. Most modules run near 270 kHz and never notice. If the first character after a clear() sometimes goes missing, that is the likely reason; a delay(1) after clear() fixes it.

The 4-bit start-up sequence

The controller has a power-on reset, but it only works if the supply rises within the limits the data sheet sets, which a USB port or a slow regulator may not meet. The data sheet therefore gives a start-up sequence by instruction that always works. In 4-bit mode it begins with three "8-bit" function sets sent as single nibbles, because the controller may be in either mode, or halfway through a byte, when your code starts (Hitachi).

sendmeaning, then wait—Power onthen more than 40 ms after VDD reaches 2.7 V0x3Function set, 8-bit (one nibble)then more than 4.1 ms0x3Function set, 8-bit (one nibble)then more than 100 µs0x3Function set, 8-bit (one nibble)then execution time0x2Function set: switch to 4-bit (one nibble)then execution time0x28Function set: 4-bit, 2 lines, 5×8 dotsthen execution time0x08Display offthen execution time0x01Clear displaythen about 2 ms (see the timing section)0x06Entry mode: cursor moves right, no shiftthen execution time0x0CDisplay on, cursor off, no blinkthen execution time
Figure 5. The 4-bit start-up from the HD44780U data sheet (Figure 24). The red steps are single nibbles sent before the controller is in 4-bit mode; the busy flag cannot be read before the 0x2.

The waits are minimums. The LiquidCrystal library waits 50 ms at power-up, then 4.5 ms, 4.5 ms and 150 µs between the three nibbles, all a little longer than the data sheet asks (Arduino). The last byte, 0x0C, is not part of the data sheet's sequence: it turns the display back on after step 7 turned it off.

Instruction time
37 µs (up to 53 µs)
Line 2 starts at
0x40 (command 0xC0)
Contrast V0 at 5 V
about 1.3 V
Logic current
1.0 mA typ

Custom characters

The controller keeps eight user-defined 5×8 characters in its character RAM (CGRAM), printed as character codes 0 to 7 (Hitachi). Each character is eight bytes, one per row from the top, with the five dots in the lowest five bits; the eighth row is the cursor line, so leave it blank. Send Set CGRAM address (40H plus the slot number × 8), then the eight bytes.

degree signslot 0, command 0x400x0C0x120x120x0C0x000x000x000x00battery, half fullslot 1, command 0x480x0E0x1B0x110x110x1F0x1F0x1F0x00thermometerslot 2, command 0x500x040x0A0x0A0x0A0x0E0x1F0x0E0x00
Figure 6. Three custom characters drawn on the 5×8 grid, with the byte for each row. A dark dot is a 1; the leftmost dot is bit 4.

Here is the complete UNO sketch for the wiring in Figure 2. It loads the three characters and prints a temperature line and a battery line. The comments give the command byte behind each library call, worked out from the address map above.

#include <LiquidCrystal.h>

// RS, E, D4, D5, D6, D7: the wiring in Figure 2
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

byte degree[8]  = {0x0C, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00};
byte battery[8] = {0x0E, 0x1B, 0x11, 0x11, 0x1F, 0x1F, 0x1F, 0x00};
byte thermo[8]  = {0x04, 0x0A, 0x0A, 0x0A, 0x0E, 0x1F, 0x0E, 0x00};

void setup() {
  lcd.begin(16, 2);            // the 4-bit start-up of Figure 5
  lcd.createChar(0, degree);   // command 0x40, then 8 bytes
  lcd.createChar(1, battery);  // command 0x48
  lcd.createChar(2, thermo);   // command 0x50
  lcd.setCursor(0, 0);         // command 0x80: back to the screen
  lcd.write(byte(2));
  lcd.print(" Temp 27.5");
  lcd.write(byte(0));
  lcd.print("C");
  lcd.setCursor(0, 1);         // command 0xC0
  lcd.write(byte(1));
  lcd.print(" Battery 52%");
}

void loop() {}

The I2C backpack

A backpack is a small board soldered onto the 16 pins that carries a PCF8574 I/O expander, so the module needs only two signal wires, SDA and SCL. The expander's eight outputs drive the module in 4-bit mode. On the common boards, and in the LiquidCrystal_I2C library written for them, P0 is RS, P1 is R/W, P2 is E, P3 switches the backlight through a transistor, and P4 to P7 are D4 to D7 (LiquidCrystal_I2C library (johnrickman/LiquidCrystal_I2C on GitHub)).

PCF8574micro-controllerSDASCLP0RSP1R/WP2EP3BacklightP4D4P5D5P6D6P7D7(transistor)7-bit addressesPCF8574: 0x20 to 0x27PCF8574A: 0x38 to 0x3Flinks A0–A2 open: the top of each range
Figure 7. The common PCF8574 backpack: four bits of each byte carry the data nibble and four carry RS, R/W, E and the backlight. The address depends on the chip version and three solder links.

Which address? A PCF8574 answers at 0x20 to 0x27 and a PCF8574A at 0x38 to 0x3F, set by its A0 to A2 pins (Texas Instruments) (Texas Instruments). Backpacks usually pull those pins high and leave solder links to ground them, so an untouched board is most often at 0x27 or 0x3F. Rather than guess, run an I2C scanner sketch once and use the address it prints.

With that bit order, sending the nibble 4 as a character with E high and the backlight on is the expander byte 0x4D: data 0100 in the top four bits, then backlight 1, E 1, R/W 0, RS 1.

How fast can you update the screen?

Whichever way you connect it, the bus is faster than the glass can follow. We counted only the waits and the bus time, not the processor's own overhead, for rewriting a whole 16×2 screen: 34 bytes, the 32 characters plus one cursor command per line.

  • Parallel, 4-bit, with LiquidCrystal: two nibbles per byte, each followed by 100 µs, about 204 µs a byte and 6.9 ms a screen. In 8-bit mode, half that.
  • I2C with LiquidCrystal_I2C: each nibble goes out as three separate I2C transmissions (data, E high, E low) (LiquidCrystal_I2C library (johnrickman/LiquidCrystal_I2C on GitHub)). At 100 kHz each transmission is at least 20 clock periods, so a byte takes about 1.30 ms and a screen 44.3 ms.
  • I2C with all six expander writes for a byte packed into one transmission: 0.65 ms a byte, 22.1 ms a screen.
050100150milliseconds per full 16×2 screen8-bit parallel3.5 ms4-bit parallel6.9 msI2C, packed22.1 msI2C, common library44.3 mscrystal response 150 ms
Figure 8. Time to rewrite all 32 characters, against the liquid crystal's own response time of 150 ms (typical, NHD-0216K1Z). Even the slowest bus is three times faster than the glass.

The NHD-0216K1Z's crystal takes 150 ms to turn a dot on or off, 200 ms at worst (Newhaven Display). Updating more than about 7 times a second only smears the digits. Update a reading two to five times a second, and rewrite only the characters that change.

3.3 V boards: ESP32, ESP8266, Raspberry Pi

Two separate things go wrong when a 5 V module meets a 3.3 V board, and only one of them is about the signals.

The first is contrast. If you run the module itself from 3.3 V, the most you can put across the glass is the whole 3.3 V supply, with V0 at ground. The NHD-0216K1Z needs at least 3.5 V, so it comes up blank or very faint, 0.2 V short however you turn the pot. Power a 5 V module from 5 V, or buy a module sold for 3.3 V operation.

The second is the logic levels. The original HD44780U accepts anything above 2.2 V as a high at a 5 V supply (Hitachi), so a 3.3 V output drives it comfortably. Many modules now carry compatible controllers with different limits: Newhaven's module, with an ST7066U, wants 3.5 V or more (Newhaven Display), which a 3.3 V pin never reaches. The PCF8574 on a backpack powered from 5 V also wants 3.5 V on SDA and SCL (Texas Instruments). It may work anyway, but it is outside the guarantee, and it is the first thing to suspect when a display shows random characters.

012345volts3.7 VV0 1.35 V supply3.3 V3.3 V supplycontrast (VDD − V0)dashed: the 3.5–3.9 V the glass needsHD44780UST7066U module3.3 V output2.2 V3.5 Vminimum HIGH input at 5 V
Figure 9. Left: the voltage across the glass from a 5 V supply (V0 at 1.3 V) and the most a 3.3 V supply can give, against the 3.5–3.9 V the NHD-0216K1Z needs. Right: the lowest input that counts as high at a 5 V supply, for two controllers, against a 3.3 V output.

Keep R/W tied to ground on a 3.3 V board. If the module ever drives the bus to read the busy flag, its outputs swing up to its 5 V supply (the data sheet guarantees at least 2.4 V for a high), and an ESP32 or Raspberry Pi pin is not 5 V tolerant. For a clean fix in the other direction, run the six signal lines through a 74AHCT125, which takes 3.3 V in and gives 5 V out; our 3.3 V to 5 V level-shifting guide works through that part and the I2C options.

Backlight current, and dimming it

The backlight, not the controller, decides your power budget. The NHD-0216K1Z takes 1.0 mA for its logic (3.0 mA at most) and 130 mA for its yellow-green backlight (156 mA at most) through a resistor already on the board (Newhaven Display). That is 99 % of the module's current. Other modules differ a lot, so measure yours: put a multimeter on its mA range in series with pin 15.

A backlight like that is too much for a pin to switch: the ATmega328P's absolute maximum is 40 mA per pin (Microchip). Switch the cathode with a transistor and you can also dim it with PWM. For 156 mA through a 2N2222A forced to a gain of 10, the base needs 15.6 mA. With VBE(sat) up to 1.2 V (onsemi), that allows at most 244 Ω from a 5 V pin, so use 220 Ω. The pin then supplies 17.3 mA to 20.0 mA, and the transistor dissipates at most 71 mW, a rise of about 14 °C.

+5 V15 ALED array +16 K130 mA typresistor on the moduleQ1 2N2222A220 ΩD10PWM
Figure 10. Backlight dimming: pin 15 to 5 V, pin 16 to a 2N2222A switched by a PWM pin through 220 Ω. The module's own resistor still sets the LED current.

With the transistor in place, analogWrite(10, 64) gives about a quarter brightness. D10 is a PWM pin on the UNO and is free with our wiring.

Tips and checks

  • Power up with only VSS, VDD and V0 connected and turn the pot. If a row of blocks appears on line 1, the module and contrast are fine, and any later fault is in the wiring or code.
  • Garbled characters that change on every reset usually mean one data wire is off by one pin, often D4 and D5 swapped. Check them against Figure 2 with the power off.
  • lcd.begin() must run after the module has power. If the module is fed from a supply that comes up later than the board's, the start-up sequence is lost and the screen stays at blocks; power both from the same rail.
  • Long ribbon cables pick up noise on E, the one line that acts on an edge. Keep the wires short and put a 100 nF capacitor across VDD and VSS at the module.
  • For a reading that changes, print it at a fixed position with trailing spaces, such as "27.5 ", rather than calling clear() every time. clear() is the slowest command and makes the screen flicker.
  • An I2C backpack showing nothing at all is often only the contrast pot on the backpack. Turn it through its whole travel before you debug the code.

What we'd do: on an UNO or any 5 V board, wire the module in 4-bit mode with R/W to ground, as in Figure 2; it costs six pins and has no address to find. If pins are short, use a PCF8574 backpack and run an I2C scanner first. On an ESP32 or a Pi, power the module from 5 V and put a 74AHCT125 in the signal lines, or buy a module rated for 3.3 V.

Questions people ask

Why does my 16×2 LCD show only black boxes on the first line?

The glass and contrast are working, but the controller has not been initialised. Check the RS, E and D4 to D7 wiring and that lcd.begin(16, 2) runs. If the boxes look too dark, turn the contrast pot until V0 is nearer 1.3 V.

What is the address of the second line of a 16×2 LCD?

Line 2 starts at DDRAM address 0x40, so the command to move there is 0xC0. Line 1 starts at 0x00, command 0x80.

Can I connect R/W to ground?

Yes, and on a 3.3 V board you should. You lose the busy flag, so the code waits a fixed time after each command instead. The Arduino LiquidCrystal library already works that way.

What I2C address does an LCD backpack use?

0x20 to 0x27 with a PCF8574 and 0x38 to 0x3F with a PCF8574A, most often the top of the range on an unmodified board. An I2C scanner sketch tells you for certain.

Will a 16×2 LCD work on 3.3 V?

A 5 V module usually will not: the glass needs about 3.5 V or more and a 3.3 V supply cannot reach it. Power it from 5 V and shift the signal levels, or buy a module made for 3.3 V.

How many custom characters can an HD44780 hold?

Eight of 5×8 dots, as character codes 0 to 7, or four of the taller 5×10 font. They live in RAM, so load them again after every power-up.

Lessons behind the numbers

Sources and assumptions

Read on 9 October 2026. Our own choices, not taken from any source: the UNO pins (those of the LiquidCrystal example), tying R/W to ground, the 15 kΩ / 5.6 kΩ contrast pair (the nearest E12 divider to the module's typical V0), the 2N2222A backlight switch on D10 with a forced gain of 10, the three custom characters, and the speed figures. Those count only the library waits and the I2C bus time at 100 kHz (9 clocks per byte plus 2 for start, stop and bus-free time), not the processor's own overhead, so real updates are somewhat slower. Module figures are for the NHD-0216K1Z; check the data sheet of the module you buy.