Controlling an LCD with just 3 pins…

by Stephen Hobley on February 7, 2011

Post image for Controlling an LCD with just 3 pins…

How do you know when you’ve had a really good idea?

It’s when you go online and realize that a bunch of other people have all done the same thing.

I use often use LCD panels in my projects – they’re cool to look at, and can really help with tracking down bugs. Trouble is, they often take waaaay too many pins to control. Even using the 4-bit transfer mode you still use 6 pins.

The “Rolls-Royce” solution is to get an LCD to serial adapter, then hookup a software serial library and control the LCD from just one pin. You can get these boards here.

I own two from Modern Devices, but I own a lot more LCDs and it soon became apparent that buying a serial controller for each one was probably not the way to go. So I had a rummage around to see what I could use at instead.

I found a big bag o’ 74HC595 shift registers that I got for next-to-nothing on eBay. The 595 shift register can control an almost unlimited number of output pins from just 3 control lines. This seemed a good place to start – so I designed a small add-on board, much like the serial adapters – with a small pot for contrast control, and the option of controlling the backlight or two additional LEDs from one 595 chip.

The data sheet for the 595 is here.

I made the board as “single-sided” as I could – but it still requires 3 jumpers. The very top one can be the resistor used to run the backlight on the LCD (if one is required – check with the data sheet for your LCD). I left space for a small MOSFET to control the backlight – but I just jumpered this out so the backlight remains on all the time.

I updated the Arduino (22 release) LiquidCrystal library to use the 595 board. Download is here.

600 DPI Etchmask (TIFF file)

It was only after I’d finished it, that I realised a couple of other people had done a similar thing – although I think it’s fair to say this is the most compact solution.

UPDATE (via the adaftruit forum) – this is a neat idea – using a non-latching shift register, a diode and a resistor you can reduce the number of pins down to 2. It does require more work on the uController, as you have to shift out zeros to clear the register first, but it’s a clever solution.


{ 16 comments… read them below or add one }

Enlightenment February 8, 2011 at 2:18 am

OR… could buy a display with SPI bus interface built into it.

Stephen Hobley February 8, 2011 at 9:16 am

How much are they?

Last time I looked anything like that was expensive.
I can get a four line LCD for around $11 and the rest of the parts were under $2.

Enlightenment February 11, 2011 at 8:24 pm

3×16 LCD module from Mouser is less than $12. Works with SPI / 4-bit / 8-bit plus works with both 3.3V and 5V.

Stephen Hobley February 11, 2011 at 9:34 pm

I just checked them out – looks good, but it says “4-wire SPI” I didn’t think you needed four wires for SPI – does that include power and ground?

fonz February 17, 2011 at 5:19 pm

4 wire spi = clk,enable,dout,din
3 wire spi = clk,enable,dout/din

with three wire spi the master needs to tristate the data pin when reading from the slave
if you don’t need to read from a four wire spi just ignore the extra pin

Rod September 21, 2011 at 4:53 am

Thanks so much for your work. You do have, sir, the only bloody library that has managed to do the right thing, and I’ve tried a few 🙂
One quick note on LED pins… the LCD doesn’t respond immediately to setLEDxPin(), _register needs to be sent (e.g. calling lcd.shift595() in the setLEDx call) maybe that’d be something to add to the code?

Steve December 2, 2011 at 4:49 am

many thanks for making this library available! I have a question though: I’d like to make my own 595 circuit on a veroboard and I prefer to use different connections of pins between the LCD and the 595. Is it possible to use your library by changing some pin definitions inside your code? eg suppose I want the LCD RS pin connected to pin 1 (not 7) of the 595 etc
thank you!

Jan-Albert December 11, 2011 at 3:40 pm

It seems to me you’ve made a small mistake in the LiquidCrystal595.cpp file :

—–Shift Reg to LCD——–
* SR Pin 15 – ENABLE 10000000
* SR Pin 1 – D4 00000010
* SR Pin 2 – D5 00000100
* SR Pin 3 – D6 00001000
* SR Pin 4 – D7 00010000
* SR Pin 5 – MOSFET / LED1 00100000
* SR Pin 6 – LED 2 01000000
* SR Pin 7 – RS 00000001
* ———————————————————————————–
// 595 mappings – LED1 is also the backlight controller
#define ENABLE_PIN B00000001
#define RS_PIN B10000000
#define LED1_PIN B00100000
#define LED2_PIN B01000000
#define DATABITS B00011110

#define PIN_D4 B00000010
#define PIN_D5 B00000100
#define PIN_D6 B00001000
#define PIN_D7 B00010000

Did you mistake RS and EN in the comment or in the code?
Trying to use your library together with an ETT ET-CONV board (which is nothing more than a prebuilt 75HC595 board, see for the diagram) but am getting no results so far, possibly due to a wrong pinout definition in the .cpp file

Stephen Hobley December 11, 2011 at 4:13 pm

Possibly – although I have a couple of boards running OK right now.

I might have posted an earlier file by mistake.

Jan-Albert December 11, 2011 at 6:19 pm

I got it to work in the end; pinout for the ET-CONV is

* SR Pin 15 – BACKLIGHT 00000001
* SR Pin 1 – RS 00000010
* SR Pin 2 – RW/LED2 00000100
* SR Pin 3 – EN 00001000
* SR Pin 4 – D4 00010000
* SR Pin 5 – D5 00100000
* SR Pin 6 – D6 01000000
* SR Pin 7 – D7 10000000

Also managed to port the library to Arduino 1.0 quite easily; just a few things changed. Here are the diffs

virtual void write(uint8_t);

#include “WProgram.h”
= _numlines ) {

> if ( row > _numlines ) {
inline void LiquidCrystal595::write(uint8_t value) {
< return 1; // assume sucess

Rowan January 4, 2012 at 1:23 pm

I could not quite follow Jan-Albert’s advice to make this library Arduino 1.0 compatible, so I had a look in to it myself, here is what worked for me:

Open LiquidCrystal.h and look around line 98 for:
virtual void write(uint8_t);
change to:
virtual size_t write(uint8_t);
Save and close.

Open LiquidCrystal.cpp. There are two changes for this file.
Change 1: Look around line 44 for:
#include “WProgram.h”
change to: (this will make it Arduino 1 and = 100

Change 2: Look around line 279 for:
inline void LiquidCrystal595::write(uint8_t value) {
send(value, HIGH);
change this to:
inline size_t LiquidCrystal595::write(uint8_t value) {
send(value, HIGH);
Save and close.
Quit Arduino and open it up again – everything should now compile just fine.

Good luck and thanks to Stephen for writing this library.

Stephen Hobley January 4, 2012 at 1:27 pm

Thanks for posting this update.


Alessandro January 6, 2012 at 6:36 pm

Very good indeed, built a custom PCB for my lcd .
Thx a lot , going to use a lot !

Alessandro January 7, 2012 at 7:31 am

Hi Steve
I just put up a page with my HW implementation of your circuit , it works like a charm !

António May 12, 2012 at 7:38 am


Nice project.
However, how do i control the leds with code.

Z.K. September 2, 2012 at 1:51 am

I was wondering if you have an actual schematic for this design? Looking at your board layout there seems to be something wrong with the way the backlight is wired up. It could just be the way I am looking at it. I am more comfortable looking at a regular schematic. I have tried several different designs like this though and I have never been able to get one to work. I have a feeling though that it is either in the libraries or the software I am using. I will probably buy the AdaFruit one though and see if I can get that to work.

Leave a Comment

Comments links could be nofollow free.

{ 1 trackback }

Previous post:

Next post: