Driving a Salvaged LCD Directly from Arduino


Probably lots of us have poked at an LCD from a clock or wherever and wondered what it would take to drive it. I’ve even taken a multimeter to one to see what the voltages looked like. I never got far doing this but I did break some lcds.

After a lot, a lot, of reading and googling around I discovered the two issues:
Most LCDs that you see around are multiplexed. For something like a clock with 7 segment digits, there’s a set of 4 backplane connectors that connect to the same parts of each digit and a set of segment connectors that connect to one particular digit; and LCDs require an AC voltage to drive them – the segment will be visible if it gets a voltage of more than 2V relative to a backplane but that voltage can be positive or negative and it HAS to even out to zero over a few tens of ms.

The result, when you look at waveforms is something like this with the backplanes and segments dancing around multiple voltage levels.

The best general reference on LCDs I found was http://ww1.microchip.com/downloads/en/AppNotes/00658a.pdf. That shows how they’re connected and driven. I later found an atmel application note http://www.atmel.com/dyn/resources/prod_documents/doc8103.pdf that shows you how to drive a multiplexed LCD with the avr’s digital I/O pins.

I had all this stuff percolating when I came across these little runaway alarm clocks for $2.50 in our local Staples. I took one apart finding great little motors, wheels with tires, a battery holder, and an LCD setup that looked like it would be easy to get test probes on and inject signals into.



By the way, I used to think those elastomeric connectors between an LCD and circuit board were from the devil but they're actually great for hacking. I did what I’ve done before and used little bits of post-it notes to insulate the connectors from the circuit one at a time and identify the eight segment and four backplane pins. With a scope I was able to see the typical stair-step voltages on the backplanes and segments. I drew myself many many little signal diagrams and built a spreadsheet and finally a segment/backplane diagram.

The segments turned out to be particularly easy to understand. The leftmost digit could only be 1 or blank and each of the other digits needed two segment pins: sort of for the left side of the digit and the right side. For my proof of concept I’m only actively driving one digit so I only need two segment pins.

Armed with this and the info from the application note I built a proof of concept driver. To get three voltage levels for the backplanes, I tied each of the pins to ground through a 33K resistor. If I set the arduino’s pin as output it could easily pull that to VCC or 0 and, if I set the pin to input with the pull-up resistor enabled I got a nice balance at ½ VCC. I used a 3V supply to run the AVR chip because the highest voltage I’d seen driving the LCD was around 3V.

The software feels clumsy to me but it proves the concept. The main loop just sets the single digit to be displayed and repeatedly calls the display driver. The display driver keeps track of millis() and every 4ms it advances a state counter. Each state from 0-7 defines a half cycle for one of the backplanes: backplane 1 in states 0 and 1, backplane 2 in states 2&3, backplane 3 in states 4 and 5, and backplane 4 in states 6 and 7.
I have a particular application in mind for this and I’ll use a timer interrupt to better separate the functions.

#include <Streaming.h>
#define cout Serial
#define endl '\n'
const int BP1=7, BP2=9, BP3=8, BP4=10; //backplanes
int S1=11, S2=6, S3=12; //segment outputs.  S1 is always blanked, S2&3 are driven

void setup()
{
  Serial.begin(19200);
  cout <<"Hello from the clock LCD test mule \n";
  pinMode(S1,OUTPUT); pinMode(S2,OUTPUT);pinMode(S3,OUTPUT); //segments are live
}

void loop(){
  int displaydigit= (millis()/1000)%11; //test pattern cycles from 0 to 10
  drivelcd(displaydigit);
}


void drivelcd(int digit){ //drive the LCD to display a digit
  //each of the LCD backplanes is cycled low then high for 4ms while the others are held at a middle-voltage
  //the segments are lit by setting them opposite to the active backplane
  //each bit in the digit segment table below defines the state of a segment for a particular backplane for that digit 
  //the segment 2 bits are first then the segment 3 bits
  //e.g. for a 0(the 1st element) segment 2 is on for backplanes 2 and 4 (0101) and segment 3 is on for all of them(1111)
  const unsigned char dst[]={0b01011111,0b00000110,0b00111101,0b00101111,0b01100110,0b01101011,0b01111011,0b00001110,0b01111111,0b01101110,0b01111110}; //digits 0-A
  static int stateprev=8; //tells us when we change states
  int statenow=(millis()/4)&0x07; //cycles from 0-7 changing every 4 ms
  if (statenow!=stateprev){ //if the state has changed
    //reset backplane pins to input and activate the pull-up resistors
    pinMode(BP1,INPUT); pinMode(BP2,INPUT);  pinMode(BP3,INPUT);pinMode(BP4,INPUT); 
    digitalWrite(BP1,HIGH);digitalWrite(BP2,HIGH);digitalWrite(BP3,HIGH);digitalWrite(BP4,HIGH);
    switch (statenow) {
      case 0:
        pinMode(BP1,OUTPUT); digitalWrite(BP1,LOW); //activate backplane 1 for 1st half of its cycle
        digitalWrite(S1,LOW); //ensure segment 1 stays blank
        digitalWrite(S2,(dst[digit]&0x80)>>7); //activate segment 2 if needed
        digitalWrite(S3,(dst[digit]&0x08)>>3); //activate segment 3 if needed
        break;
      case 1:
        pinMode(BP1,OUTPUT); digitalWrite(BP1,HIGH); //set BP1 2nd half of its cycle
        digitalWrite(S1,HIGH); //ensure segment 1 stays blank
        digitalWrite(S2,!digitalRead(S2)); //toggle segment 2
        digitalWrite(S3,!digitalRead(S3)); //toggle segment 3
        break;
      case 2:
        pinMode(BP2,OUTPUT); digitalWrite(BP2,LOW); //set BP2 1st half of its cycle
        digitalWrite(S1,LOW); //ensure segment 1 stays blank
        digitalWrite(S2,(dst[digit]&0x40)>>6); //activate segment 2 if needed
        digitalWrite(S3,(dst[digit]&0x04)>>2); //activate segment 3 if needed
        break;
      case 3:
        pinMode(BP2,OUTPUT); digitalWrite(BP2,HIGH); //set BP2 2nd half of its cycle
        digitalWrite(S1,HIGH); //ensure segment 1 stays blank
        digitalWrite(S2,!digitalRead(S2)); //toggle segment 2
        digitalWrite(S3,!digitalRead(S3)); //toggle segment 3
        break;
      case 4:
        pinMode(BP3,OUTPUT); digitalWrite(BP3,LOW); //set BP3 1st half of its cycle
        digitalWrite(S1,LOW); //ensure segment 1 stays blank
        digitalWrite(S2,(dst[digit]&0x20)>>5); //activate segment 2 if needed
        digitalWrite(S3,(dst[digit]&0x02)>>1); //activate segment 3 if needed
        break;
      case 5:
        pinMode(BP3,OUTPUT); digitalWrite(BP3,HIGH); //set BP3 2nd half of its cycle
        digitalWrite(S1,HIGH); //ensure segment 1 stays blank
        digitalWrite(S2,!digitalRead(S2)); //toggle segment 2
        digitalWrite(S3,!digitalRead(S3)); //toggle segment 3
        break;
      case 6:
        pinMode(BP4,OUTPUT); digitalWrite(BP4,LOW); //set BP4 1st half of its cycle
        digitalWrite(S1,LOW); //ensure segment 1 stays blank
        digitalWrite(S2,(dst[digit]&0x10)>>4); //activate segment 2 if needed
        digitalWrite(S3,(dst[digit]&0x01)>>0); //activate segment 3 if needed
        break;
      case 7:
        pinMode(BP4,OUTPUT); digitalWrite(BP4,HIGH); //set BP4 2nd half of its cycle
        digitalWrite(S1,HIGH); //ensure segment 1 stays blank
        digitalWrite(S2,!digitalRead(S2)); //toggle segment 2
        digitalWrite(S3,!digitalRead(S3)); //toggle segment 3
    }
  stateprev=statenow;
  }
}

Nice work figuring that out. Gonna check my local staples, see if they have any of those.

CrossRoads:
Nice work figuring that out. Gonna check my local staples, see if they have any of those.

Thanks - yes there's lots of stuff in there. besides the lcd, motors and wheels there's some nice momentary switch buttons and a speaker!

I bought three because: Hey, it's an alarm clock that runs away when you push the snooze bar!

Uploaded with ImageShack.us

This is the circuitry of a programmable remote control.
The pins in the lower part of the picture are connected to a 2-lines back-lighted alphanumeric LCD; as the remote control just costs,brand new, 8 Euros, I'd like to figure out if and how I can access and drive the display to use it with an Arduino.
The pins in upper part of picture are clearly available for external access, but.... how to figure out their use (if possible)?

i see the pins near the top of the pic - between the labels GND on the right and 3V on the left. I don't see the ones at the bottom.

do you have a scope available?

I don't look at hack-a-day every day so I was just bumping down through old posts when i came on my very own thingie.

Ok, I'm easily amused, but i love it. some good ideas in the comments too.

I went to one of my local Staples to try to find this clock; I was more interested in the gear motors and wheels!

Unfortunately, they didn't have any, which is too bad - that's a great price for such robotics components for small desktop rovers...

:stuck_out_tongue:

Not shown on line either. Maybe it was a local thing?

CrossRoads:
Not shown on line either. Maybe it was a local thing?

Apologies if i misled anyone, I'm in Ottawa so there's probably a whole Staples Canada thing going on. I bought three and there were a few left but probably not worth mailing to anyone. They were up at the checkout as christmas gimmicks so i think they could well be a local thing.

You can get them here:

http://www.dealextreme.com/p/novelty-run-around-wake-up-n-catch-me-digital-alarm-clock-on-wheels-white-4-aaa-44211?r=25235306

And in black:

http://www.dealextreme.com/p/novelty-run-around-wake-up-n-catch-me-digital-alarm-clock-on-wheels-black-4-aaa-44955?r=25235306

Not $2.50, but free shipping!

LOL!!

I read through this thread, scrolled back up to the top and clicked to go back to the parent forum, and right as the thread went poof and I was there, I read "A... Pikachu" on the chip

hello!

First of all thank you for the explanation and example code :slight_smile: i kn ow this is an old thread but i would need some help.
i used your code for driving a motorycle LCD. My dash has died and now i haave a few LCDs i want to make them work :).
My LCD has 4 Backplanes and 10 segments. I expanded your code, changed some bits and it works perfectly :slight_smile:
five segments count with no problem.
But the thing is, the segments that should be off are not off but are shaded. i measured the middle voltage of the backplanes and are 2.5V (5V Vcc). So that is exactly half. So im thinking since the arduino isn't a dedicated LCD driver, that this is a timing issue?

Hi.

Invented by a student for an industrial design class, was featured on pay (cable) TV here, Australia, about 5 years ago.
Interesting story.

Tom.... :slight_smile:

I know that some time has passed, but maybe this can be useful to someone.

@Tady: driving a LCD glass with 5V can be too much: as bill2009 says, most LCDs are driven at 3V with two 1.5V AAA cells or one 3V coin cell.
The reason why you see shaded segments instead of clear ones, is that even an "off" segment signal will have a low AC component relative to any backplane: at any time, being in phase with the active plane, it will have some AC relative to the others that are at 0V.
To avoid to switch "on" the unwanted segments, this AC component must be lower than the minimum needed to polarize the crystals. It turns out that feeding 5V to the chip, leads to an AC component that is over this threshold.

Note: if you are so lucky to find some documentation about the threshold of your LCD, you can calculate the maximum voltage figuring out the RMS voltage of that "off" AC signal. In the 5V case this should be SQRT((2*2.5^2)/3)=2.04V so it works only if the LCD threshold is higher than 2.04V (and lower than the "on" signal RMS voltage).
I found a good example here: http://www.zilog.com/docs/appnotes/lcd_apnt.pdf

I am trying to drive a thermostat LCD I would like to use for IoT: say me good luck :wink: