Routines to control an HD44780-based LCD module emulating a tty screen

by Isaac Marino Bavaresco

This is file "LCD.c".

//==============================================================================
// Copyright (c) 2005-2010, Isaac Marino Bavaresco
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Neither the name of the author nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//==============================================================================
// isaacbavaresco@yahoo.com.br
//==============================================================================
// Set TAB width to 4 characters
//==============================================================================

#include "LCDcfg.h"
//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================

#if         defined USE_READ_PIN && defined DETECT_FAILURE

// Flag to indicate that the display is defective.
unsigned char               displayfailed   = 0;

#endif  //  defined USE_READ_PIN && defined DETECT_FAILURE

//==============================================================================
#define LCDWriteCmd(c)  LCDWrite((c),0)
#define LCDWriteData(c) LCDWrite((c),1)

#define CMD_CLEAR_DISPLAY       0x01
#define CMD_SET_DDRAM_ADDRESS   0x80
//==============================================================================
// Writes a character or command to the display.
// Arguments:
//      c       The character or command to be written
//      di      Type of write: 0 = command, 1 = character

static void LCDWrite( unsigned char c, unsigned char di )
    {
    // Variable to save the interrupts state at function entry.
    DeclareIntSaveVar( Aux );

#if         defined USE_READ_PIN
    unsigned char       s;

#if         defined DETECT_FAILURE
    unsigned short      n   = NUMBER_OF_READS_TIMEOUT;

    // The display has failed previously...
    if( displayfailed )
        // ... so we will not even try to access it
        return;
#endif  //  defined DETECT_FAILURE

#endif  //  defined USE_READ_PIN

    SaveInterruptsState( Aux );

#if         defined USE_READ_PIN

    // Repeat while bit BUSY == 1
    do
        {
        RestoreInterruptsState( Aux );

        // At this point a pending interrupt may be serviced, so we don't
        // block the system for too long. On return, no assumption is
        // made about the state of the LCD interface pins, an interrupt
        // routine may change the value or direction of any pin, except
        // of the ENABLE pin.

        DisableInterrupts();

        SetDataPortAsInputs();
        // Set READ/!WRITE pin as read (1)
        SetRWAsRead();
        // Set DATA/!INSTRUCTION pin as instruction (0)
        SetDIAsInstruction();

        // Effectivate the data transfer.
        SetEAsEnabled();
        // Wait for the minimum 'tHIGH'.
        Delay500ns();
        // Read the status byte (only high nibble if in 4-bit mode).
        ReadDataPortValue( s );
        // Low half of the trasfer cycle.
        SetEAsDisabled();
        // Wait for the minimum 'tLOW'.
        Delay500ns();

#if         defined USE_FOUR_BIT_INTERFACE

        // When using a 4-bit interface we need to do a dummy read

        // Effectivate the data transfer.
        SetEAsEnabled();
        // Wait for the minimum 'tHIGH'.
        Delay500ns();
        // Low half of the trasfer cycle.
        SetEAsDisabled();
        // Wait for the minimum 'tLOW'.
        Delay500ns();

#endif  //  defined USE_FOUR_BIT_INTERFACE

        // Set READ/!WRITE pin as write (0)
        SetRWAsWrite();
        }
#if         !defined DETECT_FAILURE

    // Repeat until the BUSY bit is zero
    while( s & 0x80 );

#else   //  !defined DETECT_FAILURE

    // Repeat until the BUSY bit is zero or until the maximum number of repetitions.
    while(( s & 0x80 ) && ( --n != 0u ));

    // We tried the maximum number of times...
    if( n == 0u )
        // ... so we flag the display as failed
        displayfailed   = 1;

#endif  //  !defined DETECT_FAILURE

#else   //  defined USE_READ_PIN

    DisableInterrupts();

#endif  //  defined USE_READ_PIN

    //--------------------------------------------------------------------------
    // Now we may send the data.
    //--------------------------------------------------------------------------

    // Set READ/!WRITE pin as write (0)
    SetRWAsWrite();
    // Set DATA/!INSTRUCTION pin as data (1).
    SetDIValue( di );
    // The data (only high nibble if in 4-bit mode) is output to the data port.
    SetDataPortValue( c );
    SetDataPortAsOutputs();
    // Effectivate the data transfer.
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Low half of the trasfer cycle.
    SetEAsDisabled();
    // Wait for the minimum 'tLOW'.
    Delay500ns();

#if         defined USE_FOUR_BIT_INTERFACE
    // The low nibble is output to the data port.
    SetDataPortValueLow( c );
    // Effectivate the data transfer.
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Low half of the trasfer cycle.
    SetEAsDisabled();
    // Wait for the minimum 'tLOW'.
    Delay500ns();
#endif  //  defined USE_FOUR_BIT_INTERFACE


    SetDataPortAsInputs();
    RestoreInterruptsState( Aux );

#if         !defined USE_READ_PIN

    // If we are not reading the busy flag, then we must wait at least for
    // the worst-case execution time before any other command ca be executed.

    // This write is a "clear screen" or a "home cursor" command...
    if( di == 0 && c <= 3 )
        // ... that takes up to 1.52ms to execute.
        Delay1520us();
    // For all other writes...
    else
        // ... the execution time is no more than 37us.
        Delay37us();

#endif  //  !defined USE_READ_PIN

    }
//==============================================================================
/*
static unsigned char LCDReadStatus( void )
    {
    // Variable to save the interrupts state at function entry.
    DeclareIntSaveVar( Aux );
    // Variable to temporarily hold the value read.
    unsigned char       s;

    SaveInterruptsState( Aux );

    DisableInterrupts();

    SetDataPortAsInputs();
    // Set READ/!WRITE pin as read (1)
    SetRWAsRead();
    // Set DATA/!INSTRUCTION pin as instruction (0)
    SetDIAsInstruction();

    // Effectivate the data transfer.
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Read the status byte (only high nibble if in 4-bit mode).
    ReadDataPortValue( s );
    // Low half of the trasfer cycle.
    SetEAsDisabled();
    // Wait for the minimum 'tLOW'.
    Delay500ns();

#if         defined USE_FOUR_BIT_INTERFACE

    // Effectivate the data transfer.
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Read the status byte (low nibble only).
    ReadDataPortValueLow( s );
    // Low half of the trasfer cycle.
    SetEAsDisabled();
    // Wait for the minimum 'tLOW'.
    Delay500ns();

#endif  //  defined USE_FOUR_BIT_INTERFACE


    // Set READ/!WRITE pin as write (0)
    SetRWAsWrite();

    RestoreInterruptsState( Aux );

    // Return the read data.
    return s;
    }
*/
//==============================================================================
// Reads one data byte from the LCD.

#if         defined USE_READ_PIN
static unsigned char LCDReadData( void )
    {
    // Variable to save the interrupts state at function entry.
    DeclareIntSaveVar( Aux );
    // Variable to temporarily hold the value read.
    unsigned char       s;

#if         defined DETECT_FAILURE
    // Variable to count the number of busy flag reads before an error is signaled.
    unsigned short      n   = NUMBER_OF_READS_TIMEOUT;

    // If the display failed previously...
    if( displayfailed )
        // ... we are not using it until some routine clears the error flag.
        return 0x00;
#endif  //  defined DETECT_FAILURE

    SaveInterruptsState( Aux );

    // Repeat while busy flag == 1
    do
        {
        RestoreInterruptsState( Aux );

        // At this point a pending interrupt may be serviced, so we don't
        // block the system for too long. On return, no assumption is
        // made about the state of the LCD interface pins, an interrupt
        // routine may change the value or direction of any pin, except
        // of the ENABLE pin.

        DisableInterrupts();

        SetDataPortAsInputs();
        // Set the READ/!WRITE pin as read (1)
        SetRWAsRead();
        // Set DATA/!INSTRUCTION pin as instruction (0)
        SetDIAsInstruction();

        // Effectivate the data transfer.
        SetEAsEnabled();
        // Wait for the minimum 'tHIGH'.
        Delay500ns();
        // Read the status byte (only high nibble if in 4-bit mode).
        ReadDataPortValue( s );
        // Low half of the trasfer cycle.
        SetEAsDisabled();
        // Wait for the minimum 'tLOW'.
        Delay500ns();

#if         defined USE_FOUR_BIT_INTERFACE

        // When using a 4-bit interface we need to do a dummy read

        // Effectivate the data transfer.
        SetEAsEnabled();
        // Wait for the minimum 'tHIGH'.
        Delay500ns();
        // Low half of the trasfer cycle.
        SetEAsDisabled();
        // Wait for the minimum 'tLOW'.
        Delay500ns();

#endif  //  defined USE_FOUR_BIT_INTERFACE

        // Set READ/!WRITE pin as write (0)
        SetRWAsWrite();
        }
    // Test the LCD's BUSY flag.
#if         !defined DETECT_FAILURE
    while( s & 0x80 );
#else   //  !defined DETECT_FAILURE
    while(( s & 0x80 ) && ( --n != 0u ));

    if( n == 0u )
        displayfailed   = 1;
#endif  //  !defined DETECT_FAILURE


    //--------------------------------------------------------------------------
    // Now we can read the data.
    //--------------------------------------------------------------------------

    // Set READ/!WRITE pin as read (1)
    SetRWAsRead();
    // Set DATA/!INSTRUCTION pin as data (1).
    SetDIAsData();
    // Effectivate the data transfer.
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Read the data byte (only high nibble if in 4-bit mode).
    ReadDataPortValue( s );
    // Low half of the trasfer cycle.
    SetEAsDisabled();
    // Wait for the minimum 'tLOW'.
    Delay500ns();

#if         defined USE_FOUR_BIT_INTERFACE

    // Effectivate the data transfer.
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Read the data byte (low nibble only).
    ReadDataPortValueLow( s );
    // Low half of the trasfer cycle.
    SetEAsDisabled();
    // Wait for the minimum 'tLOW'.
    Delay500ns();

#endif  //  defined USE_FOUR_BIT_INTERFACE

    // Set READ/!WRITE pin as write (0)
    SetRWAsWrite();

    RestoreInterruptsState( Aux );

    // Return the read data.
    return s;
    }

#endif  //  defined USE_READ_PIN

//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================
//
//
// Hardware-independent part
//
//
//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================


//==============================================================================

// Current coordinates of the cursor.
static unsigned char    cursorx         = 0,    cursory = 0;

// Number of columns and lines of the screen.
static unsigned char    maxx            = INITIAL_MAXX, maxy    = INITIAL_MAXY;

#if         defined USE_DELAYED_SCROLL
// Flag to enable the delayed scroll mode.
static unsigned char    delayedscroll   = 0;
#endif  //  defined USE_DELAYED_SCROLL

//==============================================================================
unsigned char getmaxx( void )
    {
    return maxx;
    }
//==============================================================================
unsigned char getmaxy( void )
    {
    return maxy;
    }
//==============================================================================
unsigned char getcursorx( void )
    {
    return cursorx + 1;
    }
//==============================================================================
unsigned char getcursory( void )
    {
    return cursory + 1;
    }
//==============================================================================

#if         defined USE_DELAYED_SCROLL

void setscrollmode( unsigned char c )
    {
    delayedscroll = c;
    }

#endif  //  defined USE_DELAYED_SCROLL

//==============================================================================

#if         !defined USE_READ_PIN

static unsigned char screenbuffer[MAXIMUM_MAXY][MAXIMUM_MAXX];
static void scroll( void )
    {
    unsigned char x, y, c, *p, *q;

    // Locate the cursor at the top left corner of the LCD.
    LCDWriteCmd( 0x80 );
    for( y = maxy - 1, p = screenbuffer, q = screenbuffer + maxx; y; y-- )
        {
        // Locate the cursor at the begining of the current text line
        LCDWriteCmd( 0x80 | (( y << 6 ) & 0x40 ) + ( y & 0x02 ? maxx : 0 ));
        for( x = maxx; x; x-- )
            {
            *p++ = c = *q++;
            LCDWriteData( c );
            }
        }

    // Locate the cursor at the begining of the last text line
    LCDWriteCmd( 0x80 | ((( maxy - 1 ) << 6 ) & 0x40 ) + (( maxy - 1 ) & 0x02 ? maxx : 0 ));
    // Iterate for all columns of the last text line...
    for( x = 0; x < maxx; x++ )
        {
        // ... fill the last text line with spaces
        *p++ = ' ';
        LCDWriteData( ' ' );
        }

    // Locate the cursor to its original position
    LCDWriteCmd( 0x80 | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x3f ) + ( cursory & 0x02 ? maxx : 0 ));
    }

#else   //  !defined USE_READ_PIN

static unsigned char screenbuffer[MAXIMUM_MAXX];
static void scroll( void )
    {
    unsigned char x, y;

    // Iterate for all text lines, starting from the second...
    for( y = 1; y < maxy; y++ )
        {
        // Locate the cursor at the begining of the current text line
        LCDWriteCmd( 0x80 | (( y << 6 ) & 0x40 ) + ( y & 0x02 ? maxx : 0 ));
        // Iterate for all columns of the current text line...
        for( x = 0; x < maxx; x++ )
            // ... read all the line characters to the buffer
            screenbuffer[x] = LCDReadData();
        // Locate the cursor at the begining of the text line right above the current one
        LCDWriteCmd( 0x80 | ((( y - 1 ) << 6 ) & 0x40 ) + (( y - 1 ) & 0x02 ? maxx : 0 ));
        // Iterate for all columns of the text line...
        for( x = 0; x < maxx; x++ )
            // ... print the whole line back
            LCDWriteData( screenbuffer[x] );
        }

    // Locate the cursor at the begining of the last text line
    LCDWriteCmd( 0x80 | ((( maxy - 1 ) << 6 ) & 0x40 ) + (( maxy - 1 ) & 0x02 ? maxx : 0 ));
    // Iterate for all columns of the last text line...
    for( x = 0; x < maxx; x++ )
        // ... fill the last text line with spaces
        LCDWriteData( ' ' );

    // Locate the cursor to its original position
    LCDWriteCmd( 0x80 | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x3f ) + ( cursory & 0x02 ? maxx : 0 ));
    }

#endif  //  !defined USE_READ_PIN

//==============================================================================
void clrscr( void )
    {
//  interruptstate_t    Aux;
//
//  SaveInterruptsState( Aux );
//  DisableInterrupts();

    // Locate the cursor at the top-left corner of the screen
    cursorx = 0;
    cursory = 0;
    // Send the clear screen command to the LCD controller
    LCDWriteCmd( CMD_CLEAR_DISPLAY );

//  RestoreInterruptsState( Aux );
    }
//==============================================================================
void gotoxy( unsigned char x, unsigned char y )
    {
//  interruptstate_t    Aux;
//
//  SaveInterruptsState( Aux );
//  DisableInterrupts();

    // If the coordinates are valid...
    if( x <= maxx && y <= maxy )
        {
        // If the column is different than zero...
        if( x != 0u )
            // ... change the current column to it.
            cursorx = x - 1;
        // If the line is different than zero...
        if( y != 0u )
            // ... change the current line to it.
            cursory = y - 1;

        // We need to positon the hardware cursor to the right place.
        LCDWriteCmd( CMD_SET_DDRAM_ADDRESS | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x3f ) + ( cursory & 0x02 ? maxx : 0 ));
        }

//  RestoreInterruptsState( Aux );
    }
//==============================================================================
// This routine's real name is defined in the file 'LCDcfg.h'.
// The name must be chosen to allow the library routines (printf, etc.) to link
// to it.

void LCD_PUTC( unsigned char c )
    {
//  interruptstate_t    Aux;
//
//  SaveInterruptsState( Aux );
//  DisableInterrupts();

#if         defined USE_DELAYED_SCROLL
    // There is a pending scroll and the current character is not '\a', '\b' or '\f', ...
    if( cursorx >= maxx && c != (char)'\a' && c != (char)'\b' && c != (char)'\f' )
        {
        // ... return the cursor to the beginning of the next line...
        cursorx = 0;
        // ... and increment the line.
        cursory++;
        // The cursor is beyond the last line, ...
        if( cursory >= maxy )
            {
            // ... position the cursor on the last line...
            cursory = maxy - 1;
            // ... and do a 'scroll'.
            scroll();
            }
        }
#endif  //  defined USE_DELAYED_SCROLL

    switch( c )
        {

        //----------------------------------------------------------------------
        // '\a' = BELL

        case '\a':
            Beep();
//          RestoreInterruptsState( Aux );
            return /*0*/;

        //----------------------------------------------------------------------
        // '\b' = BACKSPACE

        case '\b':
            // The cursor is not on the first column, ...
            if( cursorx > 0u )
                // ... decrement the column.
                cursorx--;
            // The cursor is on the first column but it is not on the first line, ...
            else if( cursory > 0u )
                {
                // ... position the cursor at the end of the previous line.
                cursory--;
                cursorx = maxx - 1;
                }
            // The LCD must be updated with the correct cursor coordinates.
            LCDWriteCmd( 0x80 | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x0f ) + ( cursory & 0x02 ? maxx : 0 ));
            // ... finished.
//          RestoreInterruptsState( Aux );
            return /*0*/;

        //----------------------------------------------------------------------
        // '\f' = FORMFEED

        case '\f':
            // Clear the screen.
            clrscr();
//          RestoreInterruptsState( Aux );
            return /*0*/;
        //----------------------------------------------------------------------
        // '\n' = NEWLINE

        case '\n':
            // Return the cursor to the beginning of the line.
            cursorx = 0;
            // Fall-through to 'VTAB', to increment the line

        //----------------------------------------------------------------------
        // '\v' = VTAB

        case '\v':
            // Increment the line number.
            cursory++;
            // Outside the 'switch' we will check if a 'scroll' is needed.
            break;

        //----------------------------------------------------------------------
        // '\r' = CR (carriage return)

        case '\r':
            // Return the cursor to the beginning of the line.
            cursorx = 0;
            // The LCD must be updated with the correct cursor coordinates.
            LCDWriteCmd( 0x80 | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x0f ) + ( cursory & 0x02 ? maxx : 0 ));
            // Fim
//          RestoreInterruptsState( Aux );
            return /*0*/;

        //----------------------------------------------------------------------
        // Aqui tratamos do caractere tabulação ('\t' = TAB)

        case '\t':
            {
            // We need a temporary variable.
            unsigned char   Temp;
            // Calculate the column of the next tabulation.
            Temp = ( cursorx + TAB_WIDTH ) & ~( TAB_WIDTH - 1 );
            // The cursor will be beyond the column after the last, ...
            if( Temp > maxx )
                // ... restrict the cursor to the column after the last.
                Temp = maxx;
            // Calculate how many spaces must be inserted.
            Temp    -= cursorx;
            // Advance the column to its final position.
            cursorx += Temp;
            // Print as many spaces as needed to reach the final position
            for( ; Temp; Temp-- )
                LCDWriteData( ' ' );

#if         defined USE_DELAYED_SCROLL

            // The cursor ended beyond the last column but it is not on the last line, ...
            if( cursorx >= maxx && ( cursory + 1 < maxy || !delayedscroll ))

#else   //  defined USE_DELAYED_SCROLL

            // The cursor ended beyond the last column, ...
            if( cursorx >= maxx )

#endif  //  defined USE_DELAYED_SCROLL
                {
                // ... position the cursor to the beginning of the next line.
                cursorx = 0;
                cursory++;
                // Outside the 'switch' we will check if a 'scroll' is needed.
                break;
                }

            // The cursor is yet on the same line, we may return right now.
//          RestoreInterruptsState( Aux );
            return /*0*/;
            }
        //----------------------------------------------------------------------
        // Here we cope with the printable characters.

        default:
            // Print the character.
            LCDWriteData( c );
            // Increment the column.
            cursorx++;

#if         defined USE_DELAYED_SCROLL

            // The cursor ended beyond the last column but it is not on the last line, ...
            if( cursorx >= maxx && ( cursory + 1 < maxy || !delayedscroll ))

#else   //  defined USE_DELAYED_SCROLL

            // The cursor ended beyond the last column, ...
            if( cursorx >= maxx )

#endif  //  defined USE_DELAYED_SCROLL
                {
                // ... position the cursor to the beginning of the next line.
                cursorx = 0;
                cursory++;
                // Outside the 'switch' we will check if a 'scroll' is needed.
                break;
                }
            // The cursor is yet on the same line, we may return right now.
//          RestoreInterruptsState( Aux );
            return /*0*/;
        }
    //--------------------------------------------------------------------------
    // The cursor is beyond the last line, ...
    if( cursory >= maxy )
        {
        // ... position it on the last line...
        cursory = maxy - 1;
        // ... and do a scroll.
        scroll();
        // We may return now, the 'scroll' already positioned the cursor on the right place.
//      RestoreInterruptsState( Aux );
        return /*0*/;
        }
    // We must position the LCD cursor on the right place.
    LCDWriteCmd( 0x80 | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x0f ) + ( cursory & 0x02 ? maxx : 0 ));
    //--------------------------------------------------------------------------
//  RestoreInterruptsState( Aux );
    return /*0*/;
    }
//==============================================================================
void LCDControlCursor( unsigned char Mode )
    {
    LCDWriteCmd(( Mode & 0x03 ) | 0x0c );
    }
//==============================================================================
void LCDDefineChar( unsigned char Char, const unsigned char *Pattern )
    {
    unsigned char c;

    LCDWriteCmd((( Char << 3 ) & 0x38 ) | 0x40 );
    for( c = 8; c; c-- )
        LCDWriteData( *Pattern++ );
    LCDWriteCmd( 0x80 | (( cursory << 6 ) & 0x40 ) | ( cursorx & 0x3f ) + ( cursory & 0x02 ? maxx : 0 ));
    }
//==============================================================================
void LCDInit( void )
    {
    // Initialize the LCD data interface.

    // The initial value of the LCD's ENABLE pin is 0 (disabled).
    SetEAsDisabled();
    // The LCD's ENABLE pin is an output.
    SetEAsOutput();

    // The initial value of the LCD's READ/!WRITE pin is 0 (write).
    SetRWAsWrite();
    // The LCD's READ/!WRITE pin is an output.
    SetRWAsOutput();

    // The initial value of the LCD's DATA/!INSTRUCTION pin is 0 (data).
    SetDIAsInstruction();
    // The LCD's DATA/!INSTRUCTION pin is an output.
    SetDIAsOutput();

    // Chip initialization, following data-sheet procedure.

    // Delay mandated by the data-sheet

    // "Wait for more than 15 ms after VCC rises to 4.5 V."
    // or
    // "Wait for more than 40 ms after VCC rises to 2.7 V."

    Delay15ms();

    // Value to set the LCD interface to 8 bits, irrespective of the real board
    // interface length.
    SetDataPortValue( 0x30 );

    SetDataPortAsOutputs();

    // The command to change the interface to 8 bits must be issued 3 times.

    // Issue the command (first time).
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Low half of the trasfer cycle.
    SetEAsDisabled();

    // Delay mandated by the data-sheet

    // "Wait for more than 4.1 ms."
    Delay4100us();

    // Issue the command (second time).
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Low half of the trasfer cycle.
    SetEAsDisabled();

    // Delay mandated by the data-sheet

    // "Wait for more than 100us."
    Delay100us();

    // Issue the command (third time).
    SetEAsEnabled();
    // Wait for the minimum 'tHIGH'.
    Delay500ns();
    // Low half of the trasfer cycle.
    SetEAsDisabled();

#if         !defined USE_READ_PIN
    // Wait for the execution time.
    Delay37us();
#endif  //  !defined USE_READ_PIN

    // Set the correct interface length
#if         defined USE_FOUR_BIT_INTERFACE

    // "Function set (Set interface to be 4 bits long.) Interface is 8 bits in length."
    LCDWriteCmd( 0x28 );

#else   //  defined USE_FOUR_BIT_INTERFACE

    // "Function set (Interface is 8 bits long.)"
    LCDWriteCmd( 0x38 );

#endif  //  defined USE_FOUR_BIT_INTERFACE

    // Turn display on
    LCDWriteCmd( 0x0c );
    // Clear display
    LCDWriteCmd( CMD_CLEAR_DISPLAY );
    }
//==============================================================================