LCD Modules

Note: hexadecimal values are marked as 0x.

Introduction

LCD (Liquid Crystal Display) displays can be found in many forms, capable of indicating letters, numbers, and other symbols, and are used in a wide range of equipment and entertainment devices. They offer either custom characters or high resolution graphics, are able to output in either monochrome or millions of colours, and consume very little current. However, many LCDs require a backlight so that what the LCD is displaying can be viewed in a range of lighting conditions and with some LCDs it can be difficult to see clearly what is being displayed on the LCD at some viewing angles.

You can view a small selection of LCDs below which I've taken from various pieces of electronics and are lacking a backlight but it gives you a good idea of what some of the forms of simple LCDs look like.

New: Novametrix 515C Graphic LCD (21/02/2018)

MGLS-24064 Graphic LCD Module (20/12/2017)

Sonos LQ035Q7DB03F TFT LCD (6/12/2017)

Update: SainSmart 3.2" TFT LCD (5/1/2016)

SainSmart 3.2" TFT LCD (25/12/2015)

Raspberry Pi to HD44780 LCD module (24/11/2014)

Fax machine LCD Module (Added: 10/06/2013)

VTECH Precomputer 2000 LCD Module (Added: 25/05/2012)

WGM-12232M Graphic LCD Module


You can email me at james.boshikoopa@gmail.com

To return to the main electronics page please click here.

LCD modules (a.k.a LCM or Liquid Crystal Module), however, incorporate an LCD into a small unit containing electronics to drive the LCD either requiring appropriate signals to drive the LCD in a low level way or the more intelligent LCDs accept commands to determine what the LCD is to display, the colours to use, etc. Simple LCD modules just output text and are often found in printers and vending machines, to name a few examples. More complicated LCD modules can display graphics with many colours and they can be found in Sat Navs, music systems, 3D printers, etc.

HD44780 and other text only LCD modules

The 'HD44780' and compatible intelligent LCD modules use a HD44780 controller chip and are able to display characters only and are very commonly used in a wide range of electronic devices such as in printers, vending machines and much more. These parallel intelligent LCDs are easy to use to the point that for testing purposes you can get one to display characters by using just switches! They are usually used in either 4 or 8-bit mode with the 4-bit mode requiring that both halves of the 8-bit data be sent one after another but having the advantage that fewer connections are needed. This is especially useful when, for e.g., you are interfacing an LCD module to a microcontroller that has few I/O.

You can get HD44780 LCD modules that can display only a few characters, and others that can show as many as eighty, either on one or more lines; these display modules all use a standard 14 connection pinout with one or two additional connections if there is a backlight (usually an LED). There are HD44780 LCD modules that can display more than eighty characters, however, they actually use more than one controller chip and have a different 16 (or more) connection pinout.

There are two basic modes for using an HD44780 LCD module: instruction mode and data mode. Instruction mode is used when you want to send the LCD module commands such as to clear the display or to turn the display on or off. The data mode is used to display characters on the LCD by sending it ASCII values representing the characters. A single input connection to the LCD module called RS (Register Select) selects whether the value on the data bus is to be interpreted as character data or an instruction.

It should be noted that not all characters in the ASCII standard are available as some are replaced with special symbols such as an arrow or the Yen character. That at least makes it more appealing to use worldwide but it you really need the missing characters you can create them yourself and then send them to the LCD module. There is only room for 8 custom characters, a limitation which it seems is due to the fixed length instructions. The custom characters are stored in RAM as part of the LCD module so they will remain as long as power is connected to the LCD module (and the custom characters start up in a random state when the LCD module first gets power). A unique feature concerning the custom characters is that if you modify them while one or more of them is being displayed, they will be updated immediately, making possible simple animations.

There are a couple of things to watch out for that you should be aware of when using these LCD modules. Firstly, HD44780 LCD modules normally initialize themselves when powered up with the display turned off. However, under certain conditions the LCD modules's initialization may fail so it's always best to manually initialize the LCD module using its built-in instructions. It's a good idea to turn on the flashing cursor as a simple test to make sure the LCD module is working.

Another thing to watch out for is that the LCD module can be powered through a computer or microcontroller's I/O without power connected to the LCD module's power supply connections. Not only can this harm the LCD module but it also causes the LCD module to retain its current state (for e.g., what it is displaying). If you want to remove power from the LCD module while keeping the I/O connected then set the I/O to logic 0 before removing the power. The reason you may want to do this is to reset the LCD without turning off the computer or microcontroller.

You can learn more about the HD44780 LCD module by viewing this link HDD4780 datasheet and by reading the HD44780 projects on this page.

This page will also cover LCD modules that use a controller other than the HD44780.

Raspberry Pi to HD44780 LCD module

The Raspberry Pi is a great little computer which can be connected to a HD44780 compatible LCD module using its GPIO as a way of outputting useful information, such as the date and time or the current temperature. In this example project I've used the LCD module in 4-bit mode so that only 6 GPIO signal connections are needed. The code initialises the LCD module and displays 'Hello world!' on the first line along with the flashing cursor and has been tested on a Raspberry Pi B+ (but should work on other models).

The set-up is as follows (note: for the Raspberry Pi, board numbering has been used):

LCD pin 1 (Vss) to Rasp GND

LCD pin 2 (VDD) to Rasp 5V

LCD pin 3 (Vo) to 10K variable resistor centre; remaining connections to GND and 5V respectively.

LCD pin 4 (RS) to Rasp pin 26 with 10K pull-up resistor to Rasp 5V

LCD pin 5 (R/W) to Rasp GND

LCD pin 6 (E) to Rasp pin 24 with 10K pull-up resistor to Rasp 5V

LCD pin 7 to 10 (D0 to D3) to Rasp GND

LCD pin 11 (D4) to Rasp pin 12 with 10K pull-up resistor to Rasp 5V

LCD pin 12 (D5) to Rasp pin 16 with 10K pull-up resistor to Rasp 5V

LCD pin 13 (D6) to Rasp pin 18 with 10K pull-up resistor to Rasp 5V

LCD pin 14 (D7) to Rasp pin 22 with 10K pull-up resistor to Rasp 5V

It's a good idea if possible to put the LCD module into breadboard along with the other components and then connect the Raspberry Pi GPIO pins to the breadboard using cables.

The Python code for this project can be found at the bottom of this page and is called gpio_hd44780_test.py. Since it uses GPIO it must be run from the terminal using the command sudo python gpio_hd44780_test.py. In about a second 'Hello world!' should appear on the LCD along with the flashing cursor, if not check your connections and try adjusting the LCD contrast using the variable resistor.

Normally when interfacing with an LCD module such as the HD44780 we have to wait some time (well, seconds perhaps) for the LCD to start up. But since we are using the Raspberry Pi by the time the computer boots the LCD module would have started up and be ready to use. In addition, as Python is quite slow (as it uses an interpreter) we can get away with having less delays. In fact, the only delays in the program are when toggling the LCD enable pin.

In case the LCD's built-in initialisation failed the Python code issues a function set command twice and then write further settings such as to put the LCD module to 4 bit mode. The function LCD_send_instr() is what writes a command to the LCD taking an 8-bit value as input before calling function LCD_send_data() which sends the byte as two lots of 4-bit data. Similar, function LCD_send_char() displays a single character on the LCD module but internally calls LCD_send_data(). The only difference is that LCD_send_instr() takes RS low to put the LCD into instruction mode whereas LCD_send_char() takes RS high to put the LCD into character mode. Lastly, function LCD_send_str() takes a string as input and outputs each character in turn by calling LCD_send_char().

At the very end of the program all GPIO are taken low so that it is safe to remove the LCD module from the 5V supply should you need to such as to reset the LCD. If one of the GPIO pins were high and you disconnected the 5V line from the LCD module it will still be powered by the GPIO which could harm the LCD and will prevent it from being reset.


PC Parallel Port to Parallel Intelligent LCD Module

(Updated: 29/8/10)

This is great for testing out a so-called intelligent LCD which is nothing more than an LCD and dedicated controller chip on board, but does a lot of the hard work of working an LCD, especially one which is capable of at least limited graphics. This means you can interface the LCD module to a PC's parallel port with not too much difficulty and once you have the software done, I'm sure you'll find many uses for it. There are other options, such as using an USB connection, however, only the parallel port will be covered here as for most people it's the easiest to program and use (if your computer has a parallel port).

Intelligent LCD modules can be divided into two main types and they are those which can display only characters with very limited graphics (by creating your own characters) and the ones that can display graphics as well as text which can be of different sizes and styles. I'll be discussing the character only type below as they are the simplest to start with yet have many uses.

The LCD module that I used for this project came from a chucked out fax machine, and was made by Sharp with the code F2631XH-44 written on the circuit board. It uses the SEC C748B chip, compatible with the industry standard HD44780 so I was able to use it without any trouble. It is made up of two lines that are each eight characters long but instead of one line below the other they are side by side. In other words, when the two line mode is enabled for this LCD module it is actually a single line with 16 characters.

Another suitable intelligent LCD, which came from a security alarm (that was not needed!), is a Winstar WH1602B and has 16 connections, the extra two (15 and 16) are for the LED backlight. This LCD module has two lines that are 16 characters wide, one below the other. Obviously the more characters that can be displayed at once the better but the software will have to be adjusted accordingly. It can be a little tricky to use both lines as you have to tell the cursor to move to the start of the next line at a certain position and before that you have to enable two line mode (as already explained may be just an extension of the single line).

Although these LCD modules have an 8-bit interface which is ideal for your average CPU, microprocessor and parallel port the LCD can be be forced into 4-bit mode. In 'nibble' mode you have to send one half of the byte after another which may seem a pain but the advantage is that you only need four connections (not including the other signals) instead of eight.

The circuit shown above illustrates the wiring needed for interfacing an LCD module to a typical PC parallel port. You can power the LCD module using a battery or batteries, or a wall adapter; the power supply's ground (0V) connection has to be connected to the parallel port's ground signal. The LCD can run off lower than 5V (a 4.5V battery is very handy) but you will need to use pull up resistors for D0-D7 and possibly the other connections to ensure that the LCD Module gets the right voltage levels for logic 1 and 0. Actually, the LCD and the controller chip that's part of the module can work down to a tiny voltage, I've noticed that amazingly it can get power from the parallel port's signals (though the characters will be very faint). This means that you need to remove the LCD module from the parallel port (just remove the ground connection) as well as the power supply to completely turn the LCD off.

When power is applied to the intelligent LCD it starts off with the display blanked (though commands can still be sent to it such as to put it into two-line mode) but you may still see a number of blocks. If you do, then you'll need to adjust the contrast (VR1) until the blocks vanish, but not too much. The variable resistor can be replaced with a fixed reistor, as soon as you have found the right setting you can measure the variable resistor's resistance with a multimeter (when the power is off) on the ohms range to determine the value to use for the fixed resistor. Or you can use a preset variable resistor which can be adjusted once; please be aware that two-line mode requires a different level of contrast compared to just one line.

It is a good idea when testing an LCD module for the first time to set up the hardware using breadboard. Some intelligent LCDs have a ribbon cable connected to them others have a row of 'legs' which can more easily be inserted into breadboard.

When the hardware has been set up, you'll need to write the software to control the LCD, an example program I wrote in C++ follows. I used a Borland compiler which had no parallel port access functions so I used direct access by incorporating a couple of lines of assembly language amongst the C++ coding. With modern operating systems this is not a good idea and will not work with Windows 2000 or higher. Therefore you may want to explore better alternatives for accessing your computer's parallel port, on your PC (for Windows 2000 and above you can download software to grant you access to the parallel port or you could use a special driver).

//PC parallel port to LCD module

//By James S.

//Uses LPT1

const dataPort=0x378; //LPT1 data port

const statusPort=0x379; //LPT1 status port

const controlPort=0x37A; //LPT1 control port

const dataPortDir=0x20; //LPT1 data port I/O mode

const ctrlINIT=0x04; //LPT1 control port INIT o/p

const ctrlSTROBE=0x01; //LPT1 control port STROBE o/p

const LCDclr=0x01; //Clear LCD command

const LCDonULBlinkCur=0x0f; //LCD on with cursor underline and blink command

const LCDoff=0x08; //LCD off command

bool LCDon; //Is the LCD on or off?

void writeCommand(byte data); //Write command byte to LCD

void writeChar(byte data); //Write character to LCD

void writeCtrlPort(byte data); //Write byte to LPT1 control port

void writeDataPort(byte data); //Write byte to LPT1 data port

__fastcall TMainForm::TMainForm(TComponent* Owner):TForm(Owner)

{

LCDon=false; //Assumed LCD is off

}

void __fastcall TMainForm::LCDOnOffClick(TObject* Sender)

{

LCDon=!LCDon; //Toggle LCD on/off

if (LCDon) { //LCD to be turned on

writeCommand(LCDonULBlinkCur); //Turn on LCD

LCDOnOff->Caption="Turn LCD off";

}

else { //LCD to be turned off

writeCommand(LCDoff); //Turn off LCD

LCDOnOff->Caption="Turn LCD on";

}

}

void writeCtrlPort(byte data) //Write byte to LPT1 control port

{

_DX=controlPort;

_AX=data;

__emit__(0xee);

}

void writeDataPort(byte data) //Write byte to LPT1 data port

{

_DX=dataPort;

_AX=data;

__emit__(0xee);

}

void writeCommand(byte data) //Send command to LCD

{

writeCtrlPort(0); //Set data port to o/p and LCD to command mode

writeDataPort(data); //Write command to data port

writeCtrlPort(ctrlSTROBE); //Take LCD enable low

writeCtrlPort(0); //Take LCD enable high

}

void writeChar(byte data) //Write character to LCD

{

writeCtrlPort(ctrlINIT); //Set data port to o/p and LCD to character mode

writeDataPort(data); //Write char to data port

writeCtrlPort(ctrlINIT|ctrlSTROBE); //Take LCD enable low

writeCtrlPort(ctrlINIT); //Take LCD enable high

}

void __fastcall TMainForm::sendCharClick(TObject* Sender)

{

//Send character to LCD

int byte=StrToIntDef(charIn->Text,0); //Get character code

writeChar(byte);

}

void __fastcall TMainForm::clearLCDClick(TObject* Sender)

{

writeCommand(LCDclr); //Clear LCD

}

The LCD module, above, can display up to 40 characters on its single line (take note that what looks like an underscore at the end of the website address on the display is actually the built-in cursor that can be enabled or disabled as needed). I had to solder a ribbon cable to the dual-in-line connections on the LCD module so getting the right order of odd and even rows was very important.

The intelligent LCD will miss commands or characters if received too fast; you can check the module's busy flag to see if it's ready to accept more data but it's simpler just to wait a specific amount of time between sending data. Reading the busy flag requires use of the LCD's R/W line and that would need an extra output from the computer you are using. It's better to keep the R/W input connected to 0V so it's always in write mode and wait a short amount of time before sending the next byte to the LCD.

Using a for loop to create a delay isn't a good idea since the length of delay would be different on a faster or slower computer. Instead, use a timer component or better, a delay function (such as the Sleep() Windows function).

Fax machine LCD Module

In an old fax machine I was taking apart I found an LCD module that was connected to a circuit board with a flat cable. Since the LCD module has 10 connections I guessed that it was probably HD44780 compatible, assuming that there were only 4 data bits. The LCD module has these markings:

UC161938

TNAR3AA

970715

Which led me to the following site which confirmed what I had suspected:

http://translate.google.co.uk/translate?hl=en&sl=lt&u=http://megejas.lt/index.php/elektra-elektronika/projektai/90-helloworld&prev=/search%3Fq%3Duc161938%26biw%3D1366%26bih%3D705

Using my PIC LCD Character Module Driver (which you can find on the Interfacing page) the LCD module worked fine. The big problem was the flat cable connected to the LCD module as I was unable to solder wires to a suitable connector. So, what I did was solder wires direct to the LCD module where the flat cable is connected.

The only difference to a standard HD44780 LCD modules is that only triangle characters can be displayed on the second line (they were used by the fax machine to point to different things).

VTECH Precomputer 2000 LCD Module

When I bought a VTECH Precomputer 2000 cheaply at a carboot sale for the parts I assumed it used a standard HD44780 LCD character module. Well, that turned out to be almost true but it took a bit of unscrewing to find out exactly how the display works.

The Precomputer 2000 uses a Z80 (8-bit) CPU which shows how popular the CPU has been considering the Z80 came out in 1976 and the Precomputer 2000 was released in 1993. The CPU is on the main board along with a chip marked LH532HEE which is possibly a ROM containing the program code for the Z80 and a chip with the code S2564RL-100 which could be RAM. There is also an unknown chip on its own board soldered upside down to the main board; it looks to be a 'blob' IC.

As for the LCD module, it is connected to the main board with two ribbon cables, one 8 way and the other 4 way. In addition, there is a red and black wire going from the power supply board (to which the battery supply and external power supply are connected) to the LCD module. Turns out the red and black wires are not the power supply but instead are shorted together by the contrast switch to select the two levels of contrast. This is done using two resistors on board the LCD module.

I had to unscrew the LCD module to separate the actual LCD from its controller board to get further answers and I found as well as there being a 'blob' chip there was also a SED1278F chip in a square package. The SED1278F is a dot matrix LCD controller driver very similar to the HD44780. As soon as I had a datasheet for the SED1278F I was able to identify the pin-out of the two buses. If you do take this LCD module apart and put it back together and when you test it you find that the characters aren't being displayed correctly then you have the LCD the wrong way round. So you will need to unscrew it, turn the LCD round and screw it back-all while the power is off, of course.

Starting with the 4 way bus, looking from the back, connection 1 is closest to the capacitor (there are no markings on the display board so I had to do my own numbering). The connections are as follows:

1 VDD

2 RS

3 GND

4 E

VDD is the supply voltage, 5V, and RS is the 'Register Select' input; logic 0 tells the LCD module you are sending an instruction and logic 1 is for sending data (characters). On the LCD module the R/W input to the SED1278F is connected to GND so the LCD module is always in write mode. Lastly, the E input is like a clock signal which forces the LCD module to execute an instruction or display a character when the E signal goes from a high to low state.

As for the 8 way data bus, I regard connection 1 as being closest to the group of unused connections on the back of the LCD module. This is the data bus which are the 8 inputs used to transfer instructions or character data to the LCD module.

1 DB7

2 DB6

3 DB5

4 DB4

5 DB3

6 DB2

7 DB1

8 DB0

I tested the LCD module by using breadboard and first sending the code to turn the display on and then to output a character. That worked and I found it is can display 20 characters on 2 lines so it will be a very useful display.

An example of a Serial LCD Module

I took a bit of a risk when I bought online an LCD module that I knew nothing about but eventually I got it working. It's a sixteen 7-segment LCD module with what look like 16 commas, and has the numbers 35 209 16170 written on the board. The only clue to getting it working (and I do like a challenge!) was the two PCF2111T chips which so happen to be LCD drivers.

Having found a pdf data sheet about the LCD driver ICs I did a pinout of the LCD module connector which has no indication of the pin markings but the leads are all grey except for one red one.

(Red) Data input line enable DLEN (CBUS) 1

(Grey) Data input line enable DLEN (CBUS) 2

(Grey) Data input DATA (CBUS) 1 and 2

(Grey) Clock burst input CLB (CBUS) 1 and 2

(Grey) VDD (2.25V to 6.0V)

(Grey) VSS (0V)

Each LCD driver chip controls half of the display (i.e. eight 7 segments and eight commas) and just to complicate things a bit more you can only alter half of those segments at once. So, to update the entire display you would need to write to the LCD module four times. This is because one LCD driver has two latches and you can only select one or the other:

* The A latch is responsible for the segments a, b, c and g.

* The B latch is responsible for the segments d, e, f and comma.

This was assuming the usual labelling and that I had the display the right way up, but which way is the right way up depends whether you want the commas to be commas.

This LCD module isn't intelligent and requires a lot of hard work from the user; basically you send a pattern of bits using the DATA signal which, as well as turning on or off the segments, selects which latch you want to use that data. The DATA and CLB (clock) are common to both of the LCD driver chips, which of the two chips you want to receive the data is selected by taking either of the DLEN signals high.

I tested the LCD module using the parallel port of my PC which may seem a strange choice but it was the most easy for me to use. I powered the display using a 5V PSU and connected the power, display and parallel port to a common ground (0V) connection. Using C++ to 'talk' to the LCD module, the reason it took me so long to get the display working was the problem of sending the correct sequence of bits. The PCF2111T chips are 34-bit but, as suggested in the data sheet, you have to send a leading zero which is one of the conditions the LCD driver chips look out for to know when the start of a sequence has begun.

When I had finished the code I was impressed that the entire display updated very fast as I used no delays which I would have had to had it been an intelligent LCD. But it was just as well the LCD module responded as fast as it did as I had to do a lot of converting and such to send parallel data in serial form.

Since you can only display a limited number of characters using seven segments but I wanted to be able to use the String class, I tetsed each character in the string and if it was one that could be displayed on the LCD I converted the ASCII value to the correct index. This index was used to look up which segments are lit or not lit for each character stored in the array.

For the characters that couldn't be displayed on the LCD I instead sent a space character to be safe but you could use another character if you wanted to. And if there were less than 16 characters in the string, spaces were padded onto the end so that the whole display was always updated.

If you want to show numbers on the LCD it's a simple matter of converting the number variable into String form which even takes care of the minus sign for negative values (as soon as I had added the minus sign in 7-segment form). Originally, as another test, I wanted to display a sum on the LCD and the result but a plus symbol isn't possible using 7-segment so I did a minus problem instead.

It's a good idea to put the coding into a class so that any program that wants to use the LCD module can and won't have to worry about knowing how to use the LCD; you'll also save on code as well be reusing it. You will need only provide public functions to allow the program to display data on the LCD module, keeping everything else private.

Graphic LCD modules

When we talk of graphic LCD modules we are referring to LCD modules that are not limited to just displaying characters using predefined smaller graphical objects such as segments. Instead graphic LCD modules use a matrix of dots which can be turned on or off, forming patterns which can represent characters using a range of fonts and images such as symbols or more complex designs. Note that although HD44780 LCD modules use a dot matrix LCD it is the module's controller (and to an extent, the limited LCD resolution) that limits the display to only showing text including symbols.

Novametrix 515C Graphic LCD

I had acquired 2 Novametrix 515C pulse oximeter units that were slightly different versions but both contained a graphic LCD module which has the identification 'DMF-50427N' on them and is made by Optrex. The DMF-50427N displays 128 x 64 pixels with each pixel being black and the interface is a parallel type ideal to be connected to a simple CPU or microcontroller although the LCD module only understands a few commands. You can read more about the oximeter at:

https://sites.google.com/site/jamesskingdom/Home/electronics-by-james-s/inside-manufactured-products#TOC-Novametrix-515C-Pulse-Oximeter

The LCD module uses 2 hd61202 column (segment) drivers designed for dot matrix displays and for interfacing with a microcontroller or something similar. For the datasheet please click here:

https://www.datsi.fi.upm.es/docencia/Micro_C/lcd/hd61202u.pdf

There is also a datasheet for the LCD module (which I discovered after testing the LCD):

http://www.datasheetcatalog.com/datasheets_pdf/D/M/F/-/DMF-50427NYJ-SLY.shtml

I used an Arduino Uno to test the LCD and the sketch I wrote you can find for download at the bottom of this page as 'Novametrix_515C_LCD_Test.ino'. Download it, write the sketch to your Uno and then with the power off connect it to the LCD as follows (LCD pins numbers are included):

Arduino LCD

5V VDD (1)

GND VSS (2)

D2 D0 (4)

D3 D1 (5)

D4 D2 (6)

D5 D3 (7)

D6 D4 (8)

D7 D5 (9)

D8 D6 (10)

D9 D7 (11)

D10 D/I (16)

D11 /CS1 (12)

D12 /CS2 (13)

D13 /RST (14)

A0 E (17)

GND R/W (15)

GND FGND (18)

Note that there are 2 chip select inputs (/CS1 and /CS2) because the 2 hd61202 drivers handle 64 x 64 pixels each so to update the display we must enable one of the driver chips at a time and then talk to it. With the /RST input, however, that is common to both chips and ignores the state of /CS1 and /CS2.

I have not used the LED backlight but to use it you will need to connect LED+ (19) to 5V via suitable limiting resistor (datasheet specifies maximum LED current of 60mA and forward voltage of 4.1V @ 30mA) and LED- (20) to GND. What you must do is supply the LCD contrast voltage to LCD VLC pin 3 which can be from approx. -7V to -9V, the voltage of which will adjust the LCD contrast. To generate the negative voltage you can use my power supply circuit from:

https://sites.google.com/site/jamesskingdom/Home/electronics-by-james-s/led-and-lcd-modules#TOC-Sonos-LQ035Q7DB03F-TFT-LCD

By adjusting PR1 in the circuit linked above you can alter the negative voltage and thus the contrast but be sure to use a multimeter to make sure the voltage is kept within -7V to -9V.

When you power the Arduino you should see on the LCD 4 rectangles of different sizes, if not, double check your connections and try adjusting the contrast voltage.

Now looking at the Arduino sketch if you go to the setup function you will see that the I/O is set up for communicating with the LCD; because the LCD is always in write mode (R/W is connected to GND) we never have to change the direction of the data pins (D0 to D7) but it is useful to be able to read information from the LCD such as whether it is busy executing an instruction (because of the use of digitalWrite() and its slowness it is not necessary to check if the LCD is still executing an instruction). After we have set up the I/O and set the default state we call LCD_rst() which simply takes RST low for 1ms and then returns it high, waiting another 1ms before exiting (which isn't really necessary).

Back to setup() and we call LCD_send_command() twice to turn on both displays as after reset the LCD is in the off state. LCD_send_command() takes as inputs the instruction value to write to the LCD and the Arduino pin number of the LCD chip select I/O pin and then calls LCD_send_data() passing the input values and 'false' so that LCD_send_data() knows that an instruction is being sent rather than display data. Function LCD_send_data() first sets LCD D/I to instruction mode (low) then sets D2 to D9 to the instruction value by writing to the Arduino ports directly so that we don't need to use individual digitalWrite() to set each bit. Lastly, E (enable) goes low, the relevant driver chip is enabled by taking the input low and then both E and the chip select inputs go back high.

Returning to setup() again the next function we call is LCD_clr_disp() which clears the display by writing zeros to the entire display memory but the routine simply calls function LCD_write_all_disp() and passes zero as its input. Function LCD_write_all_disp() sets up a loop from 0 to 7 (LCD_max_X = 8) and at the start of the loop we set the X address for both driver chips. Horizontally we can access each pixel across by setting the Y address (which should really be called 'X') and with each write to display memory Y will increase by 1 but vertically the X address (would be better called 'Y') must be set which selects from 1 of 8 pages (using values 0 to 7). Each page is 64 x 8 pixels and writing a byte will set 8 pixels vertically. I'll show it visually as the first page only:

Y address

0 1 2 3 4 5 6 7 8 9 ... 63

1

2

X = 0 3

4

5

6

7

For example, if you wanted to turn on the very first pixel you would set the Y address to 0, the X page number to 0 and then output 0x01. Remember that while Y increases by 1 with every write to display memory the X value never changes automatically and must be set yourself.

In LCD_write_all_disp() we set the Y address to 0 before setting up another loop which goes from 0 to 63 so we can write to all 64 pixels horizontally for both driver chips (which will cover the 128 pixels across in total) by calling LCD_send_disp_data() which calls LCD_send_data() but tells it we want to write to display data by passing 'true' for the second parameter.

Also in setup(), after LCD_clr_disp(), is the commented out call to function LCD_fill_disp() (un-comment to enable) which like LCD_clr_disp() calls LCD_write_all_disp() but gives it the value 0xFF so all pixels are written to. Toward the end of setup() there are 4 calls to LCD_draw_rect() to draw rectangles of different sizes and in turn LCD_draw_rect() calls LCD_draw_horiz_line() and LCD_draw_vert_line() to draw the 4 sides of the rectangle. Not that I've included the horizontal length (horiz_len) and vertical length (vert_len) with the starting pixel (top-left point) rather than the length being added on resulting in an extra pixel as I've seen with saw drawing routines. In LCD_draw_horiz_line() we first do some basic checks to make sure the X, Y and len values are valid and then we calculate LCD_disp_value so we can effectively move the line vertically into 1 of the 8 'slots'. To get the X address it is just a matter of dividing the Y value by 8 and then we can set that for both driver chips regardless of which one will actually be used. Next, we test to see if the line starts on the left side of the display (chip 1) or the right side (chip 2) by comparing the X start value (X) and then remembering the result by updating variable update_LCD_left. With that out of the way we create a loop to count from 0 to length (len) - 1 so that we can write each pixel of the line horizontally. However, before we update the display we must check if we have moved from the left side of the display to the write by checking if the current coordinate X position (X_pos) if 64 or higher and if so then update_LCD_left is updated. Then we can call LCD_send_disp_data() for the appropriate driver chip with the previously calculated display value in LCD_disp_value.

On to function LCD_draw_vert_line() in which we check X, Y and len are valid and then determine whether we need to update the left or right side of the display and set variable update_LCD_left accordingly. We do a simple calculation to set x_count_limit to the number of line segments, that is, the number of X pages to update. Next we go into a loop to update each X page and at the beginning of the loop we calculate the current Y coordinate value (the pixel at the top of the X page) and then set the X page address value to the coordinate Y position/8 for whichever driver chip we are updating. To actually draw the line we set LCD_disp_value to 0xFF which is then shifted left or right to form the start or end of the line or if it's a middle segment then 0xFF is outputted untouched as the line segment will be a completed 8 bits. The last thing to do is set the Y address as each display write will increase Y by 1 yet we are drawing a vertical line whose X coordinate value will not change, and we also send the display data by calling LCD_send_disp_data().

A big issue with the line and rectangle drawing routines is that they will overwrite any other display data close by and if a rectangle is drawn with horizontal or vertical length of 8 or less then likely it won't display correctly as one of the lines drawn will be in the same X page. A workaround to fix the problem would either be to modify the circuit so that the LCD's display memory can be read or to maintain a display buffer on the Arduino, so that new writes to the display can be combined with whatever is already in display memory. The sketch is only test code and it could do we more boundary checking but at least it shows off a little of what the display can do.

WGM-12232M Graphic LCD Module

This graphic module has a resolution of 122x32 black pixels and features a green backlight that can be enabled or disabled. Two NJU6450 bit map LCD drivers update the LCD, with the left hand of the display controlled by one NJU6450 and the other side handled by the other NJU6450. So, in other words, one NJU6450 controls a total of 61x32 pixels. Each of the two sections of the display are divided up into columns, which go across horizontally, and pages which are vertically rectangular divisions. So, to access a particular part of the display you need to specify both the column and page value and select one of the two chips. Writing data to the display RAM sets pixels vertically, and automatically increases the column value so you can quickly update the display (however, you will need to reset the value after it reaches its maximum value).

The module has 20 connections and its pinout is shown below:

1 VDD (Power supply +5V)

2 GND

3 VO (Contrast adjust 0 to -5V)

4 /RST (Active low reset)

5 CS1 Chip select 1

6 CS2 Chip select 2

7 R/W (0 to write, 1 to read)

8 NC (No connection)

9 A0 (0 command, 1 data)

10 to 17 DB0 to DB7 (8-bit data bus)

18 VBC Backlight enable switch (0 off, 1 on)

19 to 20 GND

For contrast you only need some kind of potential divider (which could be a variable resistor) between the power supply connections with the middle tapping wired to VO. If you only want to write to the display you can tie R/W to GND, which saves an I/O pin on your microcontroller (or whatever system you are using). Reading from the display is, nonetheless, useful for getting the contents of the display RAM and for checking the busy flag to see whether the module is ready to accept another command or display data. If you don't check the busy flag then you will need to make sure you don't update the module too quickly. I have tested the display with the Arduino Uno and by using a 1ms delay the display updated very quickly (almost instant).

SainSmart 3.2" TFT LCD

This 3.2" display module (TFT_320QVT) features a TFT LCD display with white LED backlight, touch panel, and full size SD slot (with series resistors for simple voltage level conversion) on a PCB board with a male 40 pin header. The SD card was probably intended for storing images and animations to be used with the LCD; should be compatible with standard Arduino SD card library. Also there is provision for a flash memory chip with connections broken out to the header but the chip is missing.

As for the LCD controller, the chip is the SSD1289 which supports 8/16 bit interface and a resolution of 320 * 240 with 262K colours (limited to 65K by hardware connections) and has Integrated power, gate and source driver with DRAM. The chip runs off 1.4V to 3.6V and its interface appears not to be 5V tolerant; no input pin should be taken higher than 3.6V. The SSD1289 supports 4 different types of interface: 6800-series, 8080-series, 4-line SPI, 3-line SPI but is forced into 8080 mode by on-board connections.

The touch controller is supposed to be the ADS7843 but the actual chip used is the XPT2046 which is compatible with ADS7843. Nevertheless, it is 4 wire resistive touch screen controller, with up to 125KHz conversion rate, has a serial interface and runs off 2.7V to 5V.

The LCD module pinout is as follows:

1 GND 21 DB0

2 VCC (3V) 22 DB1

3 NC 23 DB2

4 RS 24 DB3

5 WR 25 DB4

6 RD 26 DB5

7 DB8 27 DB6

8 DB9 28 DB7

9 DB10 29 D_CLK

10 DB11 30 D_CS

11 DB12 31 D_DIN

12 DB13 32 D_BUSY

13 DB14 33 D_OUT

14 DB15 34 D_PENIRQ

15 CS 35 SD_OUT (F_S1)

16 F_CS 36 SD_SCK (F_SCK)

17 REST 37 SD_DIN (F_S0)

18 NC 38 SD_CS

19 LED_A 39 F_WP

20 NC 40 F_HOLD

For testing the LCD module I used an Arduino Mega and connected the LCD in this way:

Note: I used 20K resistors for all I/O between the Arduino and the LCD module to convert the 5V logic level used by the Arduino to the 3.3V logic level used by LCD module.

LCD specific connections:

(LCD module pin) (Arduino Mega)

RS(4)->I/O 38

WR(5)->I/O 39

CS(15)->I/O 40

REST(17)->I/O 41

(LCD module pin) (Arduino Mega)

D0(21)->I/O 37

D1(22)->I/O 36

D2(23)->I/O 35

D3(24)->I/O 34

D4(25)->I/O 33

D5(26)->I/O 32

D6(27)->I/O 31

D7(28)->I/O 30

D8(7)->I/O 22

D9(8)->I/O 23

D10(9)->I/O 24

D11(10)->I/O 25

D12(11)->I/O 26

D13(12)->I/O 27

D14(13)->I/O 28

D15(14)->I/O 29

Touch screen specific connections:

(LCD module pin) (Arduino Mega)

D_CLK(29)->I/O 6

D_CS(30)->I/O 5

D_DIN(31)->I/O 4

D_OUT(33)->I/O 3

D_Penirq(34)->I/O 2

Must take D_Penirq to VCC (3.3V) via 2.2K resistor otherwise IRQ won't work.

Misc. connections:

(LCD module pin) (Arduino Mega)

RD(6)->3.3V

VCC(2)->3.3V

GND(1)->GND

LED_A(19)->3.3V (you can use a variable resistor to adjust the brightness)

The Arduino code is attached to the bottom of this page and has the filename 'SainSmart_TFT_3_2.ino'. It is based on code by an unknown author. The display will start up as a garbled mess-this should be the graphics RAM starting up with random values. The screen is slowly cleared first to blue then yellow; once the screen is yellow can use the touch screen to draw in blue. Note that the blue and yellow painting is slow; this is because digitalWrite is slow and each data bit is written in a loop- it would be faster to write directly to the Arduino's ports (but more difficult to understand for a beginner).

Some notes about the LCD module:

The graphics start address must be set before writing to graphics memory. Writing to graphics memory increases/decreases the address counter by 1.

You can set up a window area to limit where graphics data can be written to.

Graphics display data RAM (GDDRAM): bitmap 240x320x18/8 (518,400 bytes). Four pages of display data forms block which can be scrolled.

If you do not have the backlight on you won't see anything on the display.

A lesson in interfacing the SainSmart TFT with other hardware.

Having had tested the LCD with an Arduino I went about porting the code to a Nios processor, which is a 'soft' processor that runs on an FPGA (a chip consisting of a great number of programmable logic blocks). As Arduino code is C-like and the Nios can be programmed in C you would think that the task shouldn't have been too difficult. However, I ended up spending a lot of time before I got the TFT working with the FPGA.

At first I couldn't get the LCD to do anything; it would just stay as a blank, white screen and by measuring the current draw I could see it was in standby mode. I used my logic analyser and found that the CS pin for the LCD was not working and once I fixed that I was able to initialise the LCD-it would turn on with random pixels and the power consumption went up, showing it was out of sleep mode. However, I couldn't make any progress after that; no matter what I did I couldn't write anything to the display.

I thought that perhaps the display wasn't being initialised correctly or I wasn't setting the address values right. I spent a lot of time stepping through the code to check the data values were being outputted correctly. What I found was that CS was wrongly being taken high shortly after it went low, so effectively the LCD was ignoring the values I was giving it.

The fault was in a function that was supposed to preserve the control signals (WR, CS, etc.) while outputting the LCD data values (both signal and data share the same communications bus with the LCD in my FPGA design). To keep the control signal values I performed a bitwise AND with the port and a bit mask, removing the previous data values. I then did a bitwise OR with the result and the new LCD data values before writing the result to the LCD bus. But this caused the control signal values to become corrupted as by looking at the assembly listing I could see that the data values were being treated as 32-bit when they were actually 16-bit (so the upper bits would be set to 1's and then ORed with the signal values). The simple fix was to clear the upper data value bits to zero so that the bitwise OR would result in the control signals being combined with the LCD data value.

Later on, when I thought there were no more problems, I added some line drawing functions and tested them by drawing lines at different positions using a simple loop. Oddly the lines would not show up at certain parts of the screen, whether using horizontal or vertical lines. I suspected the problem was the address set function as that defines where on the screen graphics will appear. By debugging I could see that values loaded into int variables that then got passed to char variables via functions calls were corrupted (the change in variable type effectively extracted the lower 8 bits from a 16-bit value).. The fix was to change the char input parameters to unsigned char so that the values would only be treated as positive rather than possibly negative.

What this shows is it's often the simple things that slip us up which in this case was the CS signal and the handling of different variable types. Always check the basics, such as connections, and if you can measure something (such as current draw) to see if something is actually happening. Also, just because some code works on one type of hardware doesn't mean it will be easy to port; you need to understand how the target hardware handles different variable types.

Sonos LQ035Q7DB03F TFT LCD

I first encountered the LQ035Q7DB03F TFT LCD by Sharp when taking apart the Sonos CR100 controller of which you can read about at:

https://sites.google.com/site/jamesskingdom/Home/electronics-by-james-s/inside-manufactured-products#TOC-Sonos-controller-CR100

The LCD measures 3.5" diagonally, has a resolution of 240 X 320, and can select from a range of 262,144 colours for each pixel. As the LCD lacks an intelligent controller able to process commands the LCD must be driven low level. This is not impossible without a dedicated LCD controller, as shall be seen shortly, but keep in mind that the LCD has 2 0.5mm pitch FFC cables (50-way for the LCD interface and 5-way for the LED backlight).

For the LCD's datasheet please go to:

http://www.mouser.com/ds/2/365/LQ035Q7DB03F_SP_100505-180506.pdf

The datasheet seems to assume you know how to work a similar type LCD, which wasn't the case for me, but nonetheless it was very helpful in conjunction with using my logic analyzer to compare the LCD signals that were being generated by the Sonos controller. I knew the big hurdle in getting the LCD working before even wrestling with timing signals would be the two FFC cables. Fortunately I came across the RE918 (RS stock 897-1461) adapter board which is designed for a 50-way FFC connector having 0.5mm pitch on one side and 1mm pitch on the other. So it is a matter of soldering a suitable 50-way FFC connector to one side of the board and a couple of male headers to the other side to adapt from SMD to through-hole. As for the LED connector I just soldered by hand to a 5-way FFC connector a couple of wires.

For communicating with the LCD I used a Rasp Pi 3 and Python which although slow (maximum I/O toggle ~142KHz using standard GPIO library) Python comes as standard on the Rasp and is quicker to run than using C, which must be compiled. Going by the timing diagrams in the LCD datasheet I tried to recreate the signals even though the actual timings would be way off, using a logic analyser to check the I/O. When I ran the script I got a very faint blue band to the left of the screen even though I had all red inputs held high with green and blue inputs stuck at OV. I eventually realised I had accidentally put the GPIO pin controlling the LCD power save (PS) signal always high by connecting it to the 3.3V supply so I corrected that mistake (and checked that thankfully I hadn't harmed the GPIO pin). Now when I ran the script I got seemingly random coloured lines across the screen which would stay for some while even after the script was terminated (the image retains because the LCD needs a certain sequence when blanking the screen).

I moved onto using an FPGA Cyclone II development board so I could better generate the timings and with it I was successful in getting the LCD to work to the point that I could control individual pixels. Note that the LCD is designed to be used in portrait mode even though in the Sonos controller it was treated as if it were in landscape orientation, which will also be so for this test example.

As I wasn't able to find all the required LCD voltages on the Sonos controller power board I soldered up a circuit instead, of which you can view the diagram of as follows:

It is not the best circuit but something I was able to put together with the components I had at hand. Take note that pin numbers in round brackets for LCD/LED supply voltages are for the 50-way and 5-way connectors as appropriate.

The only input power supply is 5V which powers the Cyclone II board and feeds the DC-to-DC converter, TMA 0515D, which takes the 5V input and outputs both +15V and -15V at 35mA (the -10V for the LCD is at only .1mA). So the TMA 0515D produces the +15V for the LCD and the -15V is fed into a LM337L (IC1), a variable negative voltage regulator that can output 100mA max (note that the higher current LM337 IC's have a slightly different pinout). The regulator is adjusted to -10V using a variable resistor (PR1, 10-turn) while under such a load that .1mA flows: a 100R/1W resistor can be used. What you have to be careful about the TMA 0515D is its output voltage can be as high as 25V when under no or a very small load which in turn affects the LM337L which will require adjusting should the input voltage change and that is why the output voltage adjustment should be done with the required load connected. As for the 20V needed for the LED for the LCD backlight (datasheet says the voltage should be around 21.6V) I used a power boost module, PM-6009DU, which takes 5V in and outputs through a 100 resistor (R2) about 10mA. You will need to adjust the on-board pot to the correct voltage which would be best to do first without the LCD backlight connected to be on the safe side.

With the power supply sorted I created a design in Quartus to display a test pattern by producing the necessary LCD signals going by what was in the datasheet and the signals I had captured from the Sonos controller. Although I was successful I did notice flickering (which could be because of the fairly long wires between the LCD connectors and the FPGA board or a power supply issue) and the viewing angle was somewhat worse than it should be (this may be a timing issue). The project (created using Quartus 13.0sp1) is attached at the bottom of this page and is called 'Sonos_CR100_LCD'; download the file, extract it and open it in Quartus.

The power supply connections to the LCD including the LED backlight have been shown in the circuit above but the following LCD pins must go to GND:

AGND (2, 8, 40, 41, 44, 45, 46, 47, 48, 49 and 50).

DGND (33).

Although I've connected both the analogue and digital GND connections together it would be better to add some decoupling between them.

In addition, LCD pin 5 is taken to GND which selects normal vertical scanning (1 to 320). LBR (37) goes to 3.3V which chooses normal horizontal scanning (1 to 240) and puts SPL (13) as an input and SPR (38) as an output (unused in this test). VCOM, pins 11 & 12, are held at 3.3V as that is what is done in the Sonos controller. Note that COM (43) is an unused output in this test example.

Let's now look at the pin connections between the LCD and Cyclone II board:

LCD pin number Cyclone II I/O number

MOD 3,4 25 Use 1K pull-down resistor to GND.

SPS 6 24

CLS 7 31

SPL 13 32

R3 17 99

R4 18 97

R5 19 96

G3 23 103

G4 24 101

G5 25 100

B3 29 94

B4 30 93

B5 31 92

PS 34 27 Use 1K pull-up resistor to 3.3V.

LP 35 28

DCLK 36 30

REV 42 26

The LCD MOD pins (3 & 4) have a 1K pull-down resistor to GND as the MOD inputs need to be low when the LCD is powered up but FPGA I/O default to inputs and will effectively act as an active high output. Similar, a 1K pull-up resistor needs to be connected between PS (34) and 3.3V to make sure default state is active high (perhaps unnecessary).

To better show the relationship between the LCD signals I have included a capture from the Cyclone II:

This is in comparison to the capture from the Sonos controller which I tried to copy:

Note that the Sonos LCD controller has periods in which DCLK remains low for 4.7us.

Next, let's look at the schematic design for the Cyclone II board that I put together in Quartus:

Note that only R3-R5, G3-G5 and B3-B5 have been used; the other colour inputs are taken to GND as follows:

R0 (14), R1 (15), R2 (16), G0 (20), G1 (21), G2 (22), B0 (26), B1 (27), and B2 (28). This was done to save on I/O (that is, less to connect up rather than being short on I/O) and because the lower colour values have less of an affect when used in the test pattern that I created.

A PLL (inst) have been used to take the clock input at I/O 17 and generate a 10MHz clock signal which is then fed into a counter (inst9) so that we can obtain a number of slower clocks (counter[0] to counter[31]), which includes DCLK (I/O 30) which is the pixel clock, running at 5MHz. To derive LP (I/O 28) we AND together a number of outputs from counter inst9 which has the effect that the signal is high for a very short time (187ns) and repeats at a slow rate. Signal SPL is derived from LP as we just need to delay LP and increase its on time slightly (high for 375ns) and this is done using shift register inst6 to delay the LP signal and OR gate inst8 to combine outputs 2 and 3 of the shift register. To generate CLS, we use a combination of shift register inst7 whith counter[8] shifted in and the logical combination of gates inst4, inst12 and inst10 which gives a much longer high period (38.4us) than low. Next, PS is simply an inversion of counter[8] which has a frequency of 19.5KHz, with REV being an inversion of PS. MOD is very simple, it just needs to start low and then go high and stay high after more than double the vertical period, which I've determined 105ms is about right. This was done by using a counter, inst18, clocked by counter[19] having a frequency of 9.5Hz with the counter going from 0 to 1 and then disabling itself. Signal SPS is derived by the use of an AND gate, inst5, which ANDs the clocks of counter[10] to counter[17] which is then inverted by inst21, producing a pulse that is low for a short time (102us). Lastly, the colour values for (R3-R5, G3-G5 and B3-B5) come from a 9-bit ROM, inst3, whose address is chosen by counter inst15. The counter is clocked by counter[10], having a frequency of 4.8KHz and is reset by SPS_out_1 (a non-inverted version of SPS) as SPS roughly marks when the LCD needs to be re-drawn from the first pixel. The faster the clock to the counter the thinner the coloured lines that are drawn (counter[11] for 8 lines thick, for e.g.). The clock to ROM inst3 should be faster than anything else that accesses it so that the correct values are retrieved but I found that if I used a faster clock than counter inst15 the colour patterns shifted slightly.

Here is a copy of the ROM values (all hex):

000 001 002 003 004 005 006 007

000 008 010 018 020 028 030 038

000 040 080 0C0 100 140 180 1C0

000 009 012 01B 024 02D 036 03F

000 041 082 0C3 104 145 186 1C7

000 048 090 0D8 120 168 1B0 1F8

000 049 092 0DB 124 16D 1B6 1FF

000 007 038 1C0 03F 1C7 1F8 1FF

Bits 0-2 are for red; 3-5 for green; 6-8 for blue. So, 0x000 is black, 0x007 is full red, 0x038 is full green, 0x1C0 is full blue, and so on.

If all is good, when you program the design to the Cyclone II board you should see displayed a number of coloured bands each 4 lines thick. The following colours are shown from black to full colour:

Red

Green

Blue

Yellow

Magenta

Cyan

White

Then it displays the above colours again (starting with black) but as a single band each at full brightness aside from black. After that the pattern repeats again showing black to red and then black to green before reaching the end of the screen. As we are using a 64-byte ROM and each colour band is 4 lines thick I ran out of entries in the ROM so I just let the whole pattern repeat itself.

Originally I had 8 lines per colour band but had the issue of the first band starting half-way 'off screen'. Although there is a non-display period I couldn't get the colour bands to start correctly no matter what I tried and changing the ROM clock (as mentioned above) didn't help. That's why I switched to 4 lines per colour band

I later revisited the project after doing some reading on VCOM and looking over again the LCD datasheet as to the little it says about VCOM. I was able to improve the brightness of the LCD, as well as the viewing angle (VCOM biases the viewing angle) and reduce flicker, by making use of a 10K variable resistor with either end connected to GND and +3.3V, and with the middle connection to both VCOM inputs. I adjusted the variable resistor until I was happy with the display colours; I found it best with 1.5V measured across +3.3V and VCOM. Note that the LCD datasheet mentions that VCOM needs to be adjusted for each display. This shows how important it is to read the datasheet carefully even when you have sample data from another system - a logic analyser reveals a lot but it can be easy to assume voltage levels.

MGLS-24064 Graphic LCD Module

I came across the MGLS-24064 graphic LCD module from VL in the Dinamap Compact T which you can read about at:

Dinamap Compact T

The LCD is a monochrome STN positive transflective yellow display with a resolution of 240 x 64 and has a yellow-green LED backlight. Based around the T6963C LCD controller, the display module also incorporates T6A39 segment drivers and T6A40 common drivers as well as 8KB of display RAM. You can find more information about the display by looking through the datasheet:

http://www.farnell.com/datasheets/86183.pdf

To test the LCD I came up with a circuit using an Arduino Uno to communicate with the display module, which you can view as follows:

The LCD has a 20-way connector for communicating with some form of processor type device, which in this case is an Arduino Uno. The Uno provides the 5V that the LCD needs for its supply logic via VDD and VSS, and the 5V is also converted to +15V and -15V by a TMA0515D DC-to-DC converter. The +15V is unused but the -15V is regulated to a lower voltage by the negative voltage regulator, a LM337 (IC1) and associated components C1, C2, PR1 and R1. PR1 adjusts the negative voltage which provides the power supply for the LCD drive (contrast) at VO, and should be between -9.3V and -10.3. Note that the power circuit was something I had put together quickly to test another LCD.

Although it is only needed in very poor lighting conditions, the LED backlight is made use of in the test circuit and it is driven by connecting LED- and LED+ to the 5V supply through a 4R7 resistor (R3), which must be rated at 0.5W or higher. Although this should give LED current of 220mA I measured about 130mA but the drop can be explained by the fact that the 5V had dropped somewhat. The backlight connections on the LCD module are separate from the main connector and have a red wire connected for LED+ and a black wire for LED- (but your module may vary).

It's usually best to leave the Uno's D0 and D1 I/O alone as they are used for updating the Arduino so I started on D2 up to D9 to send data and commands to the LCD using its 8-bit data bus D0 to D7. We then have 4 more signals to deal with which are WR for telling the LCD we want to write to it, RD to get data from the LCD, CE to select the LCD for communication and C/D to let the LCD know if we are giving it a command or data. These signals are handled by the Uno's D10, D11, D12 and D13 digital pins respectively.

The last things to talk about with regards to the circuit includes resetting the LCD using a simple power-on-reset circuit consisting of R2 and C3 connected to the LCD's RST input. It is very important that the LCD's controller (the T6963C) is reset at power up by taking RST low otherwise it may not start up correctly and in worse case the LCD could be damaged by DC bias. Other than RST, FS is taken low to select 8 x 8 font size, and the two NC connections and FG (Frame Ground) are not connected.

Next, we will go over the Arduino code but before that I will link to the T6963C datasheet which is worth reading first:

https://www.sparkfun.com/datasheets/LCD/Monochrome/Datasheet-T6963C.pdf

The Arduino code is attached to the bottom of this page and is called 'MGLS24064_test.ino'; download it and open it up in the Arduino IDE (1.8.3 or higher) to view it. Transfer the code to your Uno with the LCD connected and after a very brief pause you should see the following text displayed over several lines:

Hello world :)

I'm a 240 x 64 LCD with backlight.

My controller is a T6963C from Toshiba.

This is a demonstration of using my text mode.

If you do not get any output on the LCD double check the circuit and if all is good try adjusting PR1.

Referring the the Arduino code, we'll start with the setup() function which first defines the inputs and outputs, as well as the default states of the Uno's I/O. For digital pins 2 to 9, which are connected to the LCD's data bus D0 to D7, I manipulate the Arduino ports B and D directly as to avoid using a loop to change between output and input and for setting or reading to easily combine into one byte. Because the Arduino is relatively slow compared to the LCD module we can get away with using digitalWrite() - which is slow - wherever we need to use it.

The last part of setup() calls functions init_LCD(), clear_LCD(), and disp_text() to first initialise the LCD, then clear the screen (the LCD's display memory starts up in a random state) and lastly to display the text. If we look at init_LCD() we see that the following actions are taken:

Sets LCD to use its internal character generator (value LCD_int_CG).

The blinking cursor is turned on and so is text mode (LCD_on_blink).

Text home address (where in RAM the text memory starts) is set to the value of 0 (LCD_text_start_LSB and LCD_text_start_MSB).

Number of columns in bytes is set (LCD_text_area_set) based on the calculated value (which will be 30, that is, 240/8).

Cursor is set to be 8 lines high (LCD_set_cur_8_line).

Cursor pointer is moved to position 0,0 (LCD_set_cur_point).

This actions, and others, are carried out by use of 3 overloaded functions called LCD_command(), which either accept no, 1 or 2 operands. Each LCD_command() routine calls LCD_command_data() either once or 3 times passing the required data value and a boolean option of either false to send data or true to send a command. If we are wanting the LCD to execute a command that has operands then the operand data must be sent first and the command value last. In LCD_command_data() we first wait for the LCD to be ready by calling LCD_wait_cmnd_data() which in turn calls LCD_wait() to start of the wait process. LCD_wait_cmnd_data() will exit when both bits 0 and 1 of the byte read in from the LCD (Arduino digital pins 2 and 3) become logic 1. The data/command value is actually sent to the LCD by calling LCD_send_command_data() in LCD_command_data().

Function clear_LCD() sets the start of the text memory as where we want to start clearing the screen, by calling LCD_command() by specifying LCD_set_addr_pointer along with the text start address values. We then enter an auto increment mode which will increase the address pointer each time we update the display memory and that is done by using the LCD_set_data_auto_write value. Then it is a matter of looping over every byte that makes up the display memory and setting it to the space value 0x00 using LCD_send_command_data(). Although the LCD doesn't use ASCII it has the same characters offset by - 0x20 so whereas in ASCII space is 0x20 the LCD uses the value 0x00. When the entire text memory has been written to we exit auto increment mode by issuing the auto reset command using value LCD_auto_reset. Note that the LCD will ignore all commands other than auto reset while it is in auto increment mode but we must call LCD_wait_auto_write() to check that we are OK to send another value to display memory.

Routine disp_text() is similar to clear_LCD() but a bit more complicated as we must output individual characters from a string array (str_array[]) as well as detect overflowing lines which will cause the next string to go onto the next line by checking the length of each string and increasing variable y_pos_offset as needed. In the loop that outputs each string we call out_text_LCD() with the X position and Y position of the start of the string and a pointer to the string itself. In out_text_LCD() we must first combine the text start address low and high values into one value, then calculate the offset based on the X and Y position and then split the resulting address back into a low and high value, which is handled by function genTextAddr(). Then we can set the text start address by calling LCD_command() with the address pointer value we calculated previously. The rest of the code of out_text_LCD() follows clear_LCD() but adjusts the character value read in from the string to match the values used by the LCD module. The last thing we do in disp_text() is move the cursor to the bottom left of the LCD.

There were Arduino libraries already available to communicate with T6963C based displays but it is a learning experience to write the code yourself. Some points to note:

To get anything displayed on the LCD we must send the mode set command to use the internal CG ROM and set the display mode to text/graphic on with optional cursor too. At this point the display should be full of random characters/graphics. If this is followed by setting the text home address to 0, the number of columns in bytes to the appropriate value and the cursor pointer is set to 0,0 then the blinking cursor (if enabled) will appear amongst the random characters.

Early on in writing the Arduino code I had the issue that while the LCD was executing no operand commands it seemed to be ignoring 1/2 operand commands. I eventually found that the problem was that I was taking LCD_CE low too soon. I fixed it by taking LCD_CE low after updating LCD_WR, LCD_RD and LCD_C_D signals. This comes down to the slowness of the Arduino, especially when using digitalWrite(), and the LCD module requiring the other signals settle before LCD_CE changes.

The T6963C datasheet says that to set the cursor pointer (position) you must supply it with an X and Y address but in fact the values are the X and Y position.

The display module is capable of graphics and text modes as well as text mixed with graphics so although the demo code only uses the text mode feel free to try out the other modes.

All content of this and related pages is copyright (c) James S. 2010-2019