Multiple LCD Problem

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

Hey all I have been working on a new little project with three seperate LCD's showing different information.
I have 2 2x16 lcd's and 1 4x16 lcd, they are both controlled by a hd44780 controller.

I have the following code, strongly based of peter fluerys library, I can get them to initialize and one lcd to display it's initial screen but thats it.

Lcd's are in 4 bit mode
data lines, R/W, RS, are all shared by the lcds and are on PORTC data lines are on , bit 4,5,6,7 RS on bit 3, R/W on bit 1

Enable pins are located on PORTA bit 1,2,3.

int main(void)
{
	lcd_init(LCD_DISP_ON);
	Lcd_1_int();
	Lcd_2_int();
	Lcd_3_int();
	
	while (1)
		{
		    Lcd_Update1();
						
		}
}
void Lcd_Update1(void)
{
	LCD_E_PIN = 1;
	lcd_gotoxy(0,1);
	_delay_ms(1000);
	sprintf(Temp2, "Time Left - %.2d",TimeJ1);
	lcd_puts(Temp2);		
	
}

void Lcd_3_int(void)
{
	LCD_E_PIN = 2;
	_delay_ms(2);
	lcd_clrscr();
	sprintf(Temp1, "        Test0\n");
	sprintf(Temp2, "    Test1\n");
	sprintf(Temp3, "  Test2\n");
	sprintf(Temp4, "   Test3\n");
	lcd_gotoxy(0,0);
	lcd_puts(Temp1);
	lcd_puts(Temp2);
	lcd_puts(Temp3);
	lcd_puts(Temp4);
}

void Lcd_1_int(void)
{
	LCD_E_PIN = 1;
	_delay_ms(2);
	lcd_clrscr();
	lcd_gotoxy(0,0);
	sprintf(Temp1, " Test4\n");
	sprintf(Temp2, "   Test5");
	lcd_puts(Temp1);
	lcd_puts(Temp2);
}

void Lcd_2_int(void)
{
	LCD_E_PIN = 3;
	_delay_ms(2);
	lcd_clrscr();
	lcd_gotoxy(0,0);
	sprintf(Temp1, " Test7\n");
	sprintf(Temp2, "   Test8");
	lcd_puts(Temp1);
	lcd_puts(Temp2);
}
void toggle_e(void)
{
	PORTA |= _BV(LCD_E_PIN);
	lcd_e_delay();
	PORTA &= ~_BV(LCD_E_PIN);
}

void lcd_write(uint8_t data,uint8_t rs)
{
	
	if (rs) {   // write data   (rs=1, rw=0)
		PORTC |= _BV(3);
	} else {	// write instruction (rs=0, rw=1)
		PORTC &= ~_BV(3);
	}
	PORTC &= ~_BV(1);

	DDRC |= 0xF0; // sets Pins 4,5,6,7 to output
	
	//output high nibble first
	PORTC &= ~_BV(7);
	PORTC &= ~_BV(6);
	PORTC &= ~_BV(5);
	PORTC &= ~_BV(4);
	if (data & 0x80) PORTC |= _BV(7);
	if (data & 0x40) PORTC |= _BV(6);
	if (data & 0x20) PORTC |= _BV(5);
	if (data & 0x10) PORTC |= _BV(4);
	toggle_e();

	//output low nibble
	PORTC &= ~_BV(7);
	PORTC &= ~_BV(6);
	PORTC &= ~_BV(5);
	PORTC &= ~_BV(4);
	if (data & 0x08) PORTC |= _BV(7);
	if (data & 0x04) PORTC |= _BV(6);
	if (data & 0x02) PORTC |= _BV(5);
	if (data & 0x01) PORTC |= _BV(4);
	toggle_e();

	//all data lines high (inactive)
	PORTC |= _BV(4);
	PORTC |= _BV(5);
	PORTC |= _BV(6);
	PORTC |= _BV(7);
	
}

uint8_t lcd_read(uint8_t rs)
{
	uint8_t data;
	
	if (rs)
		PORTC |= _BV(3);		//RS=1: read data
	else
		PORTC &= ~_BV(3);		//RS=0: read busy flag
	PORTC |= _BV(1);			//RW=1: read mode

	DDRC &= ~_BV(4);
	DDRC &= ~_BV(5);
	DDRC &= ~_BV(6);
	DDRC &= ~_BV(7);			//configure data pins as input

	PORTA |= _BV(LCD_E_PIN);
	lcd_e_delay();
	data = 0;
	if ( PIN(PORTC) & _BV(4)) data |= 0x10;
	if ( PIN(PORTC) & _BV(5)) data |= 0x20;
	if ( PIN(PORTC) & _BV(6)) data |= 0x40;
	if ( PIN(PORTC) & _BV(7)) data |= 0x80;
	PORTA &= ~_BV(LCD_E_PIN);

	lcd_e_delay();				//enable 500ns low

	PORTA |= _BV(LCD_E_PIN);
	lcd_e_delay();
	if ( PIN(PORTC) & _BV(4)) data |= 0x01;
	if ( PIN(PORTC) & _BV(5)) data |= 0x02;
	if ( PIN(PORTC) & _BV(6)) data |= 0x04;
	if ( PIN(PORTC) & _BV(7)) data |= 0x08;
	PORTA &= ~_BV(LCD_E_PIN);
	return data;
}

uint8_t lcd_waitbusy(void)
{
	uint8_t c;

	while ( (c=lcd_read(0)) & (1<<LCD_BUSY)) {}
	delay(2);
	return (lcd_read(0));

}

void lcd_newline(uint8_t pos)
{
	uint8_t addressCounter;

	if ( pos < 0x14 )
		addressCounter = 0x40;
	else if ( (pos >= 0x40) && (pos < 0x54) )
		addressCounter = 0x14;
	else if ( (pos >= 0x14) && (pos < 0x40) )
		addressCounter = 0x54;
	else
		addressCounter = 0x00;

	lcd_command((1<<7)+addressCounter);
}

void lcd_command(uint8_t cmd)
{
	lcd_waitbusy();
	lcd_write(cmd, 0);
}

void lcd_data(uint8_t data)
{
	lcd_waitbusy();
	lcd_write(data,1);
}

void lcd_gotoxy(uint8_t x, uint8_t y)
{
	if ( y==0 )
		lcd_command((1<<7)+0x00+x);
	else if ( y==1 )
		lcd_command((1<<7)+0x40+x);
	else if ( y==2 )
		lcd_command((1<<7)+0x14+x);
	else
		lcd_command((1<<7)+0x54+x);
}

int lcd_getxy(void)
{
	return lcd_waitbusy();
}

void lcd_clrscr(void)
{
	lcd_command(1<<0);
}

void lcd_home(void)
{
	lcd_command(1<<1);
}

void lcd_putc(char c)
{
	uint8_t pos;

	pos = lcd_waitbusy();
	if (c=='\n')
	{	
		lcd_newline(pos); 
	}
	else
		lcd_write(c, 1);
}

void lcd_puts(const char *s)
{
	char c;

	while ( (c = *s++) ) {
		lcd_putc(c);
	}
}

void lcd_init(uint8_t dispAttr)
{
	DDRC |= 0xFF;
	DDRA |= 0xFF;
	delay(16000);
	PORTC |= _BV(5);
	PORTC |= _BV(4);
	PORTA = 0x0F;
	lcd_e_delay();
	PORTA = 0x00;
	//toggle_e();
	delay(4992);
	PORTA = 0x0F;
	lcd_e_delay();
	PORTA = 0x00;
	//toggle_e();
	delay(64);
	PORTA = 0x0F;
	lcd_e_delay();
	PORTA = 0x00;
	//toggle_e();
	delay(64);
	PORTC &= ~_BV(4);
	PORTA = 0x0F;
	lcd_e_delay();
	PORTA = 0x00;
	//toggle_e();
	delay(64);
	lcd_command(0x08);
	lcd_clrscr();
	lcd_command(((1<<2) | (1<<0x06)));
	lcd_command(dispAttr);
}

It won't let me Post the delay routine but it is identical to the one thats in peter fluery's lcd library

Any suggestions on were I have gone wrong???

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

Quote:
Any suggestions on were(sic) I have gone wrong???

You've tried to get three displays working before you got one display to work.

Don

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

When I // the other 2 displays out the code works fine it's when I add a second and or third that the problem arrises!!

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

So debug your code.

When you where here last time you didn't want to listen to advice. Has that changed in the meantime?

Stealing Proteus doesn't make you an engineer.

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

I would suspect lcd_waitbusy() function.
It makes LCD data lines output.
To make sure I would try for example

lcd_waitbusy(void)
{
_delay_ms(20)
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am certain that the wait busy is the problem. You are sending all three displays into read mode at the same time and their outputs are conflicting. If you use separate R/W lines for each and only put one into read mode at a time it should work. Otherwise, Visovian's solution will work as well.

Regards,
Steve A.

The Board helps those that help themselves.

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

Hello,
the HD44780 data sheet shows the output driver active in read mode during E high phases only. That's not conflicting.
But sometimes the outputs don't drive high. Put the pull up resistors on when you read the LCD.
Maybe lcd_e_delay(); is removed by optimizer.
Why do you handle each bit instead of half bytes?

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

Quote:
When I // the other 2 displays out the code works fine it's when I add a second and or third that the problem arrises!!

Does that mean "each" displays tested by itself works OK.

Where and how is LCD_E_PIN declared ??
Lee

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

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

Quote:
Where and how is LCD_E_PIN declared ??

From the very first post:
Quote:
Enable pins are located on PORTA bit 1,2,3.

It is also quite clear from the code.

Regards,
Steve A.

The Board helps those that help themselves.

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

Hi Steve,
I do not see whether it is "declared" as a variable or "defined" as a constant in the first post and the code does not make the hardware allocation any clearer for me.

If it is a variable, setting it to 3 will actually enable two bits on PORTA. (#define _BV (1 << (bit))

If it is a CONSTANT, I did not think it was permissible to assign a value to it at run time.

But I could be wrong and whilst I am alive, I believe I will learn something new every day and that is why I watch this forum and learn from greater minds than mine.

Incidentily the lcd_newline() function will probably need to know which LCD it is operating on or how many lines it has.

Lee

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

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

Quote:
I do not see whether it is "declared" as a variable or "defined" as a constant in the first post and the code does not make the hardware allocation any clearer for me.

Since he uses it thus:

   LCD_E_PIN = 1;

then it is obviously a variable. And whether that variable is declared as an int or a float is inconsequential since it will be converted to an int when it is used here:

   PORTA |= _BV(LCD_E_PIN);

Quote:
If it is a variable, setting it to 3 will actually enable two bits on PORTA.

No it won't. It will set bit 3 of PORTA.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:
the HD44780 data sheet shows the output driver active in read mode during E high phases only. That's not conflicting.
I have tested and found something else.
In read mode (RW=1) there are strong levels on data lines even when E=0. In no case are the lines tristating.

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

Hello,
in that case

Quote:
I have tested and found something else. In read mode (RW=1) there are strong levels on data lines even when E=0. In no case are the lines tristating.
the RW line must be separated for each display too.

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

Okay so I need to Isolate the RW line I will give that a shot!

I tried removing the waitbusy and put in the delay as suggested and the first lcd works perfectly now but still no response from the others

LCD_E_PIN is declared as a int variable!

each display is fully functional and have tested them individually!!!

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

As the next step I would disconnect RW of all LCDs from AVR and ground them. Then test the code with delay.

Besides, there are LCD code examples that do not use RW at all.

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

Well I got most of it going...turns out the other 2 LCD's weren't initialized, in order to get them to work I have to take then enable lines from the 2nd and 3rd LCD and combine them with the enable line from the first lcd then reset the Mega32, also found out for some unknown reason the lcd enable lines are located on bits 1,3,4....I can't figure that part out.....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void lcd_init(uint8_t dispAttr)
{
   DDRC |= 0xFF;
   DDRA |= 0xFF;
   delay(16000);
   PORTC |= _BV(5);
   PORTC |= _BV(4);
   PORTA = 0x0F;
   lcd_e_delay();
   PORTA = 0x00;
   //toggle_e();
   delay(4992);
   PORTA = 0x0F;
   lcd_e_delay();
   PORTA = 0x00;
   //toggle_e();
   delay(64);
   PORTA = 0x0F;
   lcd_e_delay();
   PORTA = 0x00;
   //toggle_e();
   delay(64);
   PORTC &= ~_BV(4);
   PORTA = 0x0F;
   lcd_e_delay();
   PORTA = 0x00;
   //toggle_e();
   delay(64);
   lcd_command(0x08);
   lcd_clrscr();
   lcd_command(((1<<2) | (1<<0x06)));
   lcd_command(dispAttr);
} 

Note that up to line
lcd_command(0x08);
you toggle all three E-lines (PORTA.1, PORTA.2, PORTA.3.

   PORTA = 0x0F;
   lcd_e_delay();
   PORTA = 0x00;

OK so far.
But remainig part of init you do only for one LCD according to value of LCD_E_PIN which I guess is "1" as you say LCD1 works.
It is because in func lcd_command() => lcd_write() => toggle_e() you toggle only one E line.

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

I would try this
(with RW lines grounded)

//replace these functions:
int lcd_init(uint8_t dispAttr)
{
   DDRC |= 0xFF;
   DDRA |= 0xFF;
   delay(16000);
   PORTC |= _BV(5);
   PORTC |= _BV(4);
   
   toggle_e();
   delay(4992);
      
   toggle_e();
   delay(64);
   
   toggle_e();
   delay(64);
   
   PORTC &= ~_BV(4);
   toggle_e();
   delay(64);

   lcd_command(0x08);
   lcd_command(((1<<2) | (1<<0x06)));
   lcd_command(dispAttr);
   lcd_clrscr();
}


uint8_t lcd_waitbusy(void)
{
   _delay_ms(10);  
   return (1);
} 


void lcd_putc(char c)
{
   lcd_data(c); 
} 


//test
main()   
{
   LCD_E_PIN = 1;          //init LCD1 
   lcd_init(LCD_DISP_ON); 

   LCD_E_PIN = 2;          //init LCD2 
   lcd_init(LCD_DISP_ON); 

   LCD_E_PIN = 3;          //init LCD3 
   lcd_init(LCD_DISP_ON); 


   LCD_E_PIN = 1;
   lcd_clrscr();
   lcd_puts("DISP 1");
      
   LCD_E_PIN = 2;
   lcd_clrscr();
   lcd_puts("DISP 2");
      
   LCD_E_PIN = 3;
   lcd_clrscr();
   lcd_puts("DISP 3");
      

   for(;;)
   {
   }

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

I see I see makes sense...I never thought about that before!!! I will try and see how it works!!!

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

With separate inits for each display, I would move the big delay out of the init since it would only be needed once.

Regards,
Steve A.

The Board helps those that help themselves.

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

I have it all working now thanks ......any clue why the enable lines show up at bit 1 bit 3 and bit 4
I have these for the corrisponding bits

PORTA |= _BV(1)....shows up at bit 1, PA0
PORTA |= _BV(2)....shows up at bit 4, PA3
PORTA |= _BV(3)....shows up at bit 3, PA2

Last Edited: Fri. Jul 17, 2009 - 12:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which AVR?
What is PIN 0 ? Pins on AVR count from 1.

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

ATMega 32, and sorry I meant to right PA0, PA3, PA2