# PIC18F4550 Interfacing with LCD using XC8 compiler

#### devilarm123

Joined Dec 26, 2017
33
HERES MY CODE:

#include <xc.h>
#include "delays.h"
#include "lcd.h"
unsigned char key,outchar;
char *Message1 [ ] = "Enter PIN number : "; // Defining a 20 char string

// ---- Main Program ---------------------------------------------------------------
void main(void)
{
int i;
lcd_init();

while(1)
{
lcd_write_cmd(0x80); // Move cursor to line 1 position 1
for (i = 0; i < 20; i++) //for 20 char LCD module
{
outchar = Message1;
lcd_write_data(outchar); // write character data to LCD
}

delay_ms(1000); // wait 1 second
lcd_write_cmd(0x01); // 00000001 Clear Display instruction
}
}

Problem encountered:
lcd doesn't display outchar, and shows first row blackbox(there shouldnt be any contrast issues).
only managed to make the display something after I change Message1[] = ' c ' ; //(single character)

Questions I wish to clarify:
1.Is there a way to use lcd_write_data() to display a var/string?

Any help is appreciated!

#### Ian Rogers

Joined Dec 12, 2012
820
Have you edited LCD.h to suit your hardware connections.... Also if you are connecting to portb then disable the analogue module to those pins.. You haven't set ANY config bits..

Once your code is working, you can write two routines putchar and getchar ( skeletal code is in the PLIB folder ) and use printf and scanf to write to the screen and get a key input!

#### devilarm123

Joined Dec 26, 2017
33
This the header files i included:
C:
/*
* File:   lcd utilities.c
*
* Created on 13 January, 2016, 10:28 AM
*/

//#include "LCD.H"    // Include file is located in the project directory

#include <xc.h>
#define _XTAL_FREQ 48000000
#define LCD_RS PORTDbits.RD6    //  Register Select on LCD
#define LCD_EN PORTDbits.RD4    //  Enable on LCD controller
#define LCD_WR PORTDbits.RD5    //  Write on LCD controller
void lcd_strobe(void);

//--- Function for writing a command byte to the LCD in 4 bit mode -------------

void lcd_write_cmd(unsigned char cmd)
{
unsigned char temp2;
LCD_RS = 0;                    // Select LCD for command mode
__delay_ms(4);                // 40us delay for LCD to settle down
temp2 = cmd;
temp2 = temp2 >> 4;            // Output upper 4 bits, by shifting out lower 4 bits
PORTD = temp2 & 0x0F;        // Output to PORTD which is connected to LCD

__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing
lcd_strobe();

__delay_ms(8);            // 10ms - Delay at least 1 ms after strobing

temp2 = cmd;                // Re-initialise temp2
PORTD = temp2 & 0x0F;        // Mask out upper 4 bits

__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing
lcd_strobe();
__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing

}

//---- Function to write a character data to the LCD ---------------------------

void lcd_write_data(char data)
{
char temp1;

LCD_RS = 1;                    // Select LCD for data mode
__delay_ms(4);                // 40us delay for LCD to settle down

temp1 = data;
temp1 = temp1 >> 4;
PORTD = temp1 & 0x0F;
__delay_ms(8);
LCD_RS = 1;
__delay_ms(8);            //_-_ strobe data in

lcd_strobe();
__delay_ms(8);

temp1 = data;
PORTD = temp1 & 0x0F;
__delay_ms(10);
LCD_RS = 1;
__delay_ms(10);             //_-_ strobe data in

lcd_strobe();
__delay_ms(10);
}

//-- Function to generate the strobe signal for command and character----------

void lcd_strobe(void)            // Generate the E pulse
{
LCD_EN = 1;                    // E = 0
__delay_ms(8);            // 10ms delay for LCD_EN to settle
LCD_EN = 0;                    // E = 1
__delay_ms(8);            // 10ms delay for LCD_EN to settle
}

//---- Function to initialise LCD module ----------------------------------------
void lcd_init(void)
{
int i;
TRISD = 0x00;
PORTD = 0x00;                // PORTD is connected to LCD data pin
LCD_EN = 0;
LCD_RS = 0;                    // Select LCD for command mode
LCD_WR = 0;                    // Select LCD for write mode

for(i=0;i<100;i++)
__delay_ms(10);            // Delay a total of 1 s for LCD module to
// finish its own internal initialisation

/* The data sheets warn that the LCD module may fail to initialise properly when
power is first applied. This is particularly likely if the Vdd
supply does not rise to its correct operating voltage quickly enough.

It is recommended that after power is applied, a command sequence of
3 bytes of 30h be sent to the module. This will ensure that the module is in
8-bit mode and is properly initialised. Following this, the LCD module can be
switched to 4-bit mode.
*/

lcd_write_cmd(0x33);
lcd_write_cmd(0x32);

lcd_write_cmd(0x28);        // 001010xx  Function Set instruction
// DL=0 :4-bit interface,N=1 :2 lines,F=0 :5x7 dots

lcd_write_cmd(0x0E);        // 00001110  Display On/Off Control instruction
// D=1 :Display on,C=1 :Cursor on,B=0 :Cursor Blink on

lcd_write_cmd(0x06);        // 00000110  Entry Mode Set instruction
// I/D=1 :Increment Cursor position
// S=0 : No display shift

lcd_write_cmd(0x01);        // 00000001 Clear Display instruction

__delay_ms(10);            // 20 ms delay

}

[B]As well as lcd.h header file:[/B]
/*  file : lcd.h
*    LCD interface header file
*/

/* intialize the LCD - call before anything else */
extern void lcd_init(void);

/* write a byte to the LCD in 4 bit mode */
extern void lcd_write_cmd(unsigned char cmd);

//extern void lcd_write(unsigned char i);
extern void lcd_write_data(char data);

#### devilarm123

Joined Dec 26, 2017
33
I'm connecting through port D. and I don't really understand the code in the header file to be able to change it.

Add my code is runnable, no errors were displayed. Just that the lcd isnt responding(shows black boxes first row)

Heres my code now:

C:
#include <xc.h>
#include "delays.h"
#include "lcd.h"
unsigned char key,outchar;
char *Message1 [ ] = "Enter PIN number :  ";      // Defining a 20 char string

void main(void)
{
TRISD = 0x00;

int i;
lcd_init();

while(1)
{
lcd_write_cmd(0x80);            // Move cursor to line 1 position 1
for (i = 0; i < 20; i++)        //for 20 char LCD module
{
outchar = Message1[I];
lcd_write_data(outchar);     // write character data to LCD
}

delay_ms(1000);                 // wait 1 second
lcd_write_cmd(0x01);            // 00000001 Clear Display instruction
}
}

#### Ian Rogers

Joined Dec 12, 2012
820
Are you sure the peripheral clock is at 48Mhz??? I very much doubt it What crystal have you got?

As you have not specified any config bits the sysclock will be the same as the crystal...BUT!! running a 48Mhz crystal will be too unstable.. To run at 48Mhz you'll need a stable clock input and the default setting for the FOSC isn't going to be what you expect!

If the system is calculating delays for 48Mhz but running at a lower speed, then this will cause massive delays...

Why don't you tell me what you have and I'll give you the settings... At least we have a base to start from.

#### AlbertHall

Joined Jun 4, 2014
10,915
This the header files i included:
C:
    LCD_RS = 0;                    // Select LCD for command mode
__delay_ms(4);                // 40us delay for LCD to settle down
temp2 = cmd;
temp2 = temp2 >> 4;            // Output upper 4 bits, by shifting out lower 4 bits
PORTD = temp2 & 0x0F;        // Output to PORTD which is connected to LCD
This code will not work correctly. 'temp2 & 0x0F' will set the upper four bits of temp2 to zero then this value is written to PORTD which will set the upper four bits of the port to zero. This includes the control lines for the LCD. This error occurs multiple times through the code. You need to write only the lower four bits but then you need to watch out for the read-modify-write-problem so direct the writes to LATD not PORTD.

#### devilarm123

Joined Dec 26, 2017
33
okay heres my s
Are you sure the peripheral clock is at 48Mhz??? I very much doubt it What crystal have you got?

As you have not specified any config bits the sysclock will be the same as the crystal...BUT!! running a 48Mhz crystal will be too unstable.. To run at 48Mhz you'll need a stable clock input and the default setting for the FOSC isn't going to be what you expect!

If the system is calculating delays for 48Mhz but running at a lower speed, then this will cause massive delays...

Why don't you tell me what you have and I'll give you the settings... At least we have a base to start from.

okay here's my schematics, they are connected through a rainbow cable

#### Attachments

• 57.6 KB Views: 9
• 59 KB Views: 8

#### Ian Rogers

Joined Dec 12, 2012
820
This code will not work correctly. 'temp2 & 0x0F' will set the upper four bits of temp2 to zero then this value is written to PORTD which will set the upper four bits of the port to zero. This includes the control lines for the LCD. This error occurs multiple times through the code. You need to write only the lower four bits but then you need to watch out for the read-modify-write-problem so direct the writes to LATD not PORTD.
This of course is true.. However! The code will still work for commands, so the display would initialise... But it isn't so there are other issues..

#### devilarm123

Joined Dec 26, 2017
33
This code will not work correctly. 'temp2 & 0x0F' will set the upper four bits of temp2 to zero then this value is written to PORTD which will set the upper four bits of the port to zero. This includes the control lines for the LCD. This error occurs multiple times through the code. You need to write only the lower four bits but then you need to watch out for the read-modify-write-problem so direct the writes to LATD not PORTD.
I'm not sure I quite understand this, so in layman terms I just need to change to "LATD = temp2 & 0x00" ?

#### AlbertHall

Joined Jun 4, 2014
10,915
This of course is true.. However! The code will still work for commands, so the display would initialise... But it isn't so there are other issues..
No. One of the lines which would be pulled low is the enable line.

#### AlbertHall

Joined Jun 4, 2014
10,915
I'm not sure I quite understand this, so in layman terms I just need to change to "LATD = temp2 & 0x00" ?
temp2 = temp2 & 0x0f; (temp2 &= 0x0f)
LATD = LATD & 0xf0; (LATD &= 0xf0)
LATD = LATD | temp2; (LATD |= temp2)
[EDIT - for correctness]

#### Ian Rogers

Joined Dec 12, 2012
820
Here is the thing.. The micro has a 20Mhz FOSC so we'll need to set the system clock settings to suit.

When you send data to the LCD you are using the bottom four bits for DATA and the highest 3 for CMD so Albert is saying you need to preserve the CMD bits..
No. One of the lines which would be pulled low is the enable line.
Yes! This is also true..
LATD will be better but I would have just set Temp2 as the port buffer and blit it to the port.

I could show my code, but mine is hard codded to LATB Upper nibble....

#### devilarm123

Joined Dec 26, 2017
33
temp2 = temp2 & 0x0f; (temp2 &= 0x0f)
LATD = LATD & 0xf0; (LATD &= 0xf0)
LATD = LATD | temp2; (LATD |= temp2)
[EDIT - for correctness]

C:
void lcd_write_cmd(unsigned char cmd)
{
unsigned char temp2;
LCD_RS = 0;                    // Select LCD for command mode
__delay_ms(4);                // 40us delay for LCD to settle down
temp2 = cmd;
temp2 = temp2 >> 4;            // Output upper 4 bits, by shifting out lower 4 bits
temp2 = temp2 & 0x0f;
LATD = LATD & 0xf0;
LATD = LATD | temp2;        // Output to PORTD which is connected to LCD

__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing
lcd_strobe();

__delay_ms(8);            // 10ms - Delay at least 1 ms after strobing

temp2 = cmd;                // Re-initialise temp2
PORTD = temp2 & 0x0F;        // Mask out upper 4 bits

__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing
lcd_strobe();
__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing

}
Okay I changed the code, please tell me if I did anything wrong

Last edited by a moderator:

#### devilarm123

Joined Dec 26, 2017
33
Here is the thing.. The micro has a 20Mhz FOSC so we'll need to set the system clock settings to suit.

When you send data to the LCD you are using the bottom four bits for DATA and the highest 3 for CMD so Albert is saying you need to preserve the CMD bits..

Yes! This is also true..
LATD will be better but I would have just set Temp2 as the port buffer and blit it to the port.

I could show my code, but mine is hard codded to LATB Upper nibble....

The problem I have is displaying strings. I dont know why I am only able to display 'c' but not "a long string"

e.g
void main(void)
{
char i='c';
lcd_init();
lcd_write_data(i);
}
//Code above works

void main(void)
{
char i="long string";
lcd_init();
lcd_write_data(i);
}
Code above doesnt work

both able to complie and run without errors

#### AlbertHall

Joined Jun 4, 2014
10,915
• temp2 = cmd; // Re-initialise temp2
• PORTD = temp2 & 0x0F; // Mask out upper 4 bits
The section you wrote using LATD looks OK, but you will have to repeat that for every write to PORTD

#### devilarm123

Joined Dec 26, 2017
33
The section you wrote using LATD looks OK, but you will have to repeat that for every write to PORTD

C:
#include <xc.h>
#define _XTAL_FREQ 48000000
#define LCD_RS PORTDbits.RD6    //  Register Select on LCD
#define LCD_EN PORTDbits.RD4    //  Enable on LCD controller
#define LCD_WR PORTDbits.RD5    //  Write on LCD controller
void lcd_strobe(void);

//--- Function for writing a command byte to the LCD in 4 bit mode -------------

void lcd_write_cmd(unsigned char cmd)
{
unsigned char temp2;
LCD_RS = 0;                    // Select LCD for command mode
__delay_ms(4);                // 40us delay for LCD to settle down
temp2 = cmd;
temp2 = temp2 >> 4;            // Output upper 4 bits, by shifting out lower 4 bits
temp2 = temp2 & 0x0f;
LATD = LATD & 0xf0;
LATD = LATD | temp2;        // Output to PORTD which is connected to LCD

__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing
lcd_strobe();

__delay_ms(8);            // 10ms - Delay at least 1 ms after strobing

temp2 = cmd;                // Re-initialise temp2
LATD = LATD & 0xf0;
LATD = LATD | temp2;        // Mask out upper 4 bits

__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing
lcd_strobe();
__delay_ms(8);            // 10ms - Delay at least 1 ms before strobing

}

//---- Function to write a character data to the LCD ---------------------------

void lcd_write_data(char data)
{
char temp1;

LCD_RS = 1;                    // Select LCD for data mode
__delay_ms(4);                // 40us delay for LCD to settle down

temp1 = data;
temp1 = temp1 >> 4;
temp1 = temp1 & 0x0f;
LATD = LATD & 0xf0;
LATD = LATD | temp1;
__delay_ms(8);
LCD_RS = 1;
__delay_ms(8);            //_-_ strobe data in

lcd_strobe();
__delay_ms(8);

temp1 = data;
LATD = LATD & 0xf0;
LATD = LATD | temp1;
__delay_ms(10);
LCD_RS = 1;
__delay_ms(10);             //_-_ strobe data in

lcd_strobe();
__delay_ms(10);
}

//-- Function to generate the strobe signal for command and character----------

void lcd_strobe(void)            // Generate the E pulse
{
LCD_EN = 1;                    // E = 0
__delay_ms(8);            // 10ms delay for LCD_EN to settle
LCD_EN = 0;                    // E = 1
__delay_ms(8);            // 10ms delay for LCD_EN to settle
}

//---- Function to initialise LCD module ----------------------------------------
void lcd_init(void)
{
int i;
TRISD = 0x00;
LATD = 0x00;                // PORTD is connected to LCD data pin
LCD_EN = 0;
LCD_RS = 0;                    // Select LCD for command mode
LCD_WR = 0;                    // Select LCD for write mode

for(i=0;i<100;i++)
__delay_ms(10);            // Delay a total of 1 s for LCD module to
// finish its own internal initialisation

/* The data sheets warn that the LCD module may fail to initialise properly when
power is first applied. This is particularly likely if the Vdd
supply does not rise to its correct operating voltage quickly enough.

It is recommended that after power is applied, a command sequence of
3 bytes of 30h be sent to the module. This will ensure that the module is in
8-bit mode and is properly initialised. Following this, the LCD module can be
switched to 4-bit mode.
*/

lcd_write_cmd(0x33);
lcd_write_cmd(0x32);

lcd_write_cmd(0x28);        // 001010xx  Function Set instruction
// DL=0 :4-bit interface,N=1 :2 lines,F=0 :5x7 dots

lcd_write_cmd(0x0E);        // 00001110  Display On/Off Control instruction
// D=1 :Display on,C=1 :Cursor on,B=0 :Cursor Blink on

lcd_write_cmd(0x06);        // 00000110  Entry Mode Set instruction
// I/D=1 :Increment Cursor position
// S=0 : No display shift

lcd_write_cmd(0x01);        // 00000001 Clear Display instruction

__delay_ms(10);            // 20 ms delay

}
Is there any header files or things I need to include? the code is runnable, but lcd still doesnt initialise

Moderators note : please use code=c as tags for C code

Last edited by a moderator:

#### AlbertHall

Joined Jun 4, 2014
10,915
lcd_init is wrong.
The comments say that cmd 30H should be sent 3 times, but the code doesn't do that.
There should be delays between the steps in the init routine which are missing.
I will look up how it should be and then "I'll be back".

#### AlbertHall

Joined Jun 4, 2014
10,915
#define LCD_RS PORTDbits.RD6 // Register Select on LCD
#define LCD_EN PORTDbits.RD4 // Enable on LCD controller
#define LCD_WR PORTDbits.RD5 // Write on LCD controller
These should also use LATD: e.g. LATDbits.RD6

More to come...

#### MrChips

Joined Oct 2, 2009
22,898
The problem I have is displaying strings. I dont know why I am only able to display 'c' but not "a long string"

e.g
void main(void)
{
char i='c';
lcd_init();
lcd_write_data(i);
}
//Code above works

void main(void)
{
char i="long string";
lcd_init();
lcd_write_data(i);
}
Code above doesnt work

both able to complie and run without errors
If the LCD is displaying single characters correctly then the lcd_init( ) is supposedly working.
You cannot write a string to the LCD using lcd_write_data(i) on its own. You need another function to write an entire string.

Check the lcd.h file for the appropriate function. If it does not have the string function you can write your own.
C:
void lcd_write_string(char *s)
{
while (*s) lcd_write_data(*s++);
}

#### AlbertHall

Joined Jun 4, 2014
10,915
lcd_init is wrong.
The comments say that cmd 30H should be sent 3 times, but the code doesn't do that.
There should be delays between the steps in the init routine which are missing.
I will look up how it should be and then "I'll be back".
Here is a new 'init' routine. Note that I do not have your hardware so this is untested code.
C:
void lcd_init(void)
{
TRISD = 0x00;
LATD = 0x00;

LCD_RS = 0; // write control bytes
__delay_ms(15); // power on delay
lcd_write_cmd(0x30);
__delay_ms(5);
lcd_write_cmd(0x30);
__delay_us(100);
lcd_write_cmd(0x30);
__delay_ms(5);
lcd_write_cmd(0x20);// set 4 bit mode
__delay_us(40);
lcd_write_cmd(0x28); // 4 bit mode, 1/16 duty, 5x8 font
lcd_write_cmd(0x08); // display off
lcd_write_cmd(0x0F); // display on, blink curson on
lcd_write_cmd(0x06); // entry mode
}