can't clear LCD

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

Hey everyone,

I picked up a 20x4 GDM2004D (HD44780 compatible) from sparkfun the other day and I've been able to write text to all 4 lines however when I attempt to clear the display I just get 2 rows of all black squares.

Here is an outline of how I'm attempting to clear the display:

    // Bring RS/RW/E Low
    PORTB = 0x00;
    _delay_ms(40);

    // send clear display command
    PORTD = 0x01;
    _delay_ms(40);

    // Bring E high
    PORTB = (1 << PB2);
    _delay_ms(40);

    // Bring E low
    PORTB &= ~(1 << PB2);
    _delay_ms(40);

    // Bring RS/RW High
    PORTB |= (1 << PB0) | (1 << PB1);

The long delays are just there for testing purposes. I figure I must be sending this command the wrong way. Can anyone shed a little light on where I'm going wrong here?

Thanks!

Attachment(s): 

Last Edited: Sun. Jan 6, 2008 - 05:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But have you initialized the display? Also have you tried adjusting the contrast? I would expect to see the blocks if the contrast is turned all the way up. The blocks are not from data in the displays memory, but a result of how the LCD actually works.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

I can write to the display, this image shows the result of me attempting to clear the screen after writing to it. Does it need to be reinitialized after the clear?

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

Rather then trying to explain to you why you need proper initialization and proper control signal sequencing, I figured I'd just provide a working example.

Enjoy the Matrix keyboard routines, as well

This code drives the HD44780 compatible LCD in 4 bit more.

// Written by Carl W. Livingston
// microcarl@roadrunner.com

// Image Craft ICC-AVR application builder: 09/27/2007
// Target: Mega644
// Crystal: 18.432MHz, Internal

/****************************************************************************/
/****************************************************************************/
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
#include 
/****************************************************************************/
/****************************************************************************/
#include 
#include 

/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
// If you want to use a different I/O port for LCD control & data,
// do it here!!!

#define LCD_DATA_OUT PORTC
#define LCD_DATA_IN PINC
#define LCD_DATA_DDR DDRC

#define KB_DATA_OUT PORTB
#define KB_DATA_IN PINB
#define KB_DATA_DDR DDRB

#define PORTx0 0
#define PORTx1 1
#define PORTx2 2
#define PORTx3 3
#define PORTx4 4
#define PORTx5 5
#define PORTx6 6
#define PORTx7 7

// Define LCD Register Select as PORTx, 0x01 
#define LCD_RS 0
// Define LCD Read/Write as PORTx, 0x02
#define LCD_RW 1
// Define LCD Enable as PORTx, 0x08
#define LCD_E 3
// Define LED control bit as PORTx, 0x04
#define LCD_LED 2
/****************************************************************************/
/****************************************************************************/ 
/****************************************************************************/
/****************************************************************************/

// For enabling PORTx.7 internal Pull-Up resistor
#define PULL_UP_7 7
// Define LCD BUSY bit as PORTx, 0x80
#define LCD_BUSY_FLAG 7

#define DataMask 0xF0


// Turn on power to the display, no cursor
#define	LCD_ON 0x0C
// Clear display command		
#define LCD_CLR 0x01
// Set 4 data bits
#define	LCD_4_Bit 0x20
// Set 8 data bits
#define	LCD_8_Bit 0x30
// Set number of lines
#define LCD_4_Line 0x08
// Set character font
#define LCD_Font 0x04
// Turn the cursor on
#define LCD_CURSOR_ON 0x02
// Turn on cursor blink
#define LCD_CURSOR_BLINK 0x01

// Display LINE POSITION addressing
#define LINE1 0x80
#define LINE2 0xC0
#define LINE3 0x94
#define LINE4 0xD4

#define UART_Tx_ON()	UCSR0B |= (1<<TXEN0)
#define UART_Tx_OFF()	UCSR0B &= ~(1<<TXEN0)
#define UART_Rx_ON()	UCSR0B |= (1<<RXEN0)
#define UART_Rx_OFF()	UCSR0B &= ~(1<<RXEN0)

#define LCD_BACKLIGHT_ON LCD_DATA_OUT |= (1<<LCD_LED);
#define LCD_BACKLIGHT_OFF LCD_DATA_OUT &= ~(1<<LCD_LED);

void Init_Devices (void);
void LCD_Delay (unsigned int MicroSeconds);
void LCD_PutString (char *CharacterString, unsigned char CursorPosition);
void LCD_PutCmd (char Command);
unsigned char LCD_BusyWait (void);
void LCD_PutChar (char Character);
void LCD_INIT (void);
char KB_Scan (void);
char KB_GetKey (void);

char Banner_MSG_1[] = "MicroCarl's";
char Banner_MSG_2[] = "Play Room";
char Banner_MSG_3[] = "STK500 --> HD44780";
char Banner_MSG_4[] = "Text LCD Adapter";
char PrintBuff[24];

/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
void main(void) {
	 unsigned char n;
	 char KeyCode;

	 Init_Devices();
 
	 LCD_PutString (Banner_MSG_1, LINE1+4);
	 LCD_PutString (Banner_MSG_2, LINE2+5);	
	 LCD_PutString (Banner_MSG_3, LINE3+1);	
	 LCD_PutString (Banner_MSG_4, LINE4+2);

	 for (n = 0; n < 150; n++) {
	 	 LCD_Delay(60000);
	 }	
	 LCD_PutCmd (LCD_CLR); // Clear the display
	 LCD_PutCmd (LINE2);
	 
	 while(1) {
	 		  KeyCode = KB_GetKey();
			  if (KeyCode == '#') {
			  	 LCD_PutCmd (LCD_CLR);
				 LCD_PutCmd (LINE2);
			  }
			  else LCD_PutChar (KeyCode);
	 }
}	 
/****************************************************************************/

/****************************************************************************/
char KB_GetKey (void) {
	 #define KB_DebounceDelay 200
	 
	 char DebounceCount;
	 char LastKey = NULL;
	 char KeyCode = NULL;
	 char temp;

	 // Key pressed debounce
	 DebounceCount = KB_DebounceDelay; 	 	 

	 if (KB_Scan() != -1) {
	 	do {
	 	   temp = KB_Scan();	
	 	   if (LastKey != temp) {
		   	  LastKey = temp;
		   	  DebounceCount = KB_DebounceDelay;
		   }
		} while (DebounceCount-- != 0);
		KeyCode = temp;	 

		// Key released debounce
		// Key is returned upon release of active key
		DebounceCount = KB_DebounceDelay; 	 	 
		do {
	 	   temp = KB_Scan();	
		   if (temp == KeyCode) {
		   	  DebounceCount = KB_DebounceDelay;
		   }
		} while ((DebounceCount-- != 0));	 

		return (KeyCode);
	 } else return (-1);	 

}
/****************************************************************************/ 
	 
/****************************************************************************/
char KB_Scan (void) {
	 #define KEYS_PER_ROW 4
	 #define FIRST_ROW 0x10

	 char temp;
	 char n = 0; // Column counter  
	 char i = FIRST_ROW; // Set up the first active row strobe bit 
	 char q = KEYS_PER_ROW; // Set up the Row character counter, 4 keys per row
	 char ActiveKey = NULL;
	 
	 // Data table representing Matrix Keyboard input
	 // In a hard design, this table would be fixed and inaccessible
	 // to the casual user
	 const char KB_MATRIX[] = {0x11, 0x12, 0x14, 0x18, 0x21, 0x22,
	 	   				  	   0x24, 0x28, 0x41, 0x42, 0x44, 0x48,
							   0x81, 0x82, 0x84, 0x88, NULL};

	 // Data table representing user key code output   
	 // This table will be user accessible.  This table is currently set up to
	 // produce ASCII characters.  It can also be changed to produce PC
	 // compatible KEY CODEs or, any other single byte key code.
	 const char KB_KEY[] = {'1', '2', '3', 'A', '4', '5',
	 	   				   	'6', 'B', '7', '8', '9', 'C',
							'*', '0', '#', 'D',};	 

	 // Loop until all rows are scanned or key is found
	 // The row strobe bit falls off to the left of of i.B8 making i = NULL 	
	 while (KB_MATRIX[n] != NULL) {
		   KB_DATA_OUT = ~i; // Lower the active row strobe bit
		   asm("nop"); // Synchronize the I/O
		   asm("nop"); // There is a little parasitic capacitance here,
		   asm("nop"); // so wait a little longer
		   // Invert the incoming code & clear off the unused row strobe bits	 
		   temp = ~KB_DATA_IN;; // Invert and mask off row strobes 
		   asm("nop"); // Synchronize the I/O
		   KB_DATA_OUT |= i; // Raise the active row strobe bit

		   // Scan the input array "KB_MATIX[]" for a key code match
		   do {
		   	  if (KB_MATRIX[n] == temp) {
			  	 ActiveKey = KB_KEY[n];
				 return(ActiveKey);
			  }
		   } while(++n < q);

	  	   q += KEYS_PER_ROW; // Move to next row of keys
	 	   i = (i<<1); // Shift the active row bit one place to the Left.
	 }
	 return(-1);
}
/****************************************************************************/

/****************************************************************************/
// Clock cycle = 54nS @ 18.432MHz	
// Delay resolution ~ 1uS @ 18.432MHz
void LCD_Delay (unsigned int MicroSeconds) {
	 while (MicroSeconds-- != 0);
	 asm("nop");
}
/****************************************************************************/	

/****************************************************************************/
void LCD_PutString (char *CharacterString, unsigned char CursorPosition) {
	 unsigned char n = 0;
	 LCD_PutCmd (CursorPosition);
	 while (CharacterString[n] != NULL) {
		   LCD_PutChar (CharacterString[n++]);
	 }
}
/****************************************************************************/	

/****************************************************************************/	 
void LCD_PutChar (char Character) {

	 if (Character != -1) {
	 	// Send high nibble first
		LCD_DATA_OUT &= ~(1<<LCD_RW); // Put the display in the write mode
		LCD_DATA_OUT |= (1<<LCD_E);
		LCD_DATA_OUT &= ~DataMask; // Mask off old data, to bring in new data
		LCD_DATA_OUT |= (Character & DataMask); // Mask off upper nibble, send lower nibble	 
		asm("nop");
		LCD_DATA_OUT &= ~(1<<LCD_E); // Latch upper nibble to display
		asm("nop");
		// Send low nibble second
		LCD_DATA_OUT |= (1<<LCD_E);
		LCD_DATA_OUT &= ~DataMask; // Mask off old data, to bring in new data
		LCD_DATA_OUT |= ((Character<<4) & DataMask); // Shift lower nibble to higher nibble
	 	asm("nop");
		LCD_DATA_OUT &= ~(1<<LCD_E); // Latch lower nibble to display
		LCD_DATA_OUT |= (1<<LCD_RW); // Put the display in the read mode

		// Wait until the character is done processing	
		while (LCD_BusyWait() & (1<<LCD_BUSY_FLAG));
     }	
}
/****************************************************************************/	

/****************************************************************************/
void LCD_PutCmd (char Command) {

	 LCD_DATA_OUT &= ~(1<<LCD_RS); // Set display to "Register" mode
	 LCD_PutChar (Command);
	 LCD_DATA_OUT |= (1<<LCD_RS); // Set display to "Character" mode
}
/****************************************************************************/

/****************************************************************************/
unsigned char LCD_BusyWait (void) {

	unsigned char LCDStatus = NULL;
	 
	LCD_DATA_DDR &= ~DataMask; // Set PORTx high nibble to inputs
	LCD_DATA_OUT &= ~(1<<LCD_RS); // Set display to "Register" mode
	LCD_DATA_OUT |= (1<<LCD_RW); // Put the display in the read mode

	LCD_DATA_OUT |= (1<<LCD_E); // Toggle E for high nibble
	// Read the busy flag and upper 3 bits of the current character address
	while (LCD_DATA_IN & (1<<LCD_BUSY_FLAG)); // Wait for LCD busy flag		
	LCDStatus = LCD_DATA_IN & DataMask; // Get high nibble first
	LCD_DATA_OUT &= ~(1<<LCD_E); // Latch upper nibble to display
	asm("nop");
	LCD_DATA_OUT |= (1<<LCD_E); // Toggle E for low nibble
	asm("nop");
	// Read the lower nibble of the current character address
	LCDStatus |= ((LCD_DATA_IN>>4) & ~DataMask); // & ~DataMask); // Get lower nibble next	
	LCD_DATA_OUT &= ~(1<<LCD_E); // Latch lower nibble		
	
	LCD_DATA_OUT &= ~(1<<LCD_RW); // Put the display in the write mode
	LCD_DATA_OUT |= (1<<LCD_RS); // Set display to "Character" mode	
	LCD_DATA_DDR |= DataMask; // Set PORTx high nibble to outputs
	return (LCDStatus);
}   		 
/****************************************************************************/

/****************************************************************************/
void LCD_INIT (void) {

	 unsigned char i;

 	 LCD_Delay (300); // Wait for the LCD display to boot up

	 for (i = 0; i < 2; i++)
	 	 LCD_PutCmd (LCD_4_Bit | LCD_4_Line); // Put the display in 4 bit mode
	 // The display is now in 4 bit data mode

 	 LCD_PutCmd (LCD_CLR); // Clear the display	
	 LCD_PutCmd (LCD_ON); // Power up the display
}
/****************************************************************************/	

/****************************************************************************/
// System initialization

void port_init(void) {
	 // Turn on keyboard I/O PORTx.2:PORTx.0 Pull-up resistors
	 KB_DATA_OUT = (1<<PORTx2) | (1<<PORTx2) | (1<<PORTx1) | (1<<PORTx0);
	 KB_DATA_DDR = (1<<PORTx7) | (1<<PORTx6) | (1<<PORTx5) | (1<<PORTx4);

	 LCD_DATA_DDR = (1<<PORTx7) | (1<<PORTx6) | (1<<PORTx5) | (1<<PORTx4) 
	 	   	 	  | (1<<LCD_E) | (1<<LCD_LED) | (1<<LCD_RW) | (1<<LCD_RS); 
	 LCD_DATA_OUT |= (1<<LCD_RS);
}
/****************************************************************************/

/****************************************************************************/
// TIMER1 initialize - prescale:1
// WGM: 7) PWM 10 bit fast, TOP=0x03FF
// Desired value: 18.000KHz
// Actual value:  18.000KHz (0.000%)
/*
void timer1_init(void) {
	 TCCR1B = NULL; // Stop timer
	 TCNT1 = 0x0000; // Set count value
	 OCR1A  = 0x0511; // Set compare value
	 OCR1B  = 0x0511; // Set compare value
	 // Fast PWM, 10 Bit, Mode 7, Top = 0x3FF
	 TCCR1A = (1<<COM1A1) | (1<<WGM11) | (1<<WGM10);
	 TCCR1B = (1<<WGM12) | (1<<CS10); //Start Timer 1, CLK/1
}
*/
/****************************************************************************/

/****************************************************************************/
// Actual baud rate = 115.2K BAUD, (0.0% error) @ 18.432MHz
//#define BAUD_115200 7 

// Initialize UART0 
/*void USART_Init(void)
{
 	 UCSR0B = NULL; // Disable while setting baud rate
	 UCSR0A = NULL;
	 UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); // 8 bit data
	 UBRR0 = BAUD_115200; // Set baud rate
}
*/
/****************************************************************************/

/****************************************************************************/
// Call this routine to initialize all peripherals
void Init_Devices(void) {
	 // Stop errant interrupts until set up
 	 asm("cli");
	 port_init();
  	 LCD_INIT();		 
//	 timer1_init();
//	 USART_Init ();
   	 MCUCR = NULL;
//	 asm("sei");
 // All peripherals are now initialized
}
/****************************************************************************/

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

Thanks Carl - I actually referenced your code quite a few times when getting this all going. I just followed your clear screen code and shortened it for testing but got the same results:

// PB0 = RS, PB1 = RW, PB2 = E
// clear screen
PORTB &= ~(1 << PB0); // rs low (register mode)
_delay_ms(10);
PORTB &= ~(1 << PB1); // rw low (write mode)
_delay_ms(10);
PORTB |= (1 << PB2);  // set e high
_delay_ms(10);
PORTD = 0x01;         // put clear screen (0x01) on data bus
_delay_ms(10);
PORTB &= ~(1 << PB2); // set e low
_delay_ms(10);
PORTB |= (1 << PB1);  // set rw high (read mode)
_delay_ms(10);
PORTB |= (1 << PB0);  // rs high (character mode)

Any idea where I may be going wrong here?

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

Why these HUGE delays?
You only need a delay after you put your data (0x01) in PortD before making E low again and this delay is normally shorter than 1µS (500nS typical for my display).

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

Right, like I said - I'm just using the huge delays for testing, just to rule that out as an issue.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// clear screen 

// PB0 = RS, PB1 = RW, PB2 = E 

// Why don't you get a bit more abstract, so you know what you are working with
#define PB0 RS
#define PB1 RW
#define PB2 E
#define LCD_CLR 0x01

    PORTB &= ~((1 << RS) | (1 << RW));  // RS low (instruction mode), RW low (write mode)
    PORTD = LCD_CLR;         // Use outputing data on the LCD buss for some of your delay
    asm("nop");

    PORTB |= (1 << E);  // Set E high 
    _delay_ns(270); // Wait here 270nano Seconds
    PORTB &= ~(1 << E); // Set E low 
    asm("nop"); // Wait here about 100 nano Seconds
    asm("nop");
    asm("nop");

    PORTB |= (1 << RS) | (1 << RW);  // Set RS high (read mode), RW high (character mode)

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

Thanks Carl! I think I just had to put the command on the bus before bringing E high.

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

uidzer0 wrote:
Thanks Carl! I think I just had to put the command on the bus before bringing E high.

Does that mean it is working now???

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

Yes =]

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

uidzer0 wrote:
Yes =]

Great! Now have fun!!!

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston