#include <p24FJ64GA002.h>

#include "C_LCD.h"
#include "delay.h"

unsigned char ddram_address[4] = {
	0x00,
	0x40,
	0x14,
	0x54
};


void c_lcd_init() {

	_C_LCD_ENABLE			= 0;
	_C_LCD_READ_WRITE 		= _C_LCD_WRITE;
	_C_LCD_REGISTER_SELECT = _C_LCD_DSPDATA;

	delay_ms(20);

	_C_LCD_INTERFACE_PORT = (_C_LCD_FUNCTION_8BIT) << 8 ;
	_C_LCD_REGISTER_SELECT = _C_LCD_COMMAND;
	_C_LCD_READ_WRITE 		= _C_LCD_WRITE;
	c_lcd_strobe();
	delay_ms(5);

	_C_LCD_INTERFACE_PORT = (_C_LCD_FUNCTION_8BIT) << 8 ;
	_C_LCD_REGISTER_SELECT = _C_LCD_COMMAND;
	_C_LCD_READ_WRITE 		= _C_LCD_WRITE;
	c_lcd_strobe();
	delay_ms(1);

	_C_LCD_INTERFACE_PORT = (_C_LCD_FUNCTION_8BIT) << 8 ;
	_C_LCD_REGISTER_SELECT = _C_LCD_COMMAND;
	_C_LCD_READ_WRITE 		= _C_LCD_WRITE;
	c_lcd_strobe();
	delay_ms(1);

	_C_LCD_INTERFACE_PORT = (_C_LCD_FUNCTION_4BIT) << 8 ;
	_C_LCD_REGISTER_SELECT = _C_LCD_COMMAND;
	_C_LCD_READ_WRITE 		= _C_LCD_WRITE;
	c_lcd_strobe();
	delay_ms(1);

	c_lcd_out( _C_LCD_COMMAND, _C_LCD_FUNCTION_4BIT | _C_LCD_FUNCTION_2LINE );
	c_lcd_out( _C_LCD_COMMAND, _C_LCD_DISPLAY_OFF );
	c_lcd_out( _C_LCD_COMMAND, _C_LCD_CLEAR_SCREEN );
	c_lcd_out( _C_LCD_COMMAND, _C_LCD_ENTRY_MODE_DIRECT_LEFT | _C_LCD_ENTRY_MODE_SHIFT_OFF );
	c_lcd_out( _C_LCD_COMMAND, _C_LCD_CURSOR_HOME );
	c_lcd_out( _C_LCD_COMMAND, _C_LCD_DISPLAY_ON );

	return;

}

void c_lcd_putc(unsigned short data) {

	unsigned short mode =	_C_LCD_DSPDATA;
	data &= 0xff;
	c_lcd_wait();

	_C_LCD_INTERFACE_PORT  = 
		( _C_LCD_INTERFACE_LAT & 0x0FFF ) | (( data & 0xf0) << 8);
	_C_LCD_READ_WRITE = _C_LCD_WRITE;
	_C_LCD_REGISTER_SELECT = mode;
	c_lcd_strobe();

	_C_LCD_INTERFACE_PORT  =
		( _C_LCD_INTERFACE_LAT & 0x0FFF ) | ((data & 0x0f) << 12);

	_C_LCD_READ_WRITE = _C_LCD_WRITE;
	_C_LCD_REGISTER_SELECT = mode;
	c_lcd_strobe();

	return;

}


void c_lcd_setpos(unsigned short x, unsigned short y) {

	if ( (x > (_C_LCD_CHAR_PER_LINE - 1)) || (y > (_C_LCD_NUMBER_OF_LINE - 1)) ) {
		return;
	}

	c_lcd_out( _C_LCD_COMMAND, _C_LCD_DDRAM_ADDRESS_SET | (ddram_address[y] + x) );

	return;
}


void c_lcd_line(unsigned short line, unsigned char *string) {

	short i;

	if (line > (_C_LCD_NUMBER_OF_LINE - 1) ) {
		return;
	}
	c_lcd_out( _C_LCD_COMMAND, _C_LCD_DDRAM_ADDRESS_SET | ddram_address[line] );
	i = 0;
	while( i < _C_LCD_CHAR_PER_LINE ) {
		if (!*string) {
			break;
		}
		c_lcd_out( _C_LCD_DSPDATA, *string );
		string++;
		i++;
	}
	
	return;

}


void c_lcd_out(unsigned short mode, unsigned short cmd) {

	cmd &= 0xff;
	mode &= 0x01;
	c_lcd_wait();

	_C_LCD_INTERFACE_PORT  = 
		( _C_LCD_INTERFACE_LAT & 0x0FFF ) | ((cmd & 0xf0) << 8);
	_C_LCD_READ_WRITE = _C_LCD_WRITE;
	_C_LCD_REGISTER_SELECT = mode;
	c_lcd_strobe();

	_C_LCD_INTERFACE_PORT  =
		( _C_LCD_INTERFACE_LAT & 0x0FFF ) | ((cmd & 0x0f) << 12);

	_C_LCD_READ_WRITE = _C_LCD_WRITE;
	_C_LCD_REGISTER_SELECT = mode;
	c_lcd_strobe();

	return;

}

void c_lcd_wait(void) {

	volatile unsigned short reg = 0x8000;

	TRISB = TRISB | 0x8000;
	_C_LCD_READ_WRITE = _C_LCD_READ;
	_C_LCD_REGISTER_SELECT = _C_LCD_COMMAND;

	while( reg & 0x8000 ) {
		_C_LCD_ENABLE = 1;
		delay_us(1);
		reg = _C_LCD_INTERFACE_PORT;
		_C_LCD_ENABLE = 0;
		delay_us(1);
		_C_LCD_ENABLE = 1;
		delay_us(1);
		_C_LCD_ENABLE = 0;
		delay_us(1);
	}

	_C_LCD_READ_WRITE = _C_LCD_WRITE;
	_C_LCD_REGISTER_SELECT = _C_LCD_COMMAND;

	TRISB = TRISB & 0x7FFF;
	
}

void c_lcd_strobe(void) {

		_C_LCD_ENABLE = 0;
		delay_us(1);
		_C_LCD_ENABLE = 1;
		delay_us(1);
		_C_LCD_ENABLE = 0;
		delay_us(1);

		return;

}
