/*----------------------------------------------------------------------------
**
**	Display.c 
**	
** Control of LCD display. 
** This display is only fitted during development/debug, so diagnostic 
** messages can be displayed on it. It is driven through the serial-parallel 
** shift regsiter to minimise the number of I/O ports required by the micro, 
** since execution speed is unimportant in this module.
**
** The display is a 8 character x 2 line display, using the Samsung KS0066
** LCD driver chip (refer to datasheet), allowing a total of 16 characters
** to be displayed.
**
**--------------------------------------------------------------------------*/
                                                                               
#include "ShiftReg.h"

#define SIZE_OF_MESSAGE_STORE   24

typedef enum
{
    DS_IDLE,
    DS_SENDING
} TDisplayState;
                                                 
// Store of characters/commands to send.
static short DisplayStore[SIZE_OF_MESSAGE_STORE];

// Index into DisplayStore.
static char  DisplayIdx=0;                     

// State machine variable.
static TDisplayState DisplayState = DS_IDLE;   

// Command codes embedded in DisplayStore string
#define DATA            0x0100
#define COMMAND         0x0200
#define LONG_WAIT       0x0400          
#define END_OF_MESSAGE  0x0800

// LCD Commands
#define DISPLAY_CLEAR   0x01
#define RETURN_HOME     0x02
#define ENTRY_MODE      0x04
#define DISP_ON_OFF     0x08
#define SHIFT           0x10
#define SET_FUNCTION    0x20
#define SET_CG_ADDRESS  0x40
#define SET_DD_ADDRESS  0x80
             
// LCD Command parameters
#define CURSOR_INCREASE     0x02
#define CURSOR_DECREASE     0x00
#define DISPLAY_ON          0x04
#define DISPLAY_OFF         0x00
#define INTERFACE_8BIT      0x10
#define INTERFACE_4BIT      0x00
#define TWO_LINES           0x08
#define ONE_LINE            0x00

/*----------------------------------------------------------------------------
//
//  WriteLcd
//      
// Writes to the display.
//  
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void WriteLcd(unsigned char Data, char Type)
{       
    if (Type == COMMAND)
        ChainSetBit(SR_LCD_RS, 0);
    else
        ChainSetBit(SR_LCD_RS, 1);
    ChainSetBitNow(SR_LCD_RW, 0);
    ChainSetBitNow(SR_LCD_E, 1);
    ChainSetByteNow(SR_LCD_DATA, Data);
    ChainSetBitNow(SR_LCD_E, 0);
    ChainSetBit(SR_LCD_RS, 1);
    ChainSetBitNow(SR_LCD_RW, 1);
}


/*----------------------------------------------------------------------------
//
//  InitialiseDisplay
//      
// Sets up a command sequence to initialise the display, and starts the
// state machine to send it. 
//  
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void InitialiseDisplay(void)
{                       
    DisplayStore[0] = COMMAND | DISPLAY_CLEAR;
    DisplayStore[1] = LONG_WAIT;
    DisplayStore[2] = COMMAND | DISP_ON_OFF | DISPLAY_ON;
    DisplayStore[3] = COMMAND | ENTRY_MODE | CURSOR_INCREASE;
    DisplayStore[4] = COMMAND | SET_FUNCTION | INTERFACE_8BIT | TWO_LINES;
    DisplayStore[5] = COMMAND | SET_DD_ADDRESS | 0x00;
    DisplayStore[6] = END_OF_MESSAGE;
    DisplayState = DS_SENDING;
}


/*----------------------------------------------------------------------------
//
//  Display
//      
// Writes message to be displayed sandwiched around appropriate commands, and 
// starts the state machine.
//  
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void Display(char *Message)
{      
    int i = 2;
                       
    // Copy message to display area
    DisplayStore[0] = COMMAND | RETURN_HOME;
    DisplayStore[1] = LONG_WAIT;
    do
    {
        DisplayStore[i++] = DATA | (short)(*Message++);
    } while (i<SIZE_OF_MESSAGE_STORE-1  &&  *Message != 0);
    DisplayStore[i] = END_OF_MESSAGE; 
    
    DisplayIdx = 0;
    DisplayState = DS_SENDING;
}


/*----------------------------------------------------------------------------
//
//  ProcessDisplay
//      
// If the display state is set to sending, sends the next command or character
// to the display. This means each character takes 10ms, so a full display
// could take up to 0.36 seconds to display. That doesn't really matter. This
// way it reduces the processing load in the 10ms cycle time, and this is
// only for debug anyway.
//  
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void ProcessDisplay(void)
{                                   
    short Next;
    
    if (DisplayState == DS_SENDING)
    {    
        Next = DisplayStore[DisplayIdx++];
        
        if (Next & END_OF_MESSAGE)
        {
            DisplayIdx = 0;
            DisplayState = DS_IDLE;
        }

        else if (Next & COMMAND)
            WriteLcd(Next & 0x00FF, COMMAND);
            
        else if (Next  &  DATA)
            WriteLcd(Next & 0x00FF, DATA);
    }
}