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.
Nice blog 😉 good luck