Tuto. 6 - Graphic LCD
PIC tutorial 6 - Graphic LCD
Fig. 1a Board 4 with graphic LCD display
Description:
The 6th tutorial in the series, we'll look at using LCD display, alphanumeric or graphic. The graphic display is taken from an old Nokia 3310 mobile phone.
You will need a processor board, a power supply, the alphanumeric and the graphic LCD display.
The tutorial will build a library to be able to control and use the graphic display. The alphanumeric library was already built in previous tutorial.
It also shows with a simple example how a program can be configured to run on different boards.
It displays a counter on the display, either alphanumeric or graphic. It shows how to easily switch between display types.
You can download the files for this tutorial in the zip file pic_tutorial6.zip.
The boards can be powered by one of the PSU boards (powered from USB port), or between 2.7 to 5V.
Requirements:
Processor boardAny board (board 4 in example)
Extension(s)Alphanumeric and graphic LCD Display
Power supplyPSU 1, 2 or 3. Depends on board used.
LanguageCCS C
ProgrammerPicKIT 2
Program:
#define board_id 4
#include "lib\mainboard.c" // Load board module
#if (board_id == 1) // Load alphanumeric or graphic lcd library
#include "lib\lcd_lib.c"
#else
#include "lib\nok3310_lib.c"
#endif
int8 counter;
void main() {
boot_up();
counter = 0;
init_display();
while (true) {
printf(display_putc, "\fH=%X, D=%u", counter, counter);
counter ++;
delay_ms(250);
}
}
The program defines the processor board used and load the display library. If using board 1, it loads the alphanumeric library, else the graphical one.
Once all libraries are loaded, the processor board, the counter and LCD display are initialised.
The main loop is then entered: we display the counter after clearing the screen (with the \f character) in 2 different format: hexa and decimal. The counter is incremented anda delay of 250ms is inserted.
The counter is incremented 4 times a second.
Now to the graphic LCD display library.
#ifndef _NOK3310_LIB
#define _NOK3310_LIB
// To modify accordingly to your hardware
#byte nokia3310_port = pb // Port where graphic LCD is connected
#byte tris_nokia3310 = trisb
#bit nokia3310_sck = nokia3310_port.3
#bit nokia3310_sdi = nokia3310_port.6
#bit nokia3310_dc = nokia3310_port.5
#bit nokia3310_cs = nokia3310_port.4
#bit nokia3310_res = nokia3310_port.2
// End of zone to modify
// variables used by library
int8 charsel;
// function prototypes
void Init_Nok3310(void);
void Nok3310_clean_ddram(void);
void Nok3310_wr_data(int8 bytefornokia_data);
void Nok3310_wr_cmd(int8 bytefornokia_command);
void Nok3310_wr_dorc(int8 bytefornokia);
void Nok3310_gotoxy(int8 xnokia, int8 ynokia);
void Nok3310_erase_y(int8 ynokia);
void Nok3310_erase_x(int8 xnokia);
void Nok3310_putc(int8 cvar);
void table_to_Nok3310(int8 charsel);
void Nok3310_print_degree(void);
void Nok3310_wr_data_inv(int8 bytefornokia_data_inv);
void Nok3310_plot(int8 xnokia,int8 plot_value8, int8 plot_type);
// Character/font table. Each character takes 5 bytes (5 columns of 8 pixels): 96 rows * 5 bytes= 480 bytes
int8 const TABLE5[240]= {0x00,0x00,0x00,0x00,0x00, // 20 space
0x00,0x00,0x5f,0x00,0x00, // 21 !
0x00,0x07,0x00,0x07,0x00, // 22 "
0x14,0x7f,0x14,0x7f,0x14, // 23 #
0x24,0x2a,0x7f,0x2a,0x12, // 24 $
0x23,0x13,0x08,0x64,0x62, // 25 %
0x36,0x49,0x55,0x22,0x50, // 26 &
0x00,0x05,0x03,0x00,0x00, // 27 '
0x00,0x1c,0x22,0x41,0x00, // 28 (
0x00,0x41,0x22,0x1c,0x00, // 29 )
0x14,0x08,0x3e,0x08,0x14, // 2a *
0x08,0x08,0x3e,0x08,0x08, // 2b +
0x00,0x50,0x30,0x00,0x00, // 2c ,
0x08,0x08,0x08,0x08,0x08, // 2d -
0x00,0x60,0x60,0x00,0x00, // 2e .
0x20,0x10,0x08,0x04,0x02, // 2f /
0x3e,0x51,0x49,0x45,0x3e, // 30 0
0x00,0x42,0x7f,0x40,0x00, // 31 1
0x42,0x61,0x51,0x49,0x46, // 32 2
0x21,0x41,0x45,0x4b,0x31, // 33 3
0x18,0x14,0x12,0x7f,0x10, // 34 4
0x27,0x45,0x45,0x45,0x39, // 35 5
0x3c,0x4a,0x49,0x49,0x30, // 36 6
0x01,0x71,0x09,0x05,0x03, // 37 7
0x36,0x49,0x49,0x49,0x36, // 38 8
0x06,0x49,0x49,0x29,0x1e, // 39 9
0x00,0x36,0x36,0x00,0x00, // 3a :
0x00,0x56,0x36,0x00,0x00, // 3b ;
0x08,0x14,0x22,0x41,0x00, // 3c <
0x14,0x14,0x14,0x14,0x14, // 3d =
0x00,0x41,0x22,0x14,0x08, // 3e >
0x02,0x01,0x51,0x09,0x06, // 3f ?
0x32,0x49,0x79,0x41,0x3e, // 40 @
0x7e,0x11,0x11,0x11,0x7e, // 41 A
0x7f,0x49,0x49,0x49,0x36, // 42 B
0x3e,0x41,0x41,0x41,0x22, // 43 C
0x7f,0x41,0x41,0x22,0x1c, // 44 D
0x7f,0x49,0x49,0x49,0x41, // 45 E
0x7f,0x09,0x09,0x09,0x01, // 46 F
0x3e,0x41,0x49,0x49,0x7a, // 47 G
0x7f,0x08,0x08,0x08,0x7f, // 48 H
0x00,0x41,0x7f,0x41,0x00, // 49 I
0x20,0x40,0x41,0x3f,0x01, // 4a J
0x7f,0x08,0x14,0x22,0x41, // 4b K
0x7f,0x40,0x40,0x40,0x40, // 4c L
0x7f,0x02,0x0c,0x02,0x7f, // 4d M
0x7f,0x04,0x08,0x10,0x7f, // 4e N
0x3e,0x41,0x41,0x41,0x3e // 4f O
};
int8 const TABLE6[240]= {0x7f,0x09,0x09,0x09,0x06, // 50 P
0x3e,0x41,0x51,0x21,0x5e, // 51 Q
0x7f,0x09,0x19,0x29,0x46, // 52 R
0x46,0x49,0x49,0x49,0x31, // 53 S
0x01,0x01,0x7f,0x01,0x01, // 54 T
0x3f,0x40,0x40,0x40,0x3f, // 55 U
0x1f,0x20,0x40,0x20,0x1f, // 56 V
0x3f,0x40,0x38,0x40,0x3f, // 57 W
0x63,0x14,0x08,0x14,0x63, // 58 X
0x07,0x08,0x70,0x08,0x07, // 59 Y
0x61,0x51,0x49,0x45,0x43, // 5a Z
0x00,0x7f,0x41,0x41,0x00, // 5b [
0x02,0x04,0x08,0x10,0x20, // 5c /
0x00,0x41,0x41,0x7f,0x00, // 5d ]
0x04,0x02,0x01,0x02,0x04, // 5e
0x40,0x40,0x40,0x40,0x40, // 5f --
0x00,0x01,0x02,0x04,0x00, // 60
0x20,0x54,0x54,0x54,0x78, // 61 a
0x7f,0x48,0x44,0x44,0x38, // 62 b
0x38,0x44,0x44,0x44,0x20, // 63 c
0x38,0x44,0x44,0x48,0x7f, // 64 d
0x38,0x54,0x54,0x54,0x18, // 65 e
0x08,0x7e,0x09,0x01,0x02, // 66 f
0x0c,0x52,0x52,0x52,0x3e, // 67 g
0x7f,0x08,0x04,0x04,0x78, // 68 h
0x00,0x44,0x7d,0x40,0x00, // 69 i
0x20,0x40,0x44,0x3d,0x00, // 6a j
0x7f,0x10,0x28,0x44,0x00, // 6b k
0x00,0x41,0x7f,0x40,0x00, // 6c l
0x7c,0x04,0x18,0x04,0x78, // 6d m
0x7c,0x08,0x04,0x04,0x78, // 6e n
0x38,0x44,0x44,0x44,0x38, // 6f o
0x7c,0x14,0x14,0x14,0x08, // 70 p
0x08,0x14,0x14,0x18,0x7c, // 71 q
0x7c,0x08,0x04,0x04,0x08, // 72 r
0x48,0x54,0x54,0x54,0x20, // 73 s
0x04,0x3f,0x44,0x40,0x20, // 74 t
0x3c,0x40,0x40,0x20,0x7c, // 75 u
0x1c,0x20,0x40,0x20,0x1c, // 76 v
0x3c,0x40,0x30,0x40,0x3c, // 77 w
0x44,0x28,0x10,0x28,0x44, // 78 x
0x0c,0x50,0x50,0x50,0x3c, // 79 y
0x44,0x64,0x54,0x4c,0x44, // 7a z
0x00,0x08,0x36,0x41,0x00, // 7b {
0x00,0x00,0x7f,0x00,0x00, // 7c |
0x00,0x41,0x36,0x08,0x00, // 7d }
0x10,0x08,0x08,0x10,0x08, // 7e ~
0x78,0x46,0x41,0x46,0x78 // 7f
};
void Init_Nok3310(void) { // Initialise graphic lcd module
tris_nokia3310 =0x00; // bits are all outputs
delay_us(200);
nokia3310_dc=1; // bytes are stored in the display data ram, address counter, incremented automatically
nokia3310_cs=1; // chip disabled
delay_us(200);
nokia3310_res=0; // reset chip during 250ms
delay_ms(250);
nokia3310_res=1;
Nok3310_wr_cmd(0x21); // set extins extended instruction set
Nok3310_wr_cmd(0xc8); // Vop v1: 0xc8 (for 3V)// v2: 0xa0 (for 3V)
Nok3310_wr_cmd(0x13); // bias
Nok3310_wr_cmd(0x20); // horizontal mode from left to right, X axe are incremented automatically , 0x22 for vertical addressing
Nok3310_wr_cmd(0x09); // all on
delay_ms(50);
Nok3310_clean_ddram(); // reset DDRAM, otherwise the lcd is filled with random pixels
delay_ms(10);
Nok3310_wr_cmd(0x08); // mod control
delay_ms(10);
Nok3310_wr_cmd(0x0c);
}
#define Init_display Init_Nok3310 // Init function for screen (LCD or graphic)
void Nok3310_clean_ddram(void) { // Clean screen
int16 ddram;
Nok3310_gotoxy(0,0); // 84*48/8 = 504 bytes to clear
for (ddram=504;ddram>0;ddram--){
Nok3310_wr_data(0x00);
}
}
#define Clear_display Nok3310_clean_ddram
void Nok3310_wr_data(int8 bytefornokia_data) { // Send data to display
nokia3310_dc=1; // set display to receive data
nokia3310_cs=0; // chip enale
Nok3310_wr_dorc(bytefornokia_data); // clock byte in
nokia3310_cs=1; // chip disabled
}
void Nok3310_wr_cmd(int8 bytefornokia_command) { // Send command to display
nokia3310_dc=0; // set display to receive command
nokia3310_cs=0;
Nok3310_wr_dorc(bytefornokia_command);
nokia3310_cs=1;
}
void Nok3310_wr_dorc(int8 bytefornokia) { // clock byte in display
char caa;
for (caa=8;caa>0;caa--) { // clock in 8 bits of byte
nokia3310_sck=0; // clock low
delay_us(2);
if ((bytefornokia&0x80)==0){nokia3310_sdi=0;} // set bit accordingly
else {nokia3310_sdi=1;}
nokia3310_sck=1; // clock high to clock bit in display
bytefornokia=bytefornokia<<1; // shift byte for next bit
}
}
void Nok3310_gotoxy(int8 xnokia, int8 ynokia) { // Nokia LCD 3310 Position cursor (0,0 top left corner)
Nok3310_wr_cmd(0x40|(ynokia&0x07)); // Y axe initialisation: 0100 0yyy
Nok3310_wr_cmd(0x80|(xnokia&0x7f)); // X axe initialisation: 1xxx xxxx
}
#define display_gotoxy Nok3310_gotoxy // gotoxy function for screen
void Nok3310_erase_y(int8 ynokia) { // Erase a ligne (84x8 pixels)
Nok3310_gotoxy(0,ynokia);
printf(Nok3310_putc," ");
}
void Nok3310_erase_x(int8 xnokia) { // Erase a column (6x48 pixels)
int8 column;
for (column=0;column!=6;column++) { // repeat for 6 lines (or bytes)
Nok3310_gotoxy(xnokia,column);
Nok3310_wr_data(0x00); // blank pixels in 6 rows
Nok3310_wr_data(0x00);
Nok3310_wr_data(0x00);
Nok3310_wr_data(0x00);
Nok3310_wr_data(0x00);
Nok3310_wr_data(0x00);
}
}
void Nok3310_putc(int8 cvar) { // Write 1 character to LCD
switch (cvar) {
case '\f':
Nok3310_clean_ddram(); // Clear screen
delay_ms(20);
break;
default : table_to_Nok3310(cvar);
}
}
#define display_putc Nok3310_putc // Putc function for output to screen
void table_to_Nok3310(int8 charsel) { // extract ascii character from tables & write to LCD
int8 char_row, charpos, chardata;
if (charsel<0x20)return; // check character are in the table
if (charsel>0x7f)return;
for (char_row=0;char_row<5;char_row++) { // 5 bytes
if (charsel<0x50) { // use TABLE5
charpos=(((charsel&0xff)-0x20)*5);
chardata=TABLE5[(charpos+char_row)];
} else if (charsel>0x4f) { // use TABLE6
charpos=(((charsel&0xff)-0x50)*5);
chardata=TABLE6[(charpos+char_row)];
}
Nok3310_wr_data(chardata); // send data to nokia
}
Nok3310_wr_data(0x00);
}
void Nok3310_print_degree(void) { // Writes degree character to display
Nok3310_wr_data(0x00);
Nok3310_wr_data(0x06);
Nok3310_wr_data(0x09);
Nok3310_wr_data(0x09);
Nok3310_wr_data(0x06);
Nok3310_wr_data(0x00);
}
#define display_print_degree Nok3310_print_degree
void Nok3310_wr_data_inv(int8 bytefornokia_data_inv) { // Writes inverted data white pixels on black background
int8 caa;
nokia3310_dc=1;
nokia3310_cs=0;
for (caa=8;caa>0;caa--) {
nokia3310_sck=0;
delay_us(2);
if ((bytefornokia_data_inv&0x01)==0){
nokia3310_sdi=0;
} else {
nokia3310_sdi=1;
}
nokia3310_sck=1;
bytefornokia_data_inv=bytefornokia_data_inv>>1;
}
nokia3310_cs=1;
}
void Nok3310_plot(int8 xnokia,int8 plot_value8, int8 plot_type) { // plot_type: 1=dot grap, 0=bar graph
int8 i;
int32 plot_value32, plot_umsb,plot_lmsb,plot_ulsb,plot_llsb;
plot_value32=0;
if (plot_type) {
plot_value32|=1; // dotgraph
}
for (i=0;i!=plot_value8;i++) {
if (!plot_type) {
plot_value32|=1; // bargraph
}
plot_value32<<=1;
}
plot_value32|=2; // bottom line is always filled
plot_llsb=(plot_value32&0xff);
plot_ulsb=((plot_value32>>8)&0xff);
plot_lmsb=((plot_value32>>16)&0xff);
plot_umsb=((plot_value32>>24)&0xff);
Nok3310_gotoxy(xnokia,1);
Nok3310_wr_data_inv(plot_umsb);
Nok3310_gotoxy(xnokia,2);
Nok3310_wr_data_inv(plot_lmsb);
Nok3310_gotoxy(xnokia,3);
Nok3310_wr_data_inv(plot_ulsb);
Nok3310_gotoxy(xnokia,4);
Nok3310_wr_data_inv(plot_llsb);
}
#endif
Fig. 1b Board 1 with LCD display
First we need to setup the hardware connections to the display. The library needs to know which port the display will be connected to, and then just assign the various signals to specific bits of the 8 bits bus.
Each function then gets its prototype defined.
Next is the character table (equivalent to a bitmap font). Each character is displayed on 6x8 pixels. The last column is blank, so each character is separated by a blank column. Which means we actually need 5 bytes per character (5 columns). Each byte in a character has the pixels to show (when the bit is set to one) or blank in the column. The lsb bit is always blank, so each line is separated by a blank pixel.
For example, the character 0x7e,0x11,0x11,0x11,0x7e would display as A: first column as 7e, second 11...
It is therefore easy to design custom made symbols or fonts, like in the Nok3310_Print_degree function.
Then a list of functions is available to control the display:
- Init_Nok3310: Reset and initialise the Nokia 3310 display.
- Nok3310_clean_ddram: Clear the screen: all pixels are blanked.
- Nok3310_wr_data: send a byte to data register of Nokia 3310
- Nok3310_wr_cmd: send a byte to command register of Nokia 3310
- Nok3310_wr_dorc: output a byte to the Nokia 3310 display using a software emulation of SPI
- Nok3310_gotoxy: position the pointer on the display. x and y are in pixels. 0,0 is upper left corner.
- Nok3310_erase_y: clears a row character
- Nok3310_erase_x: clears a column of character
- Nok3310_putc: send a character at the current pointer of Nokia 3310
- table_to_Nok3310: send a character from the font table to the display
- Nok3310_print_degree: print the degree symbol
- Nok3310_wr_data_inv: display in inverted mode a character
- Nok3310_plot: draw a plot of data (bargraph or dot type)
Compile the project and program the target with a PicKIT 2 programmer for example. Connect the processor board to the graphic LCD display board on port B.
Power up the boards.
The display will show a counter incrementing 4 times a second on the 1st line.
To change display type, just use board 1 or change the condition that load the display library and recompile. The program is then set for an alphanumeric LCD display.
Files and links:
Tutorial 6 source files.Processor board 4.
LCD Display board.
Graphic LCD board.

