There is a lot of LCD color screens in the various electronic devices used in everyday life, the price of these screens become affordable for electronics enthusiasts. Several sources of supply are possible. Directly attacking mobile devices or browsing the web, especially on Ebay. We can also find some parts sellers as Sparkfun which can suplly some LCD screen but also very usefull breakout board
The LCD screen used in this page, is a screen from a Nokia mobile phone. His reference on the most common site is the Nokia 6100 LCD because of its presence in the GSM Nokia 6100. This type of screen is also used in other models of the brand.
a reference forum exists for this type of screen where you can find al lot os tips. The website of Sparkfun gives some links to application examples by other enthusiasts.
You finally have one... so let's the show goes on... Yes, but... you have now to define which kind of controller is running as LCD brain. Two controllers are used : Phillips PCF8833 and Epson S1D15G00 controllers. In several threads of the LCD Forum, we can can gather information based on physical characteristics of the screen (flex color, stickers and numbers) The one bought in the Sparkfun store mid april 2009 on a EPSON based controller. (green flex,GE noted sticker and Red sticker on screen protection, GE128128 number on the backside). Unfortunately, both types of controllers are not programmed in the same way. This page is devoted only to the Epson controller.
At least, there is something in common to both types of graphics controllers : the hardware!
![]() |
To easily interface the LCD screen and the remaining circuitry, you can use the Breakout Board proposed by Sparkfun. In addition to connections with the screen, the PCB is equipped with an RGB LED and two push buttons that can improve the user's interface. A power stage is also foreseen to generate the different screen voltages. A 3.3 volts only power supply is possible even if it is not optimal as described below. The schematic of this PCB is available ici. |
Hereunder, the pin allocation of the breakout board from Sparkfun :
|
Interconnections between microcontroler and LCD are limited to only 4 pins ( CS-SCK-DIO-RESET ). If the microcontroler is supplied with 3,3 volts, these pins can directly be connected to the LCD printed circuit board . If this is not the case, you have to use voltage level translation. A simple resistor voltage divider can do the job. (1k8 - 3k3 for 5 Volts, for example). The Vbatt voltage can go downto 3,3 Volts but for this voltage the power stage has a very bad efficiency. It's better to use a higher voltage (7 volts max) to reduce currents.
Before initialisation, you have to define the communication protocol between the PIC and the screen. The used protocol is the SPI one (only two wires are necessary : clock & data). The screen SPI protocolis using the 9 bits mode, it's easier to use a software SPI module than to use the hardware one imlemented inside the Pic controller which is limited to 8 bits. The ninth bit is used to warn the screen about a command (bit = 0)or a data (bit = 1). On the Sparkfun breakout board, the Din signal is not implemented; we just have a write-only access to the screen.
Hereunder, the program used to communicate with the screen
|
************************ **** Send a command **** ************************ void gspi_command(unsigned char dat) { LCD_CS = 0; // Chip Select - Sélection de l'écran SPI_DO = 0; // 0 on data line (9ième bit=0 => command) SPI_CLK = 0; // clock Delay_us(1); SPI_CLK = 1; Delay_us(1); gspi_do(dat); // following 8 bits LCD_CS = 1; // screen unselected } ************************ **** Send a data ****** ************************ void gspi_data(unsigned char dat) { LCD_CS = 0; // Chip Select - Sélection de l'écran SPI_DO = 1; //0 on data line(9ième bit=1 => data) SPI_CLK = 0; //clock Delay_us(1); SPI_CLK = 1; Delay_us(1); gspi_do(dat); // following 8 bits LCD_CS = 1; // screen unselected } ************************************** **** Send 8 bits **** ************************************** void gspi_do(unsigned char dat) { int i; i = 0; for(i = 1; i <= 8; i++) // 8 iterations, one for each bit { if (dat > (unsigned)127) // MSB test { SPI_DO = 1; // if bit=1, DO=1 } else { SPI_DO = 0; // if bit=0, DO=0 } dat = dat << 1; // rolling bits one case to the right SPI_CLK = 0; // clock Delay_us(1); SPI_CLK = 1; Delay_us(1); } } |
|||
As a remark : 1 µsec delays(delay_us(1)) used in iterations are to long... you can reduce them by 5. Communications have bee slown down for debug.
As already with LCD screen, you need to send a initialisation sequence before to use it. The first thing to do is to make a hardware reset. Just give the reset pin a low level before a high one.
|
// Reset Hardware LCD_RESET = 0; delay_ms(100); LCD_RESET = 1; delay_ms(100); |
|||
Command DISCTL allows to control all internal clocks but also to define how much lines are used. Command DISCTL uses 4 parameters. The first one is the division factor of the internal clock. The second one use the following formula to determine how much blocks ( 4lines per block) are used : ( lines number/4 )- 1. The third one is the "highlighted" lines number. The last one is for dispersion.
|
// commande DISCTL gspi_command(DISCTL); gspi_data(0x00); // div by 2 (default) and default switching period gspi_data(0x20); // 130 lines gspi_data(0x00); // no "highlighted" lines gspi_data(0x00); // no dispersion |
|||
The following command selects the output scanning mode. the only working value is one... We then put the internal oscillator On (OSCON) and wake the system (SLPOUT).
|
gspi_command(COMSCN); gspi_data(0x01); //P1: 0x01 = Scan 1->80, 160<-81 gspi_command(OSCON); gspi_command(SLPOUT); |
|||
Then screen contrast (VOLCTR) and power stages control (PWRCTR). The screen contrast trim may be difficult. To choose the right value, i have used a for loop (values from 0 to 63). The second parameter of VOLCTR command is 0x03; only working value i found. Then command PWRCTR is send, wait 100msec to allow voltages to reach their nominal level.
|
gspi_command(VOLCTR); gspi_data(0x30); gspi_data(0x03); delay_ms(100); gspi_command(PWRCTR); gspi_data(0x0f); delay_ms(100); |
|||
the following command is DISINV which is necessary to get the right colors on the screen. after that we have the DATCTL which configures the colors , the way to control the RGB bytes and the screen position (180°). Three parameters are used : the first for screen position, the second, for RGB sequence and the third to tell how bits are used to code colors. A last command may be usefull : DISON ... to switch the display on.
|
gspi_command(DISINV); gspi_command(DATCTL); gspi_data(0x00); gspi_data(0x00); gspi_data(0x02); gspi_command(DISON); |
|||
There is only one thing to know ... to be able to switch on or off a pixel in the right color. To do this, you have to know the row and column number of the pixel to be switch on to be able to access RAM. at the address, you have to put the pixel color information. We use three command for this : PASET ( row selection), CASET (column selection) et RAMWR (writting in RAM).
|
void LCDSetPixel(int x, int y, int color) { // Choix de l'adresse de la ligne (commande 0x2B) gspi_command(PASET); gspi_data(x); gspi_data(x); // Choix de l'adresse de la colonne (commande 0x2A) gspi_command(CASET); gspi_data(y); gspi_data(y); // On allume le pixel (2nd pixel ignoré) gspi_command(RAMWR); gspi_data((color >> 4) & 0xFF); gspi_data(((color & 0xF) << 4) | ((color >> 8) & 0xF)); gspi_data(color & 0xFF); gspi_command(NOP);} |
|||
Just more exponation about commands PASET and CASET. These commands can define a rectangle whose position and lengths of sides are defined by the two end points of one of the diagonals. A small sketch for better understanding:

In the case of writing a pixel, the points defined by PASETT and CASET are identical. Using the command RAMWR, we can write directly to memory controller. If the points defined in the commands and CASET and PASETT are different, we define a rectangle where we can easily draw characters or other drawing. The great advantage of the command RAMWR is that we can sequentially send the RGB values of pixels without having to worry about memory addressing. Indeed, it begins in the lower left corner and then, automatically, the controller increments the next pixel position. Once in the end of the row, an increment to the next line is performed. (See diagram above). We see at once the command economy in relation to a drawing pixel by pixel. A sample application is presented in the following paragraph.
On a graphic LCD, writing is like drawing. Each character must first be drawn. We use a table in which each row contains different sets of pixels to be lighted for the drawing of a character.

In the table below, there is a sample table of characters used in a program which displays temperatures. The first row of data table provides the height and width of the character and the number of bytes needed for the draw.
|
const unsigned char FONTRED1 [26][8] = { 0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00, 0x3E,0x63,0x63,0x6B,0x63,0x63,0x3E,0x00, // 0 0x18,0x38,0x58,0x18,0x18,0x18,0x7E,0x00, // 1 0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00, // 2 0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00, // 3 0x0E,0x1E,0x36,0x66,0x7F,0x06,0x0F,0x00, // 4 0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00, // 5 0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00, // 6 0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00, // 7 0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00, // 8 0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00, // 9 0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00, // + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, // , 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00, // - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //space 0x7E,0x5A,0x18,0x18,0x18,0x18,0x3C,0x00, // T 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00, // e 0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00, // m 0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78, // p 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00, // e 0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00, // r 0x00,0x00,0x3C,0x06,0x3E,0x66,0x3B,0x00, // a 0x08,0x18,0x3E,0x18,0x18,0x1A,0x0C,0x00, // t 0x00,0x00,0x66,0x66,0x66,0x66,0x3B,0x00, // u 0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00, // r 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00 // e }; |
|||
Remains now to use the command RAMWR as described above to display the character at the desired location on the screen. To do this, after setting the location (x, y) where to put the character, you should browse the table of characters to retrieve the status of each pixel belonging to the character. A choice of color is possible to print the character but also the background color.
|
void LCDPutChar(char c, unsigned char x, unsigned char y,unsigned int fColor, unsigned int bColor) { unsigned char i,j,nCols,nRows,PixelRows,Mask; unsigned int Word0; unsigned int Word1; nCols = FONTRED[0][0]; nRows = FONTRED[0][1]; //Définition du cadre dans lequel dessiner le caractère // Addresse des lignes gspi_Command(PASET); gspi_data(x); gspi_data(x + nRows - 1); // Adresse des colonnes gspi_command(CASET); gspi_data(y); gspi_data(y + nCols - 1); // On écrit le caractère, ligne par ligne, de haut en bas, en // sélectionnant la couleur de fond ou la couleur du caractère gspi_command(RAMWR); for (i =0 ; i <=nRows - 1; i++) { PixelRow = FONTRED[c][i]; Mask = 0x80; for (j = 0; j < nCols; j += 2) { // si le bit du pixel = 1, couleur caractère sinon couleur de fond // attention on traite deux pixels à la fois if ((PixelRow & Mask) == 0) Word0 = bColor; else Word0 = fColor; Mask = Mask >> 1; if ((PixelRow & Mask) == 0) Word1 = bColor; else Word1 = fColor; Mask = Mask >> 1; gspi_data((Word0 >> 4) & 0xFF); gspi_data(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF)); gspi_data(Word1 & 0xFF); } } gspi_command(NOP); } |
|||
This routine displays one character at a time, it is possible to imagine a routine to display a string from it. A loop incrementing the position of the characters but also the position of the character into the string . Be careful also not to exceed 131 pixels ...