LCD Interface Issues

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I was trying to Interface LCD to Atmega328. But I get garbage displayed on the display unit.

Below is the code. 

 

/*
 * keypad_and_display_Application.c
 *
 * Created: 02-06-2015 11:19:36
 *  Author: user
 */ 

#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT  PORTB
#define LCDDDR DDRB
// used pins on port appropriate ports
#define LCD_DB4 0    // PORTD.0
#define LCD_DB5 1    // PORTD.1
#define LCD_DB6 2    // PORTD.2
#define LCD_DB7 3    // PORTD.3
#define LCD_ENABLE_BIT  4     // PORTD.6 Enable
#define LCD_RS 5     // PORTD.7 Register Select
//#define LCD_RW     // R/W is connected to GND permanently


#define LCD_DATA_BITS_MASK 0x0F
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4))    // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS)	  //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS)		 //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT)			  //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT)			 //Function to Enable LCD	  P0.18

//#define EIGHT_BIT_MODE 0x30                                      // 0 0 1 1 x x x x DB7 to DB0 0f LCD
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 200      // Give a delay of 200 msec after reset. // Datasheet says 10 msec delay.


//Commands Finalized
#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME 0x02    // return home
#define ENTRY_MODE_LEFT_TO_RIGHT 0x06              // Cursor direction from Left to right	, Bit 1 of entry mode in LCD
#define DISPLAY_OFF  0x08        // Blink ON, Cursor ON etc are don't care
#define CURSOR_OFF_BLINK_OFF   0x0C
#define CURSOR_OFF_BLINK_ON	0x0D    // blink on Even 0x0D also works. So cursor need not be ON
#define CURSOR_ON_BLINK_OFF	0x0E    // blink off
#define CURSOR_ON_BLINK_ON 0x0F
// Function prototypes
void unpackAndSend(char  data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);


void CL_delayMS(unsigned int delayMS)
{
	while(delayMS--)
	{
		_delay_ms(1);
	}
}

void CL_delayuS(unsigned int delayus)
{
	while(delayus--)
	{
		_delay_us(1);
	}
}

// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
	SET_DATA_MODE();                  // RS bit has to be 1 for data mode
	unpackAndSend(data);
}

// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
	//DISABLE_LCD();
	//_delay_us(80);
	ENABLE_LCD();
	CL_delayMS(2);
	DISABLE_LCD();
	CL_delayMS(2);
}

// writes an instruction to the LCD
void sendLCDCommand(unsigned char inst)
{
	SET_INSTRUCTION_MODE();
	unpackAndSend(inst);
	waitForBusyFlagToClear();

}

// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
	char temp=inst;
	DISABLE_LCD();
	// SET_WRITE_MODE();               // If write is permanently disabled, do not use this.
	
	LCDPORT &= (~LCD_DATA_BITS_MASK);   // Clear the data bits
	//sendLCDPulse();
	inst&=0xF0;
	inst=inst>>4;                            // Get the upper nibble
	LCDPORT|=inst<<LCD_DB4;                 //Replace the bits starting from position of bit LCD_DB4 with this new data
	sendLCDPulse();
	LCDPORT &= (~LCD_DATA_BITS_MASK);  // Clear the data bits again
	//sendLCDPulse();
	temp &=0x0f; //send low nibble
	LCDPORT|=temp<<LCD_DB4;
	sendLCDPulse();
}

// waitForBusyFlagToClear functio can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.

void waitForBusyFlagToClear(void)
{
	CL_delayMS(BUSY_FLAG_WAIT_TIME);
}


// clear display
void clearDisplay(void)
{
	sendLCDCommand (CLEAR_DISPLAY);
}

// return home
void returnCursorHome(void)
{
	sendLCDCommand (CURSOR_HOME);
}

// LCD off
void displayOFF(void)
{
	sendLCDCommand (DISPLAY_OFF);
}

// LCD on
void displayONCursorOFF(void)
{
	sendLCDCommand (CURSOR_OFF_BLINK_OFF);
}

// cursor on
void displayONCursorON(void)
{
	sendLCDCommand (CURSOR_ON_BLINK_OFF);
}

// blink on
void cursorOffBlinkOn(void)
{
	sendLCDCommand (CURSOR_OFF_BLINK_ON);
}

// blink OFF, but display and cursors are ON
void cursorOnBlinkOff(void)
{
	sendLCDCommand (CURSOR_ON_BLINK_OFF);
}

// All are ON
void cursorOnBlinkOn(void)
{
	sendLCDCommand (CURSOR_ON_BLINK_ON);
}

//go to first line
void LCDline1 (void)

{
	sendLCDCommand (0b10000000);
}

//go to second line
void LCDline2 (void)

{
	sendLCDCommand (0b11000000);
}


// goto position x,y
void setPosition (char position,char rowNumber)
{
	unsigned char pos;

	if (rowNumber == 0)
	{
		pos = 0b00000000 + position;
	}

	else //if (rowNumber == 1)     // Either row 1 or two. We cannot have else option.
	{
		pos = 0b01000000 + position;
	}

	sendLCDCommand (0b10000000 | pos);

}

//write text to the LCD
void sendTextToLCD(char *data)
{
	while (*data)
	{
		writeCharToLCD(*data);
		data++;
	}
}

// init LCD

void initializeLCD(void)
{
	// Set the direction of port pins connected to LCD display as output ports.
	// We are permanently connecting R/W pin to ground. So there is no read instruction in this case..
	LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
	
	//After reset, data sheet suggests some delay.
	CL_delayMS(LCD_INIT_DELAY);
	
	
	// Note some sites says three times 8 bit mode setting commands need to be sent.
	// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
	
	// 1st Command
	SET_EIGHT_BIT_MODE();
	sendLCDPulse();
	CL_delayMS(5);
	
	
	// Second Command
	SET_EIGHT_BIT_MODE();
	sendLCDPulse();
	CL_delayuS(100);
	
	// third Command
	SET_EIGHT_BIT_MODE();
	sendLCDPulse();
	CL_delayuS(100);
	
	// Finally Set four bit mode
	SET_FOUR_BIT_MODE();
	sendLCDPulse();
	CL_delayuS(100);
	
	// First time when 4 bit mode command is sent, only one higher nibble was sent since
	// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
	// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
	//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
	// inside the function sendLCDCommand.
	sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
	
	
	//turn on display and cursor OFF, Blink OFF (sent two times using below command)
	sendLCDCommand (CURSOR_OFF_BLINK_OFF);

	//clr display
	sendLCDCommand (CLEAR_DISPLAY);
	
	// Set Entry mode left to right
	sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);

}

void LCDProgramCallFromMain(char *row1Data, char *row2Data)
{
	initializeLCD();
	//initializeLCD();
	sendTextToLCD (row1Data);
	setPosition (2,1);
	sendTextToLCD(row2Data);
	cursorOnBlinkOn();
	returnCursorHome();
	for(;;)
	{
		//LCDblinkON();
		//displayONCursorON();
		cursorOffBlinkOn();
		CL_delayMS(10000);
		cursorOnBlinkOff();
		//displayONCursorOFF();
		CL_delayMS(10000);
		
		
	}
	
}

int main(void)
{


	LCDProgramCallFromMain("08:25", "13/06/15");
	while(1)
	{
	
		
	}
   return(0);
	
}

The strange part is that if I change SET_EIGHT_BIT_MODE() and SET_FOUR_BIT_MODE() as below, the code works!! Infact the code was working, when I reviewed the code I noticed this bug. When I fixed, it is not working!! I think somewhere something is wrong.

#define SET_EIGHT_BIT_MODE() LCDPORT|=((0<<LCD_DB5)|(0<<LCD_DB4))    // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(0<<LCD_DB5)

Also when it is working (When 8 bit mode and 4 bit mode commands are wrong), it does not work always. I have to reset it couple of times for make it work. So that means my code is working by luck.

This topic has a solution.

Last Edited: Sat. Jun 13, 2015 - 12:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But I get garbage displayed on the display unit

It would help if you could post what you were trying to display along with what was actually displayed.  What looks like 'garbage' to you frequently contains clues as to the cause of the problem.

 

 

	// Note some sites says three times 8 bit mode setting commands need to be sent.
	// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.

Why don't you just follow the recommendations in the Hitachi datasheet flowchart and be sure that all LCDs will 'work fine', not just the one you are dealing with at the current time.  You can find a complete explanation by following the LCD Initialization link at http://web.alfredstate.edu/weimandn.  If you look around you will find some working code there as well.

 

I have to reset it couple of times for make it work. So that means my code is working by luck.

This usually means that your initialization sequence is incorrect.  See the explanation in the previously mentioned link.

 

Don

Last Edited: Sat. Jun 13, 2015 - 02:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

During software initialization, what should be the state of Enable pin? In the initializeLCD() function above, sendLCDPulse() is called. It looks like this is creating problem. If I remove that it is working consistently. But in the mean time even if I keep Enable pin low during initialization, then also it works. So there is no impact of Enable pin during initialization period!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The E signal is a clock. Should go hi, hold for 500 ns, then go low. This is about 8 nops on a 16MHz avr. It isnt a chip select like on some external devices.

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to datasheet it "Starts data read/write". So is it means that for instruction writing this signal it is not needed correct? So I need not pulse this in sendLCDCommand() function!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look carefully at the timing diagrams in the datasheet.  The falling edge of the Enable signal is what triggers the reading of data sent to the LCD controller from your microprocessor.  This is true for commands as well as displayable characters since they are both 'data'.

 

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, got it. But for soft initializing the LCD (such as into 4 bit mode), I do not think we use "E" signal. Many initialization routines I saw, but not body uses this signal during initialization.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You use "E" for EVERYTHING.  READING from the LCD, and WRITING to the LCD.

 

There is an excellent LCD 'library' by a fellow named Peter Fleury.  Google him, and use that rather than roll your own.  It does 4bit or 8 bit interfacing and all the work is done for you.

 

Here, I saved you the trouble.

 

THis is the link to the manual:

http://homepage.hispeed.ch/peter...

 

This is the link to download the .h and .c files:

http://homepage.hispeed.ch/peter...

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

Last Edited: Mon. Jun 15, 2015 - 04:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think you got it.  The concept is really quite simple.  Anytime you want to communicate with the LCD controller you must use the Enable signal. 

 

To send a single chunk of data ( a byte in 8-bit mode or a nibble in 4-bit mode) you deal with the following signals:

 

(1) E low.

(2) RS high or low depending on whether you are sending a command or a displayable character.

(3) R/W low.

(4) E high.

(5) Data lines high and/or low (you can legitimately do this before you drive E high if you prefer).

(6) E low (this starts the data transfer).

 

Use the 'Write Operation' timing diagram and the 'Bus Timing Characteristics' which are near the end of the Hitachi datasheet for the timing details.

 

Did you look at any of the programming examples I mentioned earlier?

 

Jim types faster than me.

 

Don

 

Last Edited: Mon. Jun 15, 2015 - 04:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got the things, But there are few issues I need to look at. For example, my LCD works even if I initialize with E low or high. That is why I got confused if initialization requires E to be activated or not. Let me go through the libraries you referred and see where the difference is. I could have used libraries directly, but wanted to understand my own. Anyway thanks all for your nice help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is why I got confused if initialization requires E to be activated or not.

All I can say is that the library I've used for the past decade, based on CodeVision's, calls the standard "write nibble" primitive during the init sequence, which does the E toggle.

 

It is always curious why anyone would go through the effort nowadays to make a character LCD library from scratch, and why you are working so hard to avoid a few instructions.  Even if it works on this particular LCD, will it work on the different model in the next app?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can see why folks would try it, but it is sort of interesting that after 10 or 15 years of reading messages on avrfreaks, a large percentage of the messages are still about uarts, a/d converters, and text lcds, and the text lcd init is just about always "why on earth do I send 0x38 3 times?!?'. Whats old is new or whatever. The explanation should be a sticky note.

 

 

Imagecraft compiler user

Last Edited: Fri. Jun 19, 2015 - 10:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is why I got confused if initialization requires E to be activated or not.

You are getting confused because you are interpreting the name of the 'Enable' pin differently than the translator of the datasheet did when he tried to convert the original Japanese name into English.  There is also the question of what you mean by the term 'activated'.

 

Forget about the name associated with the pin and the definition of that name and concentrate on what we have been telling you about the signal and what it does.  The actual level of the signal is not the significant factor, it is the change in level that causes or allows things to happen.  Without a change in level of this signal there can be no transfer of information so no initialization can take place and no new characters can be displayed. 

 

Don