LCD direct driving (Part 1)

Part 1

Driving LCD units has been largely documented over internet, arduinoos started investigating this subject with this post.  These units look like the following one:

hex

The disassembled front panel features the following parts:

dissassembly

 

 

And the bottom panel of  the LCD unit features two controllers:

LCD_bottom_tag

These controllers handle the signals from Arduino (or any MCU) and drive the LCD matrix which is composed of two lines of 16 characters. Each character is a 5 by 8 dots matrix as illustrated below:

matrix

So the resulting matrix contains 16 lines and 80 rows. This matrix is driven by the controllers with the wiring is illustrated below:

uc_simplified_schema

For example, the controller’s may be one of KS0066, SPLC780D1 or HD74800. Carefully read the communication protocol related to each of these controllers prior to programming your own code.

 

Now we listed the leading components of an LCD unit, we will focus on only one, the liquid crystal display. The goal of this article is to show how to drive a liquid crystal display directly from Arduino, without any controller or external component. Next is a picture of a bare liquid crystal display:

LCD_3_4

 

For sake of simplicity, we will use a 7 segments display. Unlike the alphanumeric LCD units which have been detailed till now,  a character is displayed using 7 segments instead of using a 5 by 8 dots matrix. For example, displaying a ‘A’ is made as follow:

alphanum_7segments_A

The 7 segments display has 24 pins for driving 3 digits of 7 segments and 2 dots arranged as follows :

LCD_segments

Compared to a 4/8 data pins LCD units, using 24 pins may not be handy but it could be nice to use an LCD display in some use cases, for example:

  • very small packaging
  • very low cost
  • curiosity!

Technically, each pin drives one segment except one pin which is called COM (for common). The datasheet of the LCD display above gives the following segments mapping:

LCD_segs

Each digit has 7 segments called A, B…G. For example, activating the segments B2 and C2 will show a “1” on the center digit. The datasheet provides the following pins mapping:

pin_mapping

Now we know which segment to activate to print something, we will see how to activate a segment. To be set on or off, a liquid crystal segment has to be polarized in one direction, then polarized in the other direction, call it the first and the second pass. Then the segment will keep its state (on or off) for few milliseconds if no voltage difference is applied across it. The voltage applied to a segment is the difference between its associated pin voltage and the COM pin voltage as illustrated below:

unconnected_simplified

Driving an LCD display with Arduino requires the use of the digital pins, they may be:

  • at 0V
  • at 5V
  • left unconnected

Left unconnected ? digitalWrite() does not provide it, huh ? Not directly but the following couple of lines set a pin unconnected (AKA floating or High-Z):

pinMode(pin, INPUT); /* set pin as input */
digitalWrite(pin, LOW); /* set to 0 in input mode */

For sake of simplicity, we will only use the unconnected state for the COM pin as illustrated by the following schema:

unconnected

The following table gives the voltage applied to a segment depending on its pin state and the COM pin state :

COMpinvoltage differenceusage
0V0V0Vturn off a segment (first pass)
+5V+5V0Vturn off a segment (second pass)
0V+5V+5Vturn on a segment (first pass)
+5V0V-5Vturn on a segment (second pass)
unconnected 0V or 5V nohold the last state
In other word, turning a segment off then on is done as follows:

phase

Knowing the theory, displaying a digit can be made in few steps:

  • set each segment data pin according to the  desired text to print
  • set the COM pin low and wait a short period
  • set each segment data pin to the opposite voltage from its previous state
  • set the COM pin high and wait a short period
  • make the COM pin floating

On Arduino, this can be coded as follows:

void Display(uint8_t value)
{ 
/* Set segments data */
uint8_t segments;
segments = vSegments[value];
for (uint8_t i = 0; i < 7; i++) {
digitalWrite(vDataPins[i], (segments & 0x01));
segments >>= 1;
}
/* Common pin state LOW */
pinMode(_comPin, OUTPUT);
digitalWrite(_comPin, LOW);
delay(1);
/* Set segments data */
segments = vSegments[value];
for (uint8_t i = 0; i < 7; i++) {
digitalWrite(vDataPins[i], !(segments & 0x01));
segments >>= 1;
}
/* Common pin state HIGH */
digitalWrite(_comPin, HIGH);
delay(1);
/* Common pin state HIGH-Z */
pinMode(_comPin, INPUT);
digitalWrite(_comPin, LOW);
}

Note: the liquid crystal segments may be damaged if a continuous direct current is applied to them. Take care of not keeping the same voltage across a segment for too long.

Turning on all segments of the third digit except the ‘E’ with Display(0x6F) will display the ‘9’ character as illustrated below:

9

Some LCD displays have several COM inputs to manage more segments with less pins, the next post will show how to handle them.

6 Comments

  1. m00shu says:

    Do you have the full source for this?
    vSegments is not defined and I’m not quite sure what it’s purpose is.

    Thanks

    • Mickael says:

      Hi m00shu,

      The vSegments variable purpose is to store your own sequence of digits to display. Below is a complete example which loops through the 8 segments of an LCD digit and turns on one segment at a time :

      /* The number of characters to display alternately */
      #define SEQUENCE_NBR 8
      /* The number of segments of a single LCD digit */
      #define SEGMENT_NBR 7

      /* The sequence of segments to display, one segment is on at a time */
      uint8_t _vSegments[SEQUENCE_NBR] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
      /* Each LCD segment is attached to an Arduino digital pin output */
      uint8_t _vDataPins[SEGMENT_NBR] = {2, 3, 4, 5, 6, 7, 8};
      /* The common pin is attached to the Arduino pin 9 */
      uint8_t _comPin = 9;

      void setup()
      {
      /* Set all data pins as output */
      for(uint8_t i = 0; i < SEGMENT_NBR; i++) { pinMode(_vDataPins[i], OUTPUT); } /* Set the common pin as output */ pinMode(_comPin, OUTPUT); } void loop() { /* Current digit to print */ static int counter = 0; /* Display the digit */ Display(counter); /* Wait for a short moment while the digit is displayed */ delay(50); /* Prepare the next digit to print */ counter++; /* Print the first digit after the last one */ if(counter >= SEQUENCE_NBR)
      {
      counter = 0;
      }
      }

      void Display(uint8_t value)
      {
      /* Set segments data */
      uint8_t segments;
      segments = _vSegments[value];
      for (uint8_t i = 0; i < SEGMENT_NBR; i++) { digitalWrite(_vDataPins[i], (segments & 0x01)); segments >>= 1;
      }
      /* Common pin state LOW */
      pinMode(_comPin, OUTPUT);
      digitalWrite(_comPin, LOW);
      delay(1);
      /* Set segments data */
      segments = _vSegments[value];
      for (uint8_t i = 0; i < SEGMENT_NBR; i++) { digitalWrite(_vDataPins[i], !(segments & 0x01)); segments >>= 1;
      }
      /* Common pin state HIGH */
      digitalWrite(_comPin, HIGH);
      delay(1);
      /* Common pin state HIGH-Z */
      pinMode(_comPin, INPUT);
      digitalWrite(_comPin, LOW);
      }

  2. Christophe says:

    Thank you very much for your work
    but ;

    I have 2 quest about your comment ans your code
    1) how do you switch between the digits?
    2)Your programm for Arduino is only from one digit ? IF yes where your define your first digit ?

    • Mickael says:

      Hi Christophe,
      Unlike an LCD unit, the liquid crystal displays presented here are not digit driven but segment driven. This means that you control all the segments of the LCD display independently regardless of which digit it belongs to. If you want to control several digits, you have to:

        Wire all segments of all the LCD digits you want to control to your Arduino board
        Refer to the pin mapping table from your liquid crystal display data-sheet. As stated in this post, it tells you which LCD pin to activate in order to turn on or off a specific segment from a specific digit
  3. cardac says:

    Hello… I have tried this – and failed .. dismally ..

    Can anyone show me pure code using an arduino .. so that I can DIRECT DRIVE a 3 digit LCD display eg .. LTD226R-12 …

    This is a single COMMON LCD, with 4 seven segment digits. I have tried to follow whatever I read.. I can easily do a single digit.. (and I am sure all of us can do that easily).. but I cannot do two… or three..
    I challenge anyone.. who thinks they smart enough.. to show me code for 3 digits..!!!
    What I get is the “off” segments flashing on in the background.. with very low intensity, which I cannot seem to get rid of..
    If I had a MULTIPLEXED LCD.. I reckon it would be very much more easy to do… cause there you got a COMMON for each segment.. and that is terribly simple to control…
    It seems that here one has to setup each digit.. and setup the PORT to which it maybe connected.. then do the next digit setup on the next PORT .. then present the data.. in AC mode.. but then the digits mixup – because they are on the same common line..
    Please can someone WHO KNOWS .. show me.. I AM STUPID ..!!!! and cannot work it out..!!!!
    Regards
    Cardac

    • Mickael says:

      Hi Cardac,
      You are right when you say that it would be easier with a multiplexed LCD. Each LCD segment of each digit requires its own Arduino pin and can not share it with another LCD pin. Driving 3 7-segments digits plus the common pin would use 3*7 + 1 = 22 pins. The Arduino UNO has 20 free GPIO pins (including the RX/TX pins used for serial communcation, etc.). So driving 2 or 2.5 digits is OK but driving 3 digits is not possible using a pure software solution.
      You may want to use a board with more GPIO pins such as an Arduino Mega (or use a multiplexed LCD). The good news is that you do not have to configure the port or any low level register: the presented code uses the Arduino digitalWrite() and pinMode() functions. In order to drive 3 digits with 22 GPIO, you have to declare “segments” and “vSegments” to be 32 bits variables. Also, the SEGMENT_NBR macro should be ajusted to 21 in your case.
      Note for the ones who like challenges: it is possible to remove the crystal and the resistor next to the ATmega onto the Arduino UNO board, configure the ATmega fuse bits to use the 8MHz internal oscillator and solder two wires to the ATmega PB6 and PB7 pins so you have 22 free GPIO pins but I do not think it is worth doing it…

Leave a Reply

You must be logged in to post a comment.