LED and LCD Modules

By James S.

To see larger versions of any of the images please click on them.

Note: hexadecimal values are marked 0x.


LED (Light Emitting Diode) and LCD (Liquid Crystal Display) displays can be found in many forms, capable of indicating letters, numbers, and other symbols. For examples of LED displays please have a read of my overview of diodes using this link:

As for LCD's, they are available in many shapes and sizes and you can view some examples below:

Commonly LCDs are rectangular but as can be seen bottom left they can even be octagonal. Bottom right is an upturned LCD showing the elastomeric 'zebra' strips top and bottom that connect the LCD to a circuit board. Other ways to connect an LCD are through use of a flat cable or metal contacts.

Whereas the examples above are of the LCD's only, LED and LCD modules are electronic building blocks that contain some sort of display (LED or LCD) and all the necessary circuitry to show text or graphics on the display or at least handle very basic screen control (that is, the module contains some form of microcontroller). The display modules have an interface usually designed to be connected to some form of computer circuit which tells it what to display, although it is possible with some display modules to test them with simple switches.

Some display modules, in particular those that contain an LCD, are labelled as being 'intelligent' since they are able to accept commands in which to activate a cursor, scroll the display, handle text and other useful tasks. But whether the display module is regarded as intelligent or not, they can all be divided into the two main types which are text (or character) and graphic. Text (character) display modules can only show letters, numbers and special symbols; its graphical capabilities are very limited. Graphic display modules, however, are able to output text and pictures but generally are more complicated to use than text only display modules and don't follow a standard interface like most of the text only ones do. Graphic display modules actually are useful for circumstances where only text needs to be displayed, such as for word processors, as a graphic display module can show text in different forms (such as different fonts) more easily than a text only display.

LED Display Modules

LED displays seem to be as popular as ever even though LCDs have many advantages over them but perhaps it's the bright lights and different colours that make them still so much in demand. There are two main types of LED modules for displaying information and they are the segment type and the dot matrix type. Segment displays use a number of usually rectangular LEDs arranged in the figure of an eight and by lighting different combinations of the segments, numbers and some letters or other symbols can be shown. Dot matrix displays, however, use a number of round LEDs arranged horizontally and vertically to form a square or rectangle. Lighting up these LEDs forms patterns such as characters or pictures, much in the same way as images are formed on your TV screen or computer monitor. The big advantage of dot matrix displays over the segment type is that better looking numbers and letters can be displayed, as well as images.

LED displays come in all sorts of sizes and colours and especially with the dot matrix displays you can get bi-colour and tri-colour versions, which means that each LED can light up one of two or three colours. On the downside, these displays are much more expensive and require a more complicated controller chip that is part of the LED module.

Dinamap Compact T LED Board

This LED board comes from the Dinamap Compact T vital signs monitor which you can read about here:

The board has the markings '8630AB03' and '8630BS03' and is based around x3 Maxim max7219 chips (IC1-IC3) which handles the displays and the 3 individual LED's D1 - D3. To read up on the max7219 check up on the datasheet at:

Here is a photo of the LED board:

The max7219 is designed for common cathode LED displays and a single chip can handle up to 64 segments (8 displays having 8 segments each) or 64 individual LED's. The chip handles multiplexing and requires a single resistor to set the current for all of the LED's as well as having software control over the LED intensity. A number of commands are recognised, which includes the option to enable BCD decoding and individual segments can also be modified. A serial interface is used for communication which can run up to 10MHz.

On the LED board are x19 7-segment displays (DS1 - DS19) and three LED's (D1 - D3), with combinations of colours and display sizes. Connector PL31 (on the underside) is for the LCD that is also part of the Dinamap Compact T and connector PL30 (also on the underside) is the main interface to the LED board. Pin 1 is identified as the pin that has a square blob of solder; looking down at the main component side pin 1 is at the top left and it can be seen that the pin immediately below it is connected to pin 1. Using numbering with odd numbers at the top and even at the bottom the connector pinout is as follows:

1 +5V
2 +5V
5 DIN IC1-IC3 via 1K resistor
6 CLk IC1-IC3 via 1K resistor
7 IC1 LOAD via 1K resistor
8 IC3 LOAD via 1K resistor
9 IC2 LOAD via 1K resistor
10 LED D1, LED D2, and LED D3 K
11 LED D5 A (switches board)
12 S4 built-in LED A (switches board)
13 GND
14 LCD backlight control (connects to TR1 gate).

The resistors in series with the DIN, CLK and LOAD inputs are possibly to protect the IC's in event of a fault and to help prevent ringing. Because IC3 handles 2 different types of displays as well as individual LED's, resistors/links R1-R11 have been placed in series with the anodes of the LED's that have different current requirements (this is explained in the Dinamap compact T service manual).

Following is list of the displays handled by IC1 - IC3 and in brackets is the display number starting with 0 for the first display:

IC1 handles DS1 (0), DS4 (1), DS7 (2), DS11* (3), DS14 (4), DS17 (5).
IC2: DS2 (0), DS5 (1), DS8 (2), DS10 (3), DS12 (4), DS15 (5), DS18 (6).
IC3: DS13 (3), DS16 (4), DS19 (5), DS3 (0), DS6 (1), DS9 (2), LED D1, LED D2, LED D3. LED D3 is segment e of digit 6; LED D2 is segment d of digit 6; LED D1 is segment c of digit 6.

*Not marked on the board.

At power up the max7219 IC's control registers are reset, and any connected displays are blanked as the chip starts up in shutdown mode but commands can still be sent to the chip. It is easy to put the IC into test mode, which illuminates all connected segments, but the IC must be taken out of test mode for it to be able to do anything else, even to exit shutdown mode.

To test the LED board I wrote some code for the Arduino Uno which you can find attached to the bottom of this page as 'Dinamap_Compact_T_LED_board_test.ino'. Download the file, open it up in the Arduino IDE (V1.8.3 or later) and transfer the program to your Arduino. Connect the LED board as follows (the pin number in () refers to the LED board pin number):

LED board +5V (1/2) to Arduino 5V
LED board GND (3/4) to Arduino GND
LED board DIN (5) to Arduino D2
LED board CLK (6) to Arduino D3
LED board LOAD 1 (7) to Arduino D4
LED board LOAD 2 (8) to Arduino D5
LED board LOAD 3 (9) to Arduino D6

When you power up the Uno you should see after a brief delay that all segments and LED's come on then after 2 seconds the 7-segments displays will be lit as:

Displays 0 - 5 on each of the displays in group 1 (IC1).
Displays 0 - 6 on each of the displays in group 2 (IC2).
Displays 0 - 5 on each of the displays in group 3 (IC3).

Let's now look at the Arduino code which is something I put together quickly and doesn't represent well written programming. In the setup() function we set up our I/O which are the LED board signals (DIN, CLK, LOAD 1, LOAD 2 and LOAD 3) which are set to outputs and their default states are also set. A delay of 1 second begins which I found necessary as without it sometimes the LED board would be blank or 'glitched up' after power up (this is when powering the Arduino off PC USB); looking online other people had similar issues. After the delay, all 3 max7219 IC's are put in test mode by calling LED_out_data() with the data value to send (LED_test_mode) and the display group to update. If you look at LED_out_data() you will see it sends out each bit on DIN as a 16-bit string, starting with the most significant bit first. Once DIN is set accordingly, LED_clk() is called to take LED_CLK high then low and once all bits are sent LED_load() takes the appropriate load signal (LED_LOAD_1/LED_LOAD_2/LED_LOAD_3) high then low so that the max7219 acts of the data it has been given.

Back to setup(): there is a delay of 2 seconds and then we do the following:

Exit test mode.
Set to use BCD code B mode for all digits.
Set LED intensity to medium.
Set to scan all digits.
Exit shutdown mode.

Lastly, we use function LED_out_disp_num() with the display group to update and the number of digits in the group so that each display in the group in order displays 0, 1, 2, etc. The routine LED_out_disp_num() always writes to the maximum 8 digits but when it goes past the specified last digit it blanks the remaining segments. To actually update the displays we call LED_out_data() either with digit_num OR'd with the digit number (i) so that it gets the BCD number or digit_num is OR'd with 0x0F which in BCD code B (see the max7219 datasheet for the full list of values) blanks the segments.

Some notes:

With all of LED's on (using test mode on IC1 - IC3 without changing default intensity) I measured 330mA was being drawn by the LED board.

I noticed flickering on the LED displays and LED's handled by IC3 when using the default brightness. 

DLR1414 4 Character 5x7 Alphanumeric LED Dot Matrix Display Module

This LED dot matrix display module can illuminate up to 4 characters, each of which can be one of a number of predefined letters, numbers, or special characters such as punctuation. Each LED dot is red and the whole module runs off a single 5V supply and is TTL compatible. Note that the display module starts up in a random state.

The display module is not truly intelligent as it can only receive raw data of what character to display; it cannot accept any commands. Two address signals, A0 and A1 select which digit to update and a single active low write signal latches the data for the chosen digit. The ASCII value of the character to display for the selected digit is written to the 7 bit data bus; the display module has 128 built in characters conforming to the ASCII standard (with some exceptions). 

A triangle on the side of the display points out the first pin and on the opposite side of the display is pin 12 (just like with DIP IC numbering). The pinout is as follows:

1 D5
2 D4
3 /WR
4 A1 digit select
5 A0 digit select
6 Vcc
8 D0
9 D1
10 D2
11 D3
12 D6

Each digit is numbered from 1 to 4 with digit 4 being on the end where pin 1 and 12 are. Use A0 and A1 to select the digit as below:
















When it comes to soldering LED displays I usually use a chip holder and fortunately if you want to fit multiple of these displays side-by-side onto chip holders you can fit two into a single 28 pin chip holder (there is approx. 20mm distance between the rows of pins). This works out because there is a gap of one pin's worth between both displays.

Sure 0832 DE-DP10XV110 LED Dot Matrix Display Module

This display is a real beauty but quite surprisingly can be bought fairly cheap which is just as well as the accompanying documentation is very poor and not because it was translated from another language. The documentation has only just the basic information for using the display and some parts are quite confusing. For example, the display module has a blink command that you might assume would flash all the LEDs on or off but it actually blinks only LEDs that were previously lit; there is no mention in the documentation what exactly the blink command does so it's only though trying that you find out.

The display module (below) has 256 red LEDs arranged in an 8 by 32 format (8 height and 32 wide, although you could have it the other way up and treat it as a tall display). The module is actually made up of 4 smaller LED dot matrix displays each of which has 64 LEDs in 8 by 8 format, however, the module chip maps the addresses to the correct LEDs for you.

Cascading the display modules is possible so that you can put together up to 4 display modules to create a longer or higher display system. Each display module has two 16 pin connectors for power and interface signals so that each display can be linked up to the next (a ribbon cable came packaged with the display module). There is also a set of switches on each display module in which to give each a unique selection value through the interface (so that if you do have more than one display connected together, you can select each in turn to 'talk to' by its ID value). If you do link up display modules, you can solder connectors to the display modules in which to supply more power.

Being a serial display module data has to be sent to the display as a number of bits one after each other along with a write or read signal that acts as a clock for timing. Although it is possible to read from the display module's memory, if you are interfacing using a computer you can probably do without the read connection (by keeping it at logic 1) since a computer should have enough memory to keep a copy of what is on the LED display if need be. But with the case of a microcontroller based system that has very little RAM is might be a good idea to make use of the read signal.

The display module runs off a 5V supply (although not mentioned in the documentation, it appears to be TTL compatible) but is able to retain its memory long after power is disconnected, perhaps due to an on-board capacitor. Interestingly, although the display module is one colour only, the LEDs look to be of the tri-colour type (if you look closely at each LED you can see three connections) and since the display module is available in different colours, perhaps Sure only connect the colour that is needed save having to use a different display type for each colour type.

There are a number of commands that the display module recognises to turn the display on or off, to write data to the display memory to turn the LEDs on or off, to read from the display memory, and various others. Even though the commands have to be sent in serial form and to fill or clear the display requires a lot of data to be sent to the display module, it still updates instantly even through a parallel port interface (yes I used the parallel port even though the display module is serial but it was the best interface for me to use at the time).

You can write 4 bits to each display memory address and each of those 4 bits controls a group of 4 LEDs in sequence. To light an LED, the bit needs to be at logic 1 and to turn off an LED the bit must be at logic 0. Since the display module is not able to generate characters itself, you have to create the characters (numbers, letters, etc) and other images yourself and then send them to the display module as the correct arrangement of bits.

I had planned to use the display in a message board which would display the time, date and messages and for that I used an Arduino to update the display. While that worked OK I found it very time consuming to keep making small changes before transferring to the Arduino. So I moved to using the Raspberry Pi since that would let me develop the code on the computer itself and I would see the results much quicker.

At the bottom of this page you can download my test code, 'Sure_0832_test.py', a Python script for the Raspberry Pi which works the LED display through the Rasp's GPIO. It is designed for Python V3 and as it uses GPIO must be run from the terminal using:

sudo python3 Sure_0832_test.py

The hardware setup follows (LDM=Sure 0832 LED dot matrix display. Rasp GPIO numbers are the pin numbers)

LDM pin 3 (/CS1) to Rasp GPIO pin 12
LDM pins 1 (/CS2), 2 (/CS3) and 4 (/CS4) to Rasp +5V pin 2
LDM pin 5 (/WR) to Rasp pin 16
LDM 6 (/RD) to Rasp +5V pin 2
LDM 7 (DATA) to Rasp pin 18
LDM 12, 14 and 16 (+5V) to  Rasp +5V pin 2
LDM 8, 11, 13 and 15 (GND) to Rasp GND pin 6

You must set the 0832's CS switches so that only CS1 is set to on.

When the script is run you should see the time displayed in format hour:minute with a ':' flashing every second. When Ctrl+C is pressed you will be given the option to change the current mode; 'T' for time, 'D' for date (in format day/month), 'M' to scroll a message from right to left, or 'Q' to exit the program. After entering your selection and pressing Enter the mode will change and as long as you did not quit you can press Ctrl+C to bring up the options once more.

At the main program start point (near the end of the script) the GPIO is setup and then a loop is entered which checks the current mode and calls the necessary function to update the display. If Ctrl-C is pressed then the KeyboardInterrupt exception is raised and the getChangeMode() function is called to ask the user what mode they want to change to before actually changing the mode.

To display anything on the LED display then the displayChar() function is used which takes as input charI (character index) and charPos (the character to display). This function displays a single character anywhere on the display using a charPos from 0 (left side of display) to 64 (right side of display), even values only. The reason for even values is that each character is 5x8 dots and vertically two addresses make up one line of the character as each address controls four dots vertically. Thus each character requires 5 bytes and the character data is stored in the chars[] list.

Function displayChar() will clip characters or part of characters that are partly off the display to one end so that scrolling is smooth. Building on top of displayChar(), displayStr() takes a null terminated string (str) and start position (startPos) and displays the string by calling displayChar() to show each character. While displayChar() takes a character index which indexes chars[] displayStr() uses ASCII values and converts to the charI values.

Another notable function is fillDisplay() which writes to every LED by either turning them all on (by passing True) or all off (by passing False). This is useful for testing and for clearing the display which is what the function clearDisplay() does. Function fillDisplay() does not use the most optimized code, however, but it is still very fast.

There are some low level functions to talk about, starting with opAddress() which outputs an address to the display. Function opCommand() outputs a command, such as to turn the display system on. Lastly, opCmdCode() outputs a command code, which is the value representing the command.

There are a number of things that need to be fixed, one of which is the flickering you see when a message scrolls across the screen. This can be improved either by making clearDisplay() more optimized or by only outputting the dots that actually change between each frame. Another problem is that sometimes dots appear that shouldn't, however, I've yet to find what causes the random dots which may be a problem with the display itself.

Sure 2416 DE-DP016 LED Dot Matrix Display Module

This display module is similar to the Sure 0832 DE-DP10XV110 LED Dot Matrix Display Module but bigger in size. It has 384 red LEDs arranged in a 24 by 16 format (16 height and 24 wide). Although up to four of these display modules can be linked up in the same way as the 0832 display module, the 2416 modules can only be stacked closely together side-by-side as the main circuit is at the bottom of the display module.

Bundled with the display module was two ribbon cables, and two additional power connectors were already soldered to the display module. The pinout is almost the same as the 0832 display module with the difference in the order of the chip select connections (a much more logical order). So if you wanted to link one of these displays to a 0832 display module, all you need to do is remember that the chip select signals will be different for one of the displays. Oddly, in the documentation (which is only slightly worse than the documentation for the 0832 display module), the photo of the display module shows that it has eight switches yet the pinout lists only the (expected) four chip select signals.

There is one problem with this display module using the same control chip as the 0832 display module; when power is first connected, it is only possible to access half of the display. By way of a command (called common options) you have to tell the display module to switch to the mode where you can use the whole display. This also changes the layout of the display memory addresses so that they flow down the complete display. 

*** More to be added soon***

Build Your Own Display

While displays of all sorts are readily available at cheap prices, it's very rewarding to make your own and it may be that you can't get hold of a particular type of display or the one you want is expensive. This section will deal with making your own 7-segment LED display which can be connected to some form of decoder to show numbers on the display. So while the rest of this page has been concerned with display modules, which are displays that have a built-in decoder and interface, this section is about making just the display part.

A 7-segment display is made up of 7 segments arranged in a figure '8' which can display numbers and some letters by lighting combinations of the segments. I should point out that although these types of displays are called 7-segment, they often include one or more LED's that act as a decimal point. The decimal point may be at the left of the main 7 segments, to the right, at both sides, and may even include additional LED's either side near the top (such as for clock radios).

Internally, big 7 segment displays, like those typically used in arcade machines, their segments are actually made up of multiple LED's in a row, since a single LED would not light up the whole area of the segment. If you look carefully at the back of one of these large displays while the segments are on, you should be able to make out the individual LED's that make up each segment.

Before making the display you need to think about the size you want it to be, the colour of the segments and how many decimal points (if any) you want. A good idea is to pick the LED's you want to use (they could be round or rectangular) and then place them into a board with pre-drilled holes (such as strip board or matrix board). You can then space out the LED's as you please and see how they look as well as the overall size of the display.

Once you know the layout of the LED's and how many LED's each segment is made up of you can work out the wiring. This will depend on whether you want the display to be common anode or common cathode and how the LED's of each segment are to be connected. If you want to be able to use low voltage for the segments then the LED's in each segment can be connected in parallel (through suitable limiting resistor). However, if you intend to operate the display segments at a higher voltage then the segment LED's could be connected in series which could eliminate the need for segment limiting resistors (for e.g., 3 LED's with a forward voltage drop of 2V can be connected in series and run off 6V without a limiting resistor). If there are a lot of LED's in each segment then you may have to connect them in a combination of series and parallel as to get a good balance of voltage and current requirements.

I needed a large, dual 7-segment LED display for a project and instead of buying one I made my own which you can view below:

Except for the two decimal point LED's, each segment is made up of three blue, high brightness LED's in parallel. As there were only three LED's for each segment I figured that would be better than putting them in series which would require a higher voltage and possibly complicate the controller circuit.

Two sets of cables carry the common cathode and separate anode connections for the LED segments. If you wanted you could connect the matching segments together and use individual common cathodes to act as a multiplex display but I avoided that because I would be using the display with a controller chip that doesn't support multiplexed displays.

As for the soldering, I arranged the LED's in such a way that the cathodes would be near to each other so I could solder across the LED legs to common them. For the anodes I took advantage of the coloured cables so that each segment was in turn and represented by a colour. Be sure to solder as quickly as possible to prevent damage to the LED's and test often to ensure none of them have blown.

Each of the blue LED's has a forward voltage drop of 3.2V and draws about 15mA so a single segment (that isn't a decimal point) draws around 45mA and we would expect a total current draw of something like 630mA if all the main segments were on. To lower the current requirement we could use a higher limiting resistor at the cost of loss of brightness.

LCD Modules

Liquid Crystal Displays (LCDs) have many advantages over other forms of display, for one thing they use very little current and because of that they have found themselves in many portable devices. That said, LCDs often require back lights and can be more difficult to read than other display technology such as LEDs.

It's just as well that Intelligent LCDs were invented, which can use either a parallel or serial interface, and are designed to help out the programmer with displaying information on an LCD. The text only intelligent displays usually have a built-in character generator (that is, a ROM containing the graphical information of each character) and accept a limited amount of commands to display characters, scroll the display, move to a set position, and so on. This 'intelligence' is thanks to one or more controller chips that are part of the LCD module, handling both input from the 'outside world' and updating the LCD.

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:


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:


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


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:

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
X = 0    3

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.

HD44780 LCD modules

The 'HD44780' and compatible intelligent LCD modules 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.

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

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

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

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:

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:









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.

















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.

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)
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).

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.

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)
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:

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:

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.





SPL 13


R3 17


R4 18


R5 19


G3 23


G4 24


G5 25


B3 29


B4 30


B5 31


PS 34

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

LP 35




REV 42


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):

































































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:








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:


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:


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 seem 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-2018

New: Novametrix 515C Graphic LCD (21/02/2018)
Dinamap Compact T LED Board (26/12/2017)
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)
Update: Sure 0832... (03/03/2015)
Raspberry Pi to HD44780 LCD module (24/11/2014)
Update: DLR1414 4 Character 5x7 Alphanumeric LED Dot Matrix Display Module (21/05/2014)
Fax machine LCD Module (Added: 10/06/2013)
VTECH Precomputer 2000 LCD Module (Added: 25/05/2012)
Build Your Own Display
WGM-12232M Graphic LCD Module

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

To return to the main electronics page please click here.
James Stuart,
Dec 27, 2017, 12:07 PM
James Stuart,
Dec 20, 2017, 7:02 AM
James Stuart,
Feb 21, 2018, 2:26 AM
James Stuart,
Dec 25, 2015, 4:54 AM
James Stuart,
Dec 12, 2017, 11:25 PM
James Stuart,
Mar 3, 2015, 12:32 AM
James Stuart,
Nov 24, 2014, 7:16 AM