Need help initializing display on ATmega162

avrlcd

I have a DEM 16217 SYH-PY 2×16 character LCD and I have problems initializing it.

I'm using AVR Studio 4. The 8 datalines are connected to port A of the microcontroller while the 3 control lines are connected to port E. Enable is PE2, R/W is PE1 and S is PE0. Port A connections are DBn to PORTAn. As far as I can see from the datasheet, no instructions seem to take more than 2 ms. For some reason, busy flag check isn't working correctly, so I used 2 ms delay instead.

At first, I wanted to have an LED blink after each step, but it turned out to be much better to have output at a serial port.

Here's my code:

#include <avr/io.h>
#include<util/delay.h>
#include "LED.h"
#include "Terminal.h"

void Screen_Execute(void)
{
    _delay_us(2);
    PORTE=PORTE | 4;
    _delay_us(2);
    PORTE=PORTE ^4;
    _delay_us(2);
    //_delay_us(2);

}

void Screen_busy(void)
{
    _delay_ms(2);
    //is the screen busy?   
    //PORTE=PORTE | 2;
    //DDRA=0x7F;
    //Screen_Execute();
    //while ((PINA & 128))
    //{
        //Screen_Execute();
        //BLINK_LED(500);
    //}// wait until zero
    //DDRA=0xFF;
    //PORTE=PORTE ^ 2;
    //Screen_Execute();
    //no longer busy
}




void Screen_init(void)
{

    //BLINK_LED(1000);
    SEND_STRING("Starting initialization\r\n",0);
    DDRE=0xFF;
    PORTE=0x00;
    DDRA=0xFF;
    PORTA=0x30;
    _delay_ms(250);
    _delay_ms(1000);
    SEND_STRING("Step 1 executing.\r\n",0);

    Screen_Execute();//voltage stabilized
    _delay_ms(5);//first output complete
    SEND_STRING("Step 1 complete.\r\n",0);


    //PORTA=0x30;
    //Screen_Execute();//second time
    //_delay_us(110);//end 

    //PORTA=0x30;//third time
    //Screen_Execute();

    SEND_STRING("Step 2 start.\r\n",0);
    //BLINK_LED(1000);
    Screen_busy();
    PORTA=0x38;//two lines, 8 bits, 5*7 characters
    SEND_STRING("Step 2 execute.\r\n",0);
    Screen_Execute();
    SEND_STRING("Step 2 complete.\r\n",0);

    SEND_STRING("Step 3 start.\r\n",0);
    //BLINK_LED(1000);
    Screen_busy();
    PORTA=0x08;//screen off
    SEND_STRING("Step 3 execute.\r\n",0);
    Screen_Execute();
    SEND_STRING("Step 3 complete.\r\n",0);


    SEND_STRING("Step 4 start.\r\n",0);
    //BLINK_LED(1000);
    Screen_busy();
    PORTA=0x14;//cursor goes to home and screen is cleared
    SEND_STRING("Step 4 execute.\r\n",0);
    Screen_Execute();
    SEND_STRING("Step 4 complete.\r\n",0);

    //Screen_busy();
    //PORTA=0x04;//
    //Screen_Execute();//end of init
    //Screen_busy();
    SEND_STRING("Step 5 start.\r\n",0);
    //BLINK_LED(1000);
    PORTA=0x0F;
    Screen_Execute();//cursor blinks, screen works
    SEND_STRING("Step 5 execute.\r\n",0);
    Screen_busy();
    SEND_STRING("Step 5 complete.\r\n",0);
}

My problem is that after initialization, cursor appears on a random position on the screen.

When I try to do initialization as described in the documentation, by sending the PORTA=0x30; line 3 times, it doesn't work. Also, if I try to use tighter timing, it again doesn't work.

Best Answer

I like the way you do serial port debugging, and the comments at each stage.

It appears that your display, like every other small character LCD display I've ever held in my hands, uses the Hitachi 44780 interface standard. May I ask why you aren't using some pre-tested off-the-shelf LCD library code?

Would something like the following work for you?

// WARNING: untested code. And therefore riddled with bugs.
#define SCREEN_ENABLE 4
#define SCREEN_READ 2
#define SCREEN_RS 1
// LCD #defines copied from http://techref.massmind.org/techref/io/lcd/44780.htm
#define LCDClearDisplay 1
#define LCDCursorHome   2
#define LCDOff      8   ;0x08 turn the display (and the cursor) off.
#define LCDOn       12  ;0x0C Turn on the display, but without a cursor
#define LCDCursorOn 14  ;0x0E Turn on the cursor (as an underline) as well as the display
#define LCDCursorBlink  15  ;0x0F Turn on a blinking block cursor as well as the display
#define LCDCursorLeft   16  ;0x10 Move the cursor left by one character
#define LCDCursorRight  20  ;0x14 Move the cursor right one letter
#define LCDShiftLeft    24  ;0x18 Shift the entire display left one character
#define LCDShiftRight   30  ;0x1C Shift the entire display right one character
#define LCDGoto     128 ;0x80 Add the location of the character position 
                ;   to which you wish to move the cursor

void screen_enable(void){
    _delay_us(2);
    PORTE |= SCREEN_ENABLE;
    _delay_us(2); // must be at least 140 ns
}
void screen_disable(void){
    PORTE &= ~SCREEN_ENABLE;
    _delay_us(2);
}
void Screen_Execute(void)
{
    screen_enable();
    screen_disable();
}

/** wait until screen is no longer busy */
void screen_busywait(void)
{
    char busy = 0x80;
    DDRA=0x00; // all inputs, to avoid fighting the screen outputs
    while(busy){
        // read busy flag
        PORTE |= SCREEN_READ; // read
        PORTE &= ~SCREEN_RS; // instruction: read busy flag and address
        screen_enable();
        // read busy bit while the Enable bit is high
        busy = PINA & 0x80;
        screen_disable;
        //BLINK_LED(500);
    }// wait until no longer busy

    // switch to "normal" "MCU writes to display" mode.
    PORTE &= ~SCREEN_READ; // write
    DDRA=0xFF;
}

void screen_send_instruction( char instruction ){
    PORTA=instruction;
    PORTE=0x00 + SCREEN_RS; // RS high: instruction
    DDRE=0xFF;
    DDRA=0xFF;
    Screen_Execute();
};

void Screen_init(void)
{    
    //BLINK_LED(1000);
    SEND_STRING("Starting initialization\r\n",0);
    // see http://techref.massmind.org/techref/io/lcd/44780.htm

    _delay_ms(15);
    SEND_STRING("Step 1 executing.\r\n",0);
    screen_send_instruction( 0x30 );
    _delay_ms(250);
    SEND_STRING("Step 1 complete.\r\n",0);

    screen_send_instruction( 0x30 );//second time
    _delay_ms(5);
    screen_send_instruction( 0x30 );//third time
    _delay_ms(1);

    SEND_STRING("Step 2 start.\r\n",0);
    screen_send_instruction( 0x38 ); // 8 bit interface, 2 line display, normal font
    SEND_STRING("Step 2 complete.\r\n",0);

    SEND_STRING("Step 3 start.\r\n",0);
    //BLINK_LED(1000);
    screen_busywait();
    screen_send_instruction( LCDOff );//screen off
    SEND_STRING("Step 3 complete.\r\n",0);

    screen_busywait();
    screen_send_instruction( 0x06 );// Entry mode: increment

    screen_busywait();
    screen_send_instruction( LCDClearDisplay );//cursor goes to home and screen is cleared

    screen_busywait();
    screen_send_instruction( LCDCursorOn );//screen on and underline cursor on.

    SEND_STRING("Step 4 start.\r\n",0);
    //BLINK_LED(1000);
    screen_busywait();
    screen_send_instruction( 0x14 );//Move the cursor right one letter (why????)
    SEND_STRING("Step 4 complete.\r\n",0);

    SEND_STRING("Step 5 start.\r\n",0);
    //BLINK_LED(1000);
    screen_busywait();
    screen_send_instruction( LCDCursorBlink );//Turn on a blinking block cursor as well as the display
    SEND_STRING("Step 5 complete.\r\n",0);
}

p.s.: Rather than use 11 pins of your MCU to control the display, some people sacrifice a little speed to gain back some of those pins: some control the LCD display with 7 pins of the MCU in 4-bit mode, others control their LCD display with 1 pin of the MCU.