PIC 18f4550 Basic 2x8 LCD Character Display Help

Thread Starter

Micro9000

Joined Sep 22, 2009
10
This is my first time programming for a character LCD, and I am having a bit of trouble getting this 2x8 LCD to work with my PIC18f4550. I am using MPLAB v8.76 with C18 Lite compiler (for programming in C).

I will be programming the LCD in 4-bits at a time (Info about LCD below).

Here is a diagram of the connections:



The connections are as follows:
RB5 --> RS
RB4 --> R/W
RB3 --> DB7
RB2 --> DB6
RB1 --> DB5
RB0 --> DB4
VDD --> VDD
VSS --> VSS
RD7 --> E

Here is the data sheet for the LCD I am using: http://www.newhavendisplay.com/specs/NHD-0208BZ-RN-GBW.pdf

It is a 2x8 14pin (no backlight) character LCD display

The data sheet provides me with the following code layout for the initialization:

Rich (BB code):
4-bit Initialization: 
/**********************************************************/ 
void command(char i) 
{ 
      P1 = i;                       //put data on output Port 
      D_I =0;                       //D/I=LOW : send instruction 
      R_W =0;                       //R/W=LOW : Write      
      Nybble();                     //Send lower 4 bits 
      i = i<<4;                     //Shift over by 4 bits 
      P1 = i;                       //put data on output Port 
      Nybble();                     //Send upper 4 bits 
} 
/**********************************************************/ 
void write(char i) 
{ 
      P1 = i;                       //put data on output Port 
      D_I =1;                       //D/I=HIGH : send data 
      R_W =0;                       //R/W=LOW : Write     
      Nybble();                     //Clock lower 4 bits 
      i = i<<4;                     //Shift over by 4 bits 
      P1 = i;                       //put data on output Port 
      Nybble();                     //Clock upper 4 bits 
} 
/**********************************************************/ 
void Nybble() 
{ 
      E = 1; 
      Delay(1);                     //enable pulse width  >= 300ns 
      E = 0;                        //Clock enable: falling edge 
} 
/**********************************************************/ 
void init() 
{ 
      P1 = 0; 
      P3 = 0; 
      Delay(100);                   //Wait >15 msec after power is applied 
      P1 = 0x30;                    //put 0x30 on the output port 
      Delay(30);                    //must wait 5ms, busy flag not available 
      Nybble();                     //command 0x30 = Wake up  
      Delay(10);                    //must wait 160us, busy flag not available 
      Nybble();                     //command 0x30 = Wake up #2 
      Delay(10);                    //must wait 160us, busy flag not available 
      Nybble();                     //command 0x30 = Wake up #3 
      Delay(10);                    //can check busy flag now instead of delay 
      P1= 0x20;                     //put 0x20 on the output port 
      Nybble();                     //Function set: 4-bit interface 
      command(0x28);                //Function set: 4-bit/2-line 
      command(0x10);                //Set cursor 
      command(0x0F);                //Display ON; Blinking cursor 
      command(0x06);                //Entry Mode set 
} 
/**********************************************************/
I have modified the above code to:

Rich (BB code):
//turns watch dog timer off, turn low voltage programming off
#pragma config WDT=OFF, LVP=OFF, DEBUG=ON

//Internal oscillator, port function on RA6, EC used by USB 
#pragma config FOSC = INTOSCIO_EC 
#include <p18f4550.h>
#include <delays.h>

//LCD (Port B and 1 pin PortD)
#define  RS  LATBbits.LATB5 //Define LCD pinout RS
#define  R_W LATBbits.LATB4 //Define LCD pinout R/W
#define  DB7 LATBbits.LATB3 //Define LCD pinout DB7
#define  DB6 LATBbits.LATB2 //Define LCD pinout DB6
#define  DB5 LATBbits.LATB1 //Define LCD pinout DB5
#define  DB4 LATBbits.LATB0 //Define LCD pinout DB4
#define  E	 LATDbits.LATD7 //Define LCD pinout E

void command(char);
void write(char);
void Nybble(void);
void init(void);

void main()
{

	while(!OSCCONbits.IOFS);      //wait for osc stable
   	ADCON1 = 0x0F;                //make RA0 digital
  
        //data direction register all 0's mean all pins are set to output
	//all 1's mean all pins are set to operate as inputs

	TRISB = 0x00;  		    

	//main program loop
	while(1)
	{

		init();
		//after initialization call function for outputting character to LCD with appropriate command

		
	}	 
}

/**********************************************************/
//4-bit methods for LCD
/**********************************************************/
void command(char i)
{
//	P1 = i; //put data on output Port
//	D_I =0; //D/I=LOW : send instruction
	
	
	//send lower bits of char (masking bits)
	DB7 = i&08;
	DB6 = i&04; 
	DB5 = i&02;
	DB4 = i&01;

	R_W =0; //R/W=LOW : Write
	Nybble(); //Send lower 4 bits
	i = i<<4; //Shift over by 4 bits

        //since sifted sending upper bits of char
	DB7 = i&08;
	DB6 = i&04; 
	DB5 = i&02;
	DB4 = i&01;

	Nybble(); //Send upper 4 bits
}

void write(char i)
{
	//P1 = i; //put data on output Port
	//D_I =1; //D/I=HIGH : send data
	DB7 = i&08;
	DB6 = i&04; 
	DB5 = i&02;
	DB4 = i&01;

	R_W =0; //R/W=LOW : Write
	Nybble(); //Clock lower 4 bits
	i = i<<4; //Shift over by 4 bits
//	P1 = i; //put data on output Port
	DB7 = i&08;
	DB6 = i&04; 
	DB5 = i&02;
	DB4 = i&01;

	Nybble(); //Clock upper 4 bits
}

/**********************************************************/
void Nybble(void)
{
	E = 1;
	Delay1TCY(); //enable pulse width >= 300ns (used 4uS)
	E = 0; //Clock enable: falling edge
}

/**********************************************************/
void init(void)
{
	//P1 = 0;
	//P3 = 0;
	DB7 = 0;
	DB6 = 0; 
	DB5 = 0;
	DB4 = 0;

	Delay1KTCYx(5); //Wait >15 msec after power is applied (used 20mS)
	//P1 = 0x30; //put 0x30 on the output port
	
	DB7 = 0;
	DB6 = 0; 
	DB5 = 1;
	DB4 = 1;

	
	Delay1KTCYx(1.25); //must wait 5ms, busy flag not available (used 5ms)

	Nybble(); //command 0x30 = Wake up

	Delay10TCYx(5); //must wait 160us, busy flag not available (used 160uS)

	Nybble(); //command 0x30 = Wake up #2

	Delay10TCYx(5); //must wait 160us, busy flag not available (used 160uS)

	Nybble(); //command 0x30 = Wake up #3

	Delay10TCYx(5); //can check busy flag now instead of delay

	//P1= 0x20; //put 0x20 on the output port
	DB7 = 0;
	DB6 = 0; 
	DB5 = 1;
	DB4 = 0;

	Nybble(); //Function set: 4-bit interface
	command(0x28); //Function set: 4-bit/2-line
	command(0x10); //Set cursor
	command(0x0F); //Display ON; Blinking cursor
	command(0x06); //Entry Mode set
}
/**********************************************************/
//End methods for LCD
/**********************************************************/
I have looked at a few other tutorials, but I am still left a bit confused. Even without looking at my modified code how should those functions look like when translated into code the PIC18F can work with and understand?

I understand the delay part, but I am a bit confused about the lines involving "P1 and D_I" commands. I just want to get to the point where I can at least display the letter "A" anywhere. Any help or suggestions is greatly appreciated. Thanks in advance for your time and assistance.
 
Last edited:

CraigHB

Joined Aug 12, 2011
127
You probably wouldn't be able to just plug in the code provided by the data sheet. They tend to leave out the delays required to keep comm timings within spec.

I believe D_1 and P1 are simply referring to the Register Select and Enable pins. Would be nice if they just used RS and E.

I'd have to simulate your code to troubleshoot it so it's I can't say off-hand what your problem is. Maybe someone else can tell you exactly.

One time, I was pulling my hair out over an LCD for some time until I realized I forgot the command to turn the damn thing on. Another time, I couldn't figure out why I was getting a partially blank screen. It was because I was failing to set the LCD's DDRAM address (or cursor) properly and the controller had unused DDRAM.

Timings can be a source of trouble. What I typically do is simulate my LCD routines and then watch the control bits in the simulator to verify timings. You can run comms too fast for LCDs even when using the MCU's default speed.

I've generally found the data sheets provided by Newhaven to be lacking. I use their displays a lot. You'll need the data sheet for your LCD controller to verify timings are within spec. Looks like the link is no good in the Newhaven data sheet, but I usually can find them with Google. The controller specification will also give you more and better information on LCD initialization.

Well, sorry I can't tell you exactly the problem, but hopefully that will give you some ideas.
 

Thread Starter

Micro9000

Joined Sep 22, 2009
10
I found the data sheet for the controllers:

SPLC80D
http://www.newhavendisplay.com/app_notes/SPLC780D.pdf
(Timing info for 4-bit operation on pg. 11)

ST7066U:
http://www.newhavendisplay.com/app_notes/ST7066U.pdf
(pg. 25/42 shows more timing information for 4-bit operation mode)

I also updated my code.

Rich (BB code):
//turns watch dog timer off, turn low voltage programming off
#pragma config WDT=OFF, LVP=OFF, DEBUG=ON

//Internal oscillator, port function on RA6, EC used by USB 
#pragma config FOSC = INTOSCIO_EC 
#include <p18f4550.h>
#include <delays.h>

//Power LED (PortA)
#define LEDPin LATAbits.LATA0 //Define LEDPin as PORT A Pin 1

//LCD (Port B and 1 pin PortD)
#define  RS  LATBbits.LATB5 //Define LCD pinout RS
#define  R_W LATBbits.LATB4 //Define LCD pinout R/W
#define  DB7 LATBbits.LATB3 //Define LCD pinout DB7
#define  DB6 LATBbits.LATB2 //Define LCD pinout DB6
#define  DB5 LATBbits.LATB1 //Define LCD pinout DB5
#define  DB4 LATBbits.LATB0 //Define LCD pinout DB4
#define  E	 LATDbits.LATD7 //Define LCD pinout E

void command(char);
void write(char);
void Nybble(void);
void init(void);

void main()
{
	unsigned int count=1;
	while(!OSCCONbits.IOFS);      //wait for osc stable
   	ADCON1 = 0x0F;                //make RA0 digital
  
    //data direction register all 0's mean all pins are set to output
	//all 1's mean all pins are set to operate as inputs
	TRISA = 0x00;  
	TRISB = 0x00; 
	TRISD = 0x00; 		    

	//main program loop
	while(1)
	{		
		if(count == 1)
		{
			LEDPin = 1; //output high Power LED indicator
			init();
			count++;
			command(0x01); //command to clear screen 
			wrtie(0x30); //display number 0 hopefully in first postion top row
		}
		break;

		
	}	 
}

/**********************************************************/
//4-bit methods for LCD
/**********************************************************/
void command(char i)
{
//	P1 = i; //put data on output Port
//	D_I =0; //D/I=LOW : send instruction

	R_W =0;   //R/W=LOW : Write

	DB7 = ((i>>7)&'1');
	DB6 = ((i>>6)&'1'); 
	DB5 = ((i>>5)&'1');
	DB4 = ((i>>4)&'1');

	R_W =0;   //R/W=LOW : Write
	Nybble(); //Send lower 4 bits

	DB7 = ((i>>3)&'1');
	DB6 = ((i>>2)&'1'); 
	DB5 = ((i>>1)&'1');
	DB4 = i&1;


	Nybble(); //Send upper 4 bits
}

void write(char i)
{
	//P1 = i; //put data on output Port
	//D_I =1; //D/I=HIGH : send data
	DB7 = ((i>>7)&'1');
	DB6 = ((i>>6)&'1'); 
	DB5 = ((i>>5)&'1');
	DB4 = ((i>>4)&'1');

	R_W =0; //R/W=LOW : Write
	Nybble(); //Clock lower 4 bits

//	P1 = i; //put data on output Port
	DB7 = ((i>>3)&'1');
	DB6 = ((i>>2)&'1'); 
	DB5 = ((i>>1)&'1');
	DB4 = i&'1';

	Nybble(); //Clock upper 4 bits
}

/**********************************************************/
void Nybble(void)
{
	E = 1;
	Delay1TCY(); //enable pulse width >= 300ns (used 4uS)
	E = 0; //Clock enable: falling edge
}

/**********************************************************/
void init(void)
{
	//P1 = 0;
	//P3 = 0;
	command(0x00);

	Delay1KTCYx(5); //Wait >15 msec after power is applied (used 20mS)

	//P1 = 0x30; //put 0x30 on the output port
	
		DB7 = 0;
		DB6 = 0; 
		DB5 = 1;
		DB4 = 1;

	
	Delay1KTCYx(1.25);     //must wait 5ms, busy flag not available (used 5ms)
	Nybble(); 			//command 0x30 = Wake up
	Delay10TCYx(4); 	        //must wait 160us, busy flag not available (used 160uS)
	Nybble(); 			//command 0x30 = Wake up #2
	Delay10TCYx(4); 	        //must wait 160us, busy flag not available (used 160uS)
	Nybble(); 			//command 0x30 = Wake up #3
	Delay10TCYx(4);    	//can check busy flag now instead of delay
	//P1= 0x20; 		//put 0x20 on the output port

		DB7 = 0;
		DB6 = 0; 
		DB5 = 1;
		DB4 = 0;

	Nybble();           //Function set: 4-bit interface
	command(0x28); //Function set: 4-bit/2-line
	command(0x10); //Set cursor
	command(0x0F); //Display ON; Blinking cursor
	command(0x06); //Entry Mode set
}
/**********************************************************/
//End methods for LCD
/**********************************************************/
So I am using the following to handle the output for the write and command function by masking the bits:
//Upper bits:
...
DB7 = ((i>>7)&'1');
DB6 = ((i>>6)&'1');
DB5 = ((i>>5)&'1');
DB4 = ((i>>4)&'1');
...
//Lower bits:
DB7 = ((i>>3)&'1');
DB6 = ((i>>2)&'1');
DB5 = ((i>>1)&'1');
DB4 = i&'1';
...

So far, when I run the program the entire top row of the LCD has black squares, while the bottom row shows nothing. If you look in the main portion of the updated code I am trying to clear the screen and output the number 0 after the initialization. But, it doesn't work and the screen still has black squares across the top row :( .

I believe I have the delays correct, at least as far as the correct value from the datasheet. The 18F should be oscillating at 1MHz. I checked it with the LED I have connected by using the following code:

Rich (BB code):
#define LEDPin LATAbits.LATA0 //Define LEDPin as PORT A Pin 1
...
while(1)
{
     //Delay 250K cycles (1 second at 1MHz since each instruction takes 4 cycles)
     Delay10KTCYx(25);
     LEDPin = ~LEDPin; //constantly inverts output
}
...
I've tried switching the order in which I feed the upper and lower nibbles into the LCD. But, it doesn't appear to have an effect on the display. Only changing the bottoms commands in the initialize function makes the LCD behave oddly.

___________
EDIT:

Here is a video showing the problem:

http://www.youtube.com/watch?v=q4QzkPm7BR0

(Sometimes it just stays with the black rows across the top. So, I can't say for sure what the problem is)
 
Last edited:
Top