PIC18F4550 Interfacing with LCD using XC8 compiler

Thread Starter

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
774
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!
 

Thread Starter

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
*    See lcd.c for more info
*/

/* 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);
 

Thread Starter

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
774
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
9,873
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.
 

Thread Starter

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

Ian Rogers

Joined Dec 12, 2012
774
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..
 

Thread Starter

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" ?
 

Ian Rogers

Joined Dec 12, 2012
774
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....
 

Thread Starter

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:

Thread Starter

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
 

Thread Starter

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
9,873
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".
 

MrChips

Joined Oct 2, 2009
21,128
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
9,873
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
}
 
Top