Tuto. 3 - LCD Display
PIC tutorial 3 - LCD Display
Fig. 1 Board 3 with LCD Display
Description:
For the third tutorial, we will start looking at LCD displays. We will build a library to drive generic alphanumeric LCD display, of different type: 1, 2 or 4 lines, and up to 40 caracters per line.
The tutorial will update the display with the value of a counter, displayed in hexadecimal and decimal, updated every 250 ms.
The boards can be powered by one of the PSU boards (powered from USB port), or between 3.3 to 5V, depending on the display used.
Requirements:
Processor boardAny board (board 3 in example)
Extension(s)LCD Display
Power supplyPSU 1, 2 or 3. Depends on board used.
LanguageCCS C
ProgrammerPicKIT 2
Program:
#define board_id 3
#include "lib\mainboard.c" // Load board module
#include "lib\lcd_lib.c"
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 defined the processor board used, and loads the library files.
We then load the LCD library, which defines functions to communicate with the LCD display.
The main program initialise the board with the function boot_up, reset the counter and initialise the display.
It then enters a loop that display the counter in Hexadecimal and decimal using the built-in function printf. Printf is told to use the display_putc function to output each caracter. Display_putc sends a caracter to the LCD display at the current position of the cursor. The counter is then incremented and the programs counts for 250ms before looping again.
The board used for this tutorial is board 3, with a PIC 16F873A processor. The library for the board is similar to the one in tutorial 2 and is included in the zip file.
Now, the LCD library. The LCD display is implemented differently (port A or B) on different processor boards.
#ifndef _LCD_LIB
#define _LCD_LIB
#ifndef DEBUG_MODE
#define DEBUG_MODE 0
#endif
#ifndef lcd_type
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#endif
#ifndef lcd_line_two
#define lcd_line_two 0x40 // LCD RAM address for the second line
#endif
#ifndef lcd_char_line
#define lcd_char_line 20 // number of characters per line
#endif
struct lcd_pin_map { // This structure is overlayed
int8 data : 4; // on to an I/O port to gain
int1 rs; // access to the LCD pins.
int1 unused; // The bits are allocated from
int1 rw; // low order up.
int1 enable;
} lcd_port;
// 1: 16F819, 2: 16F628, 3: 16F873A, 4: 18F2320
#if (board_id==1)
#locate lcd_port = pa // LCD on port A
#define set_tris_lcd(x) set_tris_a(x)
#elif (board_id==2)
#locate lcd_port = pa // LCD on port A
#define set_tris_lcd(x) set_tris_a(x)
#elif (board_id==3)
#locate lcd_port = pb // LCD on port B
#define set_tris_lcd(x) set_tris_b(x)
#elif (board_id==4)
#locate lcd_port = pb // LCD on port B
#define set_tris_lcd(x) set_tris_b(x)
#else
#error "Board board_id not supported"
#endif
int8 const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6}; // These bytes need to be sent to the LCD to start it up.
struct lcd_pin_map const LCD_WRITE = {0,0,0,0,0}; // For write mode all pins are out
struct lcd_pin_map const LCD_READ = {15,0,0,0,0}; // For read mode data pins are in
// function prototypes
void Init_LCD(void);
void lcd_send_nibble( int8 n );
void lcd_send_byte(int1 address, int8 n);
int8 lcd_read_byte(void);
void lcd_gotoxy(int8 x, int8 y);
void lcd_putc(int8 c);
int8 lcd_getc( int8 x, int8 y);
// Library functions
void Init_LCD() {
int8 i;
set_tris_lcd(LCD_WRITE);
lcd_port.rs = 0;
lcd_port.rw = 0;
lcd_port.enable = 0;
delay_ms(15);
for(i=1;i<=3;++i) {
lcd_send_nibble(3);
delay_ms(5);
}
lcd_send_nibble(2);
for(i=0;i<=3;++i)
lcd_send_byte(0,LCD_INIT_STRING[i]);
}
#define Init_display Init_LCD // Init function for screen
void lcd_send_nibble( int8 n ) {
lcd_port.data = n;
delay_cycles(4);
lcd_port.enable = 1;
delay_us(2);
lcd_port.enable = 0;
}
void lcd_send_byte(int1 address, int8 n ) { // Send byte as 2 4bits word (address: 0=cmd, 1=data)
lcd_port.rs = 0;
while ( bit_test(lcd_read_byte(),7) ); // Wait for display to be ready
lcd_port.rs = address;
delay_cycles(4);
lcd_port.rw = 0;
delay_cycles(4);
lcd_port.enable = 0;
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
int8 lcd_read_byte() { // Reads byte from display
int8 low,high;
set_tris_lcd(LCD_READ);
lcd_port.rw = 1;
delay_cycles(4);
lcd_port.enable = 1;
delay_cycles(4);
high = lcd_port.data;
lcd_port.enable = 0;
delay_cycles(4);
lcd_port.enable = 1;
delay_us(1);
low = lcd_port.data;
lcd_port.enable = 0;
set_tris_lcd(LCD_WRITE);
return( (high<<4) | low);
}
void lcd_gotoxy( int8 x, int8 y) { // Position cursor (0,0 top left corner)
int8 address;
switch(y) {
case 0 : address=0;
break;
case 1 : address=lcd_line_two;
break;
case 2 : address=lcd_char_line;
break;
case 3 : address=lcd_line_two+lcd_char_line;break;
}
address+=x;
lcd_send_byte(0,0x80|address);
}
#define display_gotoxy lcd_gotoxy // gotoxy function for screen
void lcd_putc( int8 c) { // Send character to display
switch (c) {
case '\f' : lcd_send_byte(0,1); // Clear screen
delay_ms(20);
break;
case '\n' : lcd_gotoxy(0,1); // Goto to second line
break;
case '\b' : lcd_send_byte(0,0x10); // Shift screen
break;
default : lcd_send_byte(1,c); // Send character as data
break;
}
}
#define display_putc lcd_putc // Putc function for output to screen
int8 lcd_getc( int8 x, int8 y) { // Reads data from display at address x,y
int8 value;
lcd_gotoxy(x,y);
while ( bit_test(lcd_read_byte(),7) ); // wait until busy flag is low
lcd_port.rs=1;
value = lcd_read_byte();
lcd_port.rs=0;
return(value);
}
void lcd_print_degree(void) { // Writes degree character to display
lcd_putc('ß');
}
#define display_print_degree lcd_print_degree // Putc function for output to screen
#endif
The library initialise some variables to default values if not already defined in the main program. It defines the type of display, the number of lines and the number of caracters per line. The variables can be defined in the main program to suit the LCD display used, or in the library to change the default display type. The standard LCD displays are organised as 2 lines in memory. Even if the display has 4 lines, it is still organised as 2 lines of caracters in memory: once the first line is full, the next bytes would be displayed on line 3 of the display, but the caracters belong to the memory area of line 1. lcd_line_two defined the start address of line 2 in the display memory (0x40 by default).
The library then defines how the display is connected to the processor. The LCD display board is connected to an 8 bits port of the processor. The library defines the lcd_pin_map structure to fit the connection to the display: bits 0-3 are the data lines, then bit 4 is rs, 5 not used, 6 rw and 7 enable. It is easy to match the connection of any LCD board to that structure. The structure is named lcd_port. Next, it needs to be maped to a processor port.
Using the board_id variable, the library maps lcd_port to port A or port B of the processor.
A set of functions is then defined to control the display. They are not all needed, but the main ones are:
- Init_LCD initialize the LCD display. It is the first function to call when using the display.
- lcd_gotoxy set the cursor position to X and Y (Top left corner is position 0,0).
- lcd_putc sends a caracter to the display. Special caracters have special functions: \f clears the screen, \n go to the second line,...
- lcd_print_degree prints a degree symbol to the display.
The main functions are also defined with a generic name, used by all display libraries (Alphanumeric or graphical LCD display. It makes the main program the same, whatever display is used).
For example, the main program uses the init_display function, and not Init_LCD. The same program could be used with the graphic LCD display. We would just need to load the graphic LCD library instead of the alphanumeric one.
Compile the project and program the target with a PicKIT 2 programmer for example. Connect the processor board to the LCD display board on port B.
Power up the boards.
The display will display un incrementing counter in hexadecimal and decimal format.
Files and links:
Tutorial 3 source files.Processor boards.
LCD Display board.
LCD Display caracters table.

