Newhaven 4x40 Display Initialization - Lines 1 & 3 Solid

Thread Starter

andy.senn

Joined May 17, 2016
4
Hey, guys:

First of all, I'm new here, so I'm very hopeful that I've found the right place to ask my question! :)

I've been working on learning electronics on my own. I'm currently working on programming an Atmel MCU (ATmega88A) to control a Newhaven 4x40 character display (NHD-0440WH-ATMI-JT#). I've followed the specification as closely as I could, but after days of banging my head against the wall, certain that I was just doing something dumb, I've decided to ask for some help. I've found a couple of folks with similar problems, but the responses have not helped me much. I'm also trying to learn C in the process, so please let me know if you have any constructive criticism there, too.

Please remember that I'm learning. I'm a Java engineer by day, so this is new territory to me. :)

When I power it up, lines 1 and 3 of the display are solid, while lines 2 and 4 are blank. Here is what I have so far:

lcd.h
Code:
#ifndef LCD_H_
#define LCD_H_


#define LCD_DATA_DDR    DDRB
#define LCD_CTRL_DDR    DDRD

#define LCD_DATA        PORTB
#define LCD_CTRL        PORTD

#define LCD_E1            PIND0        // Operation Enable for Top 2 Lines
#define LCD_RW            PIND1        // Read / Write (1/0)
#define LCD_RS            PIND2        // Register Select (Command Register/Data Register)
#define LCD_E2            PIND3        // Operation Enable for Bottom 2 Lines

#define DEBUG_LED        PINC5

#define LCD_CMD_CS        0b00000001    // Clear Screen
#define LCD_CMD_HOME    0b00000010    // Return Cursor to Home Position
#define LCD_CMD_DIR        0b00000100    // Set: Cursor Move Direction [ Pin 1 ], Display Shift [ Pin 0 ]
#define LCD_CMD_PWR        0b00001000    // Set Display: Full [ Pin 2 ], Cursor [ Pin 1 ], [ Pin 0 ] Cursor Position
#define LCD_CMD_SHIFT    0b00010000    // Set: Shift Control [ Pin 3 ], Cursor Move Direction (RTL/LTR) [ Pin 2 ]
#define LCD_CMD_FUNC    0b00100000    // Set: Data Length (8/4 Bits) [ Pin 4 ], Line Number (2/1) [ Pin 3 ], Font Size (5x11/5x8) [ Pin 2 ]
#define LCD_CMD_CGRAM    0b01000000    // Set: CGRAM Address [ Pins 5:0 ]
#define LCD_CMD_DDRAM    0b10000000    // Set: DDRAM Address [ Pins 6:0 ]

#define LCD_COLS        40
#define LCD_ROWS        4

void lcd_command ( int _command );
void lcd_init ( void );
void lcd_wait ( void );
void lcd_write ( char *_text );
void lcd_write_char ( unsigned char _char );

#endif /* LCD_H_ */
lcd.c
Code:
#include <asf.h>
#include <util/delay.h>
#include "lcd.h"

void lcd_command ( int _command ) {
    lcd_wait();
  
    LCD_DATA = _command;
    LCD_CTRL &= ~ 1 << LCD_RW;
    LCD_CTRL &= ~ 1 << LCD_RS;
  
    lcd_wait();
  
    LCD_DATA = 0;
}

void lcd_init ( void ) {
    _delay_ms( 1000 ); // Specification >40mS
    lcd_command( 0x3C );
    _delay_us( 100 ); // Specification: >37uS
    lcd_command( 0x3C );
    _delay_us( 100 ); // Specification: >37uS
    lcd_command( LCD_CMD_PWR + 0x07 );
    _delay_us( 100 ); // Specification: >37uS
    lcd_command( LCD_CMD_CS );
    _delay_ms( 10 ); // Specification: >1.52mS
    lcd_command( LCD_CMD_DIR + 0x02 );
}

void lcd_wait ( void ) {
    LCD_DATA_DDR = 0x00;
  
    LCD_CTRL |= 1 << LCD_RW;
    LCD_CTRL &= ~ 1 << LCD_RS;
  
    while ( LCD_DATA > 0x80 ) {
        LCD_CTRL |= 1 << LCD_E1 | 1 << LCD_E2;
        asm volatile ( "nop" );
        LCD_CTRL &= ~ 1 << LCD_E1 | 1 << LCD_E2;
    }
  
    LCD_DATA_DDR = 0xFF;
}

void lcd_write ( char *_text ) {
    while ( *_text > 0 ) { lcd_write_char( *_text++ ); }
}

void lcd_write_char ( unsigned char _char ) {
    lcd_wait();
  
    LCD_DATA = _char;
    LCD_CTRL &= ~ 1 << LCD_RW;
    LCD_CTRL |= 1 << LCD_RS;
  
    lcd_wait();
  
    LCD_DATA = 0x00;
}
main.c
Code:
#include <asf.h>
#include "lcd/lcd.h"

int main ( void )
{
    board_init();
  
    DDRB = 0b11111111;
    DDRC = 0b11111111;
    DDRD = 0b11111111;
  
    lcd_init();
  
    lcd_write( "Hello, World!" );
  
    while ( 1 ) {
      
    }
}
I hate to drop a bunch of code on here, but I really don't know where the problem resides. I assume that I'm doing something wrong as early as 'lcd_init()' since lines 1 and 3 are still solid.

Any help whatsoever would be very, very much appreciated!
 

MrChips

Joined Oct 2, 2009
30,810
This is a common result when the display is not initialized correctly. I have not looked into your code in full detail but here are some suggestions.

Are you using 4-bit or 8-bit interface?
A circuit schematic of your interface would be useful to examine for issues.

Check to see where your code is ending up. Is it reaching the final while (1) loop?

Replace the lcd_wait() function with a simple _delay_ms(10) for now.

I have some code written in my MSP430 blog that you can examine.
While this is for a different processor, the steps for initializing the LCD are shown.
Note that this example uses a 4-bit interface.
 

Sensacell

Joined Jun 19, 2012
3,448
I have experienced a similar problem in the past.
If I remember correctly, the address of the 2nd line character position is not what you think it is- read the data sheet carefully, it jumps over a range in an awkward way - it's a bit counter-intuitive.
 

ErnieM

Joined Apr 24, 2011
8,377
I cannot follow your lcd_command Or lcd_write_char functions as you haven't included some symbols but I suspect the problem is in there as I do not see you exercising the E or clock line.

One excellent first post though. You actually found the code tags and your code is well spaced and self documenting.

You must do this stuff for a living. <g>

Welcome to the forums!
 

Thread Starter

andy.senn

Joined May 17, 2016
4
Hey, guys:

Thank you very much for your feedback. There are a few things that are wrong, and your comments helped bring those to light.

In the end, I ended up just writing a bare-bones implementation of the LCD initialization as defined in the specification. My implementation looks like this:

main.c
Code:
#include <avr/io.h>
#include <avr/portpins.h>
#include <util/delay.h>
#include "main.h"

int main( void )
{
    DDRB = 0b11111111; // Data
    DDRD = 0b11111111; // Control
   
    _delay_ms( 15 );
    command1( 0x30 );
    command2( 0x30 );
   
    _delay_ms( 5 );
    command1( 0x30 );
    command2( 0x30 );
   
    _delay_ms( 5 );
    command1( 0x30 );
    command2( 0x30 );
   
    _delay_ms( 5 );
    command1( 0x38 );
    command2( 0x38 );
   
    command1( 0x08 );
    command2( 0x08 );
   
    command1( 0x01 );
    command2( 0x01 );
   
    command1( 0x06 );
    command2( 0x06 );
   
    command1( 0x0C );
    command2( 0x0C );
   
    _delay_ms( 10 );
    writedata1( 'H' );
    writedata1( 'e' );
    writedata1( 'l' );
    writedata1( 'l' );
    writedata1( 'o' );
    writedata1( ',' );
    writedata1( ' ' );
    writedata1( 'w' );
    writedata1( 'o' );
    writedata1( 'r' );
    writedata1( 'l' );
    writedata1( 'd' );
    writedata1( '!' );
   
    _delay_ms( 10 );
    writedata2( 'H' );
    writedata2( 'e' );
    writedata2( 'l' );
    writedata2( 'l' );
    writedata2( 'o' );
    writedata2( ',' );
    writedata2( ' ' );
    writedata2( 'w' );
    writedata2( 'o' );
    writedata2( 'r' );
    writedata2( 'l' );
    writedata2( 'd' );
    writedata2( ' ' );
    writedata2( '2' );
    writedata2( '!' );
   
   
    while (1)
    {
       
    }
}

void command1( char i )
{
    PORTB = i;
    PORTD |= 1 << PIND0;
    _delay_ms( 2 );
    PORTD &= ~ 1 << PIND0;
}

void command2( char i )
{
    PORTB = i;
    PORTD |= 1 << PIND6;
    _delay_ms( 2 );
    PORTD &= ~ 1 << PIND6;
}

void writedata1( char i )
{
    PORTB = i;
    PORTD |= 1 << PIND0 | 1 << PIND2;
    _delay_ms( 2 );
    PORTD &= ~ 1 << PIND0;
}

void writedata2( char i )
{
    PORTB = i;
    PORTD |= 1 << PIND6 | 1 << PIND2;
    _delay_ms( 2 );
    PORTD &= ~ 1 << PIND6;
}
This worked to initialize the first controller (the first 2 lines), but I'm still getting a solid 3rd line. Either way, this is definitely some much needed progress. :) I should have known to do this earlier.

Thanks again for the feedback, guys. This simple accomplishment has re-motivated me to keep playing with this stuff. :)
 

Thread Starter

andy.senn

Joined May 17, 2016
4
And yes, @ErnieM. As a Senior Software Engineer, I've used forums like this before. I've just become a bit gun-shy thanks to the Stack Exchange sites. :)

Thanks for your understanding. It's great to know that there's a community out there that is willing to help those that are just getting started.
 

Thread Starter

andy.senn

Joined May 17, 2016
4
I just figured out the issue with the 2nd controller. Since pins 12-14 on the display are power-related, I skipped those and wired E2 to PIND3, not PIND6. I was just being dumb. Thanks again, guys!
 
Top