/*
	LCD routines by Adam Davis
	January 2000

	This specifies a 4-bit interface to any intelligent LCD
	based on the HD44780 text LCD controller.
	The LCD is connected as follows:

	LCD	PIC
	Data4	PORTA:0
	Data5	PORTA:1
	Data6	PORTA:2
	Data7	PORTA:3
	RS	PORTA:4
	EN	PORTA:5
	RW	Not used in this example,
		The LCD is always written to.
		Tie to Ground (low)

	I have written routines for reading from the LCD, but
	they did not work as expected, and I did not need that
	functionality, so they are not included here.  It is a
	simple matter to change the writing routine into a 
	reading routine, and connect the RW wire to another
	PIC pin.

	If you want to change the port or bits used, you'll need 
	to change some pragma statements below, as well as the
	functions write4() and main().

	After initializing the LCD, you can select the register
	(data or instruction) by setting RS to 1 or 0.  Then
	use write8(byte) to write a byte to the LCD.
*/

#include "c:\progra~1\ccs\16c66.h"
#define CP_off  |= 0x3F30	// This CP_off is specific to the 16c66

#pragma config CP_off,PWRTE=on,WDTE=off,FOSC=HS,BODEN=on,ID=0x0001

#pragma bit RS @ PORTA.4
#pragma bit EN @ PORTA.5

char initmsg(char number);
void delayms(uns8 ms);
void write4(uns8  nyble);
void write8(uns8 byte);
void initlcd(void);

char initmsg(char number)
{
	skip(number);
	#pragma return[16] = "Adam's LCD Test!"	// This is the text which will be displayed
}

void delayms(uns8 ms)
{
	// Delay ms Milliseconds (1/1000 of a second)
	// Can delay from 1 to 255 ms
	// This is NOT an exact delay routine!  
	// The inner loop is about 8 instructions, 
	// so the entire delay is a few uS over time.

	uns8 tempcount;			// Temporary register
	while(ms != 0)			// If ms still has time left, delay another ms
	{
		tempcount = 125;	// This loop is about 8 instructions
		while(tempcount != 0)	// 125 loops is 1000 instructions
			tempcount--;	// Run at 4MHz is 1ms
		ms--;			// Decrement ms
	}
	
	return;		// Return nothing
}

void write4(uns8  nyble)
{
	uns8 temp;
	nyble &= 0xf;		// Prepare to send the nibble (4 bits) by getting rid of the top four
	temp = PORTA & 0xf0;	// Store the current state of bits 7-4 of PORTA in temp
	temp |= nyble;		// OR the PORTA state and nibble to be sent together
	PORTA = temp;		// Write 7-4 of PORTA and 3-0 of nyble to PORTA

	EN=1;	// Bring enable high
	nop();	// Wait a teensy bit
	EN=0; 	// Bring Enable Low

	return;	// Return nothing
}

void write8(uns8 byte)
{
	uns8 nibble;
	nibble = (byte & 0xf0) >> 4;	// Rotate the high 4 bits (7-4) of byte into bits (3-0) of nibble
	write4(nibble);			// Write the high 4 bits to the LCD
	nibble = byte & 0xf;		// Copy the low four bits of byte into the low four bits of nibble
	write4(nibble);			// Write the low 4 bits to the LCD
}

void initlcd(void)
{
	delayms(20);	// Wait for LCD to power up ( >15ms )
	RS=0;		// Set RS low for instruction 
	write4(3);	// Set interface to 8 bits 
	delayms(5);	// Wait for LCD execute instruction ( >4.1ms )
	write4(3);	// Set interface to 8 bits 
	delayms(1);	// Wait for LCD execute instruction ( >100us )
	write4(3);	// Set interface to 8 bits 
	delayms(5);  	// Wait for LCD execute instruction (At this point 
			// we could actually start using the busy flag) 
	write4(2);	// Set the display to 4 bit interface 
	delayms(5);	// Wait for LCD execute instruction 
	write8(0x28);	// Set the display to two line and ???
	delayms(5);	// Wait for LCD execute instruction 
	write8(6);	// ???
	delayms(5);	// Wait for LCD execute instruction 
	write8(1);	// Clear the LCD
	delayms(5);	// Wait for LCD execute instruction
	write8(0xf);	// ???
	delayms(5);	// Wait for LCD execute instruction
	return;
}

void main(void)
{
	uns8 x;		// x is a temporary variable, unsigned byte (0-255)

	PORTA = 0b.0000.0000; 		// All ports low at start
	PORTB = 0b.0000.0000;
	PORTC = 0b.0000.0000;

	TRISA = 0b.1100.0000; 		// 7:6 Input(1), 5:0 Output(0) 
	TRISB = 0b.1111.1111; 		// 7:0 Input
	TRISC = 0b.1111.1111; 		// 7:0 Input
	
	initlcd(); 			// Initialize LCD
	
	while(1)
	{
		// This loop will 'blink' the init message by writing 
		// it to the LCD and clearing it twice a second

		delayms(250);			// Delay for 1/4 of a second
		RS=1;				// Select the data register
		for(x=0;x < 16;x++)		// Initmsg has 16 characters, send each one in turn
			write8(initmsg(x));	// Write the initmsg on the screen
		delayms(250);			// Delay for 1/4 of a second
		RS=0;				// Select the Instruction Register
		write8(0x01);			// Clear LCD and move to position one, line one
	}
}

