Nokia 3310 LCD display driver

By | January 21, 2011

I’ve been working on a project that uses the inexpensive and easily obtainable and programmable LCD panel from Nokia 3310 mobile phone. I’m posting some of the source code here because it was quite hard to find a ready-made driver on the web. The code was tested on an ARM-based microcontroller. Add pin definitions, sleep functions according to your board.
There are at least two models of the LCD panels. One of them is based on the PCD8544 controller, another uses PCF8813 (?). The difference is that the latter controller displays the data with an offset of several rows. The driver example supports both controllers via a compiler switch.

#define CMD 0
#define DATA 1

#define LCD_RESET //  TODO: add pin mapping
#define LCD_SCLK  // Clock -- add pin mapping
#define LCD_SI      // MOSI
#define LCD_DC     // Data / !Command
#define LCD_CS     // Chip select

#define LCD_DELAY //TODO: few microseconds delay

#define LCD_WIDTH 84
#define LCD_HEIGHT 48
#define LCD_ROWS 6
#define LCD_BUFFER_SIZE 504 // width x height / 8

static unsigned char buffer [LCD_BUFFER_SIZE]; // 1 bit per pixel

#define LCD_CMD_CONFIG_BLANK 0x08
#define LCD_CMD_CONFIG_NORMAL 0x0C
#define LCD_CMD_CONFIG_ALL_ON 0x09
#define LCD_CMD_CONFIG_INVERSE 0x0D

// command value 0 sends controller command, 1 sends data
void lcd_write(unsigned char command, unsigned char data)
{
	int i;

	LCD_CS = 0;
	LCD_DC = command;

	for (i = 8; i > 0; i--)
	{
		LCD_SCLK = 0;
		LCD_DELAY

		if ((data & 0x80)==0)
		    LCD_SI = 0;
		else
		    LCD_SI = 1;

		LCD_SCLK = 1;
		LCD_DELAY
		data = data << 1;
	}

	LCD_CS = 1;
}

void lcd_clear_n3310(void)
{
	int x, y;

	for (y = 0; y < LCD_ROWS; y++){
		lcd_write(CMD, 0x40 + y); // y position
		lcd_write(CMD, 0x80); // x position
		for(x = 0; x < LCD_WIDTH; x++) {
			lcd_write(DATA, 0x00);
		}
	}

	lcd_write(CMD, 0x40); // x position
	lcd_write(CMD, 0x80); // y position
}

void lcd_init_interface_n3310(void)
{
	// LCD panel interface pins
	set_output(LCD_RESET);
	set_pull_up(LCD_RESET);
	LCD_RESET = 0;

	set_output(LCD_SCLK);
	LCD_SCLK = 0;

	set_output(LCD_SI);
	LCD_SI = 0;

	set_output(LCD_DC);
	LCD_DC = 0;

	set_output(LCD_CS);
	LCD_CS = 0;

	LCD_CS = 1; // disable Chip Enable signal (active low), only to be enabled during data or command sending

	LCD_RESET = 0;
	//TODO sleep few ms
	LCD_RESET = 1;

	lcd_write(CMD, 0x21);  // LCD Extended Commands.
	lcd_write(CMD, 0xC8);  // Set LCD Vop (Contrast).
	lcd_write(CMD, 0x06);  //  Set Temp coefficent.
	lcd_write(CMD, 0x13);  // Bias
	#ifdef PCD8544
		lcd_write(CMD, 0x40+0x05);  // undocumented command - add vertical offset (or change display start row)
	#endif
	lcd_write(CMD, 0x20);  // LCD Standard Commands, Vertical addressing mode.

	lcd_write(CMD, LCD_CMD_CONFIG_BLANK);  // set display mode to 'blank', need to clear it first because the RAM contents are random
}

void lcd_init(void)
{
	lcd_init_interface_n3310();
	lcd_clear_n3310();       // reset DDRAM, otherwise the lcd is blurred with random pixels
}

// draws a frame buffer on the screen.
void lcd_draw_buffer_n3310(const unsigned char* const buffer)
{
	const unsigned char* ptr = buffer;
	int x, y;

	lcd_write(CMD, LCD_CMD_CONFIG_NORMAL );  // set display mode

	for (y = 0; y < LCD_ROWS; y++){
		// set Y position and reset X position to 0 for each row. The controller actually supports wider 102 pixel panels
	      #ifdef PCD8544
			lcd_write(CMD, 0x40 + y + 1); // y position
	      #elif
			lcd_write(CMD, 0x40 + y); // y position
	      #endif

		lcd_write(CMD, 0x80); // x position
		for(x = 0; x < LCD_WIDTH; x++) {
			lcd_write(DATA, *ptr);
			ptr++;
		}
	}
}

The example initializes the LCD panel and can draw a given frame buffer. I’ll post the source code for the frame buffer functions (characters, fixed and variable width fonts, etc) later. There is also a small embedded internationalization framework in the works.

0 thoughts on “Nokia 3310 LCD display driver

Leave a Reply

Your email address will not be published.