/********************************************************************
	lcd_demo.c			 
	Z-World, 2000			 

	The is a sample program for connecting to an LCD (which uses the
	HD44780 controller, or an equivalent), using the Rabbit 2000
	external I/O rabbit port.

	This file contains information for a 4 bit interface.  The 4 bit
	interface uses one of the parallel I/O.

This is the initialization sequence for a 2 by 20 character LCD
programmed in 4 bit mode (chosen in order to reduce the number of
I/O pins):
	delay for power up - allow LCD to reset
	command: 4 bit mode
	command: 2 lines, 5x10 character size, and 4 bit mode again
	command: turn on display and cursor - non blinking
	command: incr address and shift cursor with each character
Note: each command must be followed by a delay to allow the display
to respond.

Connection list: 
Jackrabbit Pin LCD Signal
	PA0 J3-3		DB4
	PA1 J3-4		DB5
	PA2 J3-5		DB6
	PA3 J3-6		DB7

	PA6 J3-9		RS		Register Select: 0=command, 1=data
	PA7 J3-10	E		normally low: latches on high to low transition

DB0 - DB3 of the LCD are grounded as is the R/W pin.  Since we are
using 4 bit mode DB0 - DB3 are not used by the controller.  We will
only be writing so we ground the R/W pin

Since Port A is used to control the LEDs on the expansion board, they
will be controlled by the data values which you program.

VEE is used to control brightness - the simplest thing to do is ground
it. If you need to control the brightness you can connect a
potentiometer between ground and -5V with the arm going to the VEE
pin. Check the specs on the LCD before doing this as some may require
a different connection.  VCC is +5 and VSS is ground. 
********************************************************************/


/********************************************************************
msDelay - delay some number of milliseconds
	input parameter: int number of milliseconds to delay
	return value: none
	errors: none
********************************************************************/
nodebug
void msDelay(unsigned int delay)
{
	auto unsigned long done_time;
	
	done_time = MS_TIMER + delay;
   while( (long) (MS_TIMER - done_time) < 0 );
} 

/*******************************************************************
UsDelay - delay some number of micro seconds - very approximate!!
	about 11usec per iteration with the 7.3MHz crystal.  The formula
	used here was determined experimentally.
********************************************************************/
void UsDelay ( int iDelay )
{	int i;
	iDelay /= 11;
	for ( i=0; i<iDelay; i++ );
}

/*************************** 4 bit functions ***********************/
#define DATA		0x40
#define COMMAND	0x00
int iDataFlag;

/*******************************************************************
WriteNibble - send a single nibble to the LCD
********************************************************************/
void WriteNibble ( char cNib )
{	cNib &= 0x0F;								// remove upper 4 bits
	cNib |= iDataFlag;						// insert data flag for RS
	WrPortI ( PADR, NULL, cNib|0x80 );	// assert E
	WrPortI ( PADR, NULL, cNib );			// remove E
}

/*******************************************************************
				for those who prefer assembly code.........
#asm
WriteNibble::
; character is in HL
			ld		a, L			; get the character
			and	0x0F			; insure upper nibble is zero
			ld		hl, iDataFlag ; get address of data flag
			or		(hl)			; insert Data/Command bit
			or		0x80			; assert E
	ioi	ld		(PADR),a		; ship data value and E
			and	0x7F			; remove E
	ioi	ld		(PADR),a		; ship data value without E
			ret
#endasm
*******************************************************************/

/******************************************************************
WriteByte - send two nibbles to the LCD, upper nibble first
*******************************************************************/
void WriteByte ( char cNib )
{	int i;
	i = cNib;				// temp store
	cNib >>= 4;				// put upper nibble into lower 4 bits
	WriteNibble ( cNib );	// send upper nibble
	WriteNibble ( i );		// send lower nibble
	UsDelay (100);
}

/*********************************************************************
LCD_Init - Initialize the LCD for the following operating parameters:
	4 bit mode, 2 lines, 5x10
	turn on display and cursor: non-blinking
	incr address and shift cursor with each character
*******************************************************************/
void LCD_Init ()
{	WrPortI ( PADR, NULL, 0 );		// write 0 to port A output register
	WrPortI ( SPCR, NULL, 0x84 );	// make port A output
	iDataFlag = COMMAND;				// show command mode
	msDelay ( 1000 );				// wait for LCD to reset itself
	WriteNibble ( 2 );					// 4 bit mode
	UsDelay (100);
	WriteByte ( '\B00101100' );		// 4 bit mode, 2 lines, 5x10
	WriteByte ( '\B00001110' );		// turn on display and cursor: non-blinkin
	WriteByte ( '\B00000110' );		// incr address and shift cursor with each character
}

/*********************************************************************
Display - display a line of text on the LCD
	argument: address of null terminated text string
*******************************************************************/
void Display ( char *szp )
{	iDataFlag = DATA;					// next bytes are data
	while ( *szp ) WriteByte ( *szp++ );
}

void Clear ( void )
{	iDataFlag = COMMAND;				// set up for command
	WriteByte ( 0x01 );					// clear the display
	msDelay (3);						// insure at least 2 msec
}

void Line2 ( void )
{	iDataFlag = COMMAND;				// set up for command
	WriteByte ( 0xC0 );					// set RAM address to Line 2
					//note: this value may vary for different displays
	msDelay (3);						// insure at least 2 msec
}


void main ()
{

	LCD_Init ();
		// IMPORTANT - the initialization sequence should only
		// be executed once after powerup.  Multiple executions
		// will cause the display to malfunction.
	
	while (1) {
		Clear();
		Display ( "0123456789" );
		msDelay (500);
		Clear();
		Display ( "This is a test!" );
		msDelay (500);

		Line2 ();
		Display ( "This is line TWO!" );
		msDelay (500);
		}
}
