/*
 * my_LCD.c
 *
 *  Created on: gg/mmm/ccaa
 *      Author: cognome.nome
 *
 * gestione dello schermo
 *
 */

//==============================================================================
// Include files

#include "string.h"
#include "stdbool.h"
#include "stdint.h"

#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"

#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/rom.h"
#include "driverlib/pin_map.h"

#include "main.h"
#include "drivers/my_timer.h"
#include "drivers/my_GPIO.h"
#include "drivers/my_LCD.h"

//==============================================================================
// Constants

// N.B. invertiti perchè c'è una porta NOT
#define WRITE_1_LCD				0x00
#define WRITE_0_LCD		    	0xFF

#define	one						0x80
#define	two						0xC0
#define	three					0x94
#define	four					0xD4

#define DISPLAY_CLEAR_INS		0x01
#define CURSOR_HOME_INS	    	0x02
#define DISPLAY_ON_INS	    	0x0C
#define DISPLAY_OFF_INS	    	0x08
#define CURSOR_ON_INS			0x0E
#define CURSOR_OFF_INS      	0x0C
#define CURSOR_BLINK_ON_INS		0x0F
#define CURSOR_BLINK_OFF_INS    0x0E
#define CURSOR_SHIFT_LEFT       0x10
#define CURSOR_SHIFT_RIGHT      0x14

//==============================================================================
// Types

typedef struct
{
	unsigned char is_write;
	union
	{
		struct
		{
			unsigned char BIT_0	    :  1;
			unsigned char BIT_1	    :  1;
			unsigned char BIT_2	    :  1;
			unsigned char BIT_3	    :  1;
			unsigned char BIT_4	    :  1;
			unsigned char BIT_5	    :  1;
			unsigned char BIT_6	    :  1;
			unsigned char BIT_7	    :  1;
		};
		unsigned char dataAll;
	};
}TLCDData;

typedef struct
{
	union
	{
		struct
		{
			unsigned char PF_DUMMY_0   	:  1;
			unsigned char PF_DUMMY_1   	:  1;
			unsigned char PF_DUMMY_2   	:  1;
			unsigned char PF_DUMMY_3   	:  1;
			unsigned char PF_DUMMY_4   	:  1;
			unsigned char PF_DUMMY_5   	:  1;
			unsigned char PF_DUMMY_6   	:  1;
			unsigned char PF_7  	 	:  1;
		};
		struct
		{
			unsigned char PS_DUMMY_0   	:  1;
			unsigned char PS_DUMMY_1   	:  1;
			unsigned char PS_DUMMY_2   	:  1;
			unsigned char PS_DUMMY_3   	:  1;
			unsigned char PS_4	    	:  1;
			unsigned char PS_5	    	:  1;
			unsigned char PS_DUMMY_6   	:  1;
			unsigned char PS_DUMMY_7   	:  1;
		};
		struct
		{
			unsigned char PR_DUMMY_0   	:  1;
			unsigned char PR_DUMMY_1   	:  1;
			unsigned char PR_DUMMY_2   	:  1;
			unsigned char PR_3 		  	:  1;
			unsigned char PR_4	    	:  1;
			unsigned char PR_5	    	:  1;
			unsigned char PR_6		   	:  1;
			unsigned char PR_7   		:  1;
		};
		unsigned char dataAll;
	};
}TLCDDataBus;

//==============================================================================
// Static global variables

static TLCDDataBus LCDDataBus;
static TLCDData LCDData;

//==============================================================================
// Static functions

// EN - RS - RW sono invertiti perchè c'è una porta not
static inline void Set_EN(unsigned long value)
{
	ROM_GPIOPinWrite(GPIO_PORTJ_BASE, GPIO_PIN_6, value);
}
static inline void Set_RS(unsigned long value)
{
	ROM_GPIOPinWrite(GPIO_PORTR_BASE, GPIO_PIN_1, value);
}
static inline void Set_RW(unsigned long value)
{
	ROM_GPIOPinWrite(GPIO_PORTR_BASE, GPIO_PIN_2, value);
}
static inline void Set_BACKLIGHT(unsigned long value)
{
	ROM_GPIOPinWrite(GPIO_PORTR_BASE, GPIO_PIN_0, value);
}

static long LCD_Set_Data_Pin_Write(void)
{
	// DATA_2
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_7);
	// DATA_0 - DATA_1 - DATA_3 - DATA_4 - DATA_5
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTR_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3);
	// DATA_6 - DATA_7
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTS_BASE, GPIO_PIN_5 | GPIO_PIN_4);

	return 0;
}

static long LCD_Write_Data(unsigned char data)
{
	LCD_Set_Data_Pin_Write();

	LCDData.dataAll = data;

	memset(&LCDDataBus, 0, sizeof(TLCDDataBus));
	LCDDataBus.PF_7 = LCDData.BIT_2;
	ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_7, LCDDataBus.dataAll);

	memset(&LCDDataBus, 0, sizeof(TLCDDataBus));
	LCDDataBus.PS_4 = LCDData.BIT_6;
	LCDDataBus.PS_5 = LCDData.BIT_7;
	ROM_GPIOPinWrite(GPIO_PORTS_BASE, GPIO_PIN_5 | GPIO_PIN_4, LCDDataBus.dataAll);

	memset(&LCDDataBus, 0, sizeof(TLCDDataBus));
	LCDDataBus.PR_3 = LCDData.BIT_3;
	LCDDataBus.PR_4 = LCDData.BIT_0;
	LCDDataBus.PR_5 = LCDData.BIT_1;
	LCDDataBus.PR_6 = LCDData.BIT_4;
	LCDDataBus.PR_7 = LCDData.BIT_5;
	ROM_GPIOPinWrite(GPIO_PORTR_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3,
			         LCDDataBus.dataAll);

	return 0;
}

/*
void WriteIns(char instruction)
{
	 RS = 0;
	 RW = 0;
	 Data_BUS = instruction;
	 Enable = 1;            //1us
	 _nop_();		//1us
	 Enable = 0;		//1us
	 CheckBusy();
}
*/

static void WriteIns(char instruction)
{
	Set_RS(WRITE_0_LCD);
	Set_RW(WRITE_0_LCD);

	//-----------------------------------
	LCD_Write_Data(instruction);
	Set_EN(WRITE_1_LCD);
	ROM_SysCtlDelay(MICRO_DELAY);
	Set_EN(WRITE_0_LCD);
	ROM_SysCtlDelay(MICRO_DELAY);
	//-----------------------------------

	Set_RS(WRITE_1_LCD);
	Set_RW(WRITE_1_LCD);

	ROM_SysCtlDelay(MILLI_DELAY);
	//CheckBusy();
}

/*
void WriteData(char data1)
{
	 RS = 1;
	 RW = 0;
	 Data_BUS = data1;
	 Enable = 1;
	 _nop_();
	 Enable = 0;
	 CheckBusy();
}
*/

static void WriteData(char data1)
{
	Set_RS(WRITE_1_LCD);
	Set_RW(WRITE_0_LCD);

	//-----------------------------------
	LCD_Write_Data(data1);
	Set_EN(WRITE_1_LCD);
	ROM_SysCtlDelay(MICRO_DELAY);
	Set_EN(WRITE_0_LCD);

	//-----------------------------------

	Set_RS(WRITE_1_LCD);
	Set_RW(WRITE_1_LCD);

	ROM_SysCtlDelay(100*MICRO_DELAY);
	//CheckBusy();
}

static void Initial_ks0066(void)
{
	ROM_SysCtlDelay(100*MILLI_DELAY);
	WriteIns(0x38);
	ROM_SysCtlDelay(100*MILLI_DELAY);
	WriteIns(0x38);
	ROM_SysCtlDelay(100*MILLI_DELAY);
	WriteIns(0x38);
	ROM_SysCtlDelay(100*MILLI_DELAY);

	WriteIns(0x38);
	WriteIns(0x08);
	WriteIns(0x01);
	WriteIns(0x06);
	WriteIns(0x0C); // Display ON/OFF control
}

//==============================================================================
// Global variables

char MSG[20]  = "";

//==============================================================================
// Global functions

// Configurazione GPIO iniziale
long LCD_Init(void)
{

	memset(&LCDDataBus,0,sizeof(TLCDDataBus));
	memset(&LCDData,0,sizeof(TLCDData));

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOR);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOS);

    // EN
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTJ_BASE, GPIO_PIN_6);
	// RS
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTR_BASE, GPIO_PIN_1);
	// RW
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTR_BASE, GPIO_PIN_2);
	// BACKLIGHT
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTR_BASE, GPIO_PIN_0);

	Set_BACKLIGHT(WRITE_1);

	Set_RS(WRITE_1_LCD);
	Set_RW(WRITE_1_LCD);
	Set_EN(WRITE_0_LCD);

	Initial_ks0066();

	return 0;
}

long LCD_WriteChar(unsigned long line, unsigned long position, char ch)
{
	LCD_Cursor_To_Position(line, position);
	WriteData(ch);
	LCD_Cursor_Shift_Left();
	return 0;
}

long LCD_WriteLine(unsigned long line, char *MSG)
{
	unsigned long i;
	//unsigned long j;
	size_t StringLength = strlen(MSG);

	switch(line)
	{
		case 1:
			WriteIns(one);
			break;
		case 2:
			WriteIns(two);
			break;
		case 3:
			WriteIns(three);
			break;
		case 4:
			WriteIns(four);
			break;
		default:
			WriteIns(one);
	}

	for( i = 0 ; (i < StringLength) && (i < 20) ; i++ )
	{
		WriteData(MSG[i]);
	}
	for( ; i < 20 ; i++ )
	{
		WriteData(' ');
	}

	LCD_Cursor_Shift_Left();
	return 0;
}

long LCD_Display_Clear(void)
{
	WriteIns(DISPLAY_CLEAR_INS);
	LCD_Cursor_Home();
	return 0;
}

long LCD_Cursor_Home(void)
{
	WriteIns(CURSOR_HOME_INS);
	return 0;
}

long LCD_Display_ON(void)
{
	WriteIns(DISPLAY_ON_INS);
	return 0;
}

long LCD_Display_OFF(void)
{
	WriteIns(DISPLAY_OFF_INS);
	return 0;
}

long LCD_Cursor_ON(void)
{
	WriteIns(CURSOR_ON_INS);
	return 0;
}

long LCD_Cursor_OFF(void)
{
	WriteIns(CURSOR_OFF_INS);
	return 0;
}

long LCD_Cursor_Blink_ON(void)
{
	WriteIns(CURSOR_BLINK_ON_INS);
	return 0;
}

long LCD_Cursor_Blink_OFF(void)
{
	WriteIns(CURSOR_BLINK_OFF_INS);
	return 0;
}

long LCD_Cursor_Shift_Left(void)
{
	WriteIns(CURSOR_SHIFT_LEFT);
	return 0;
}

long LCD_Cursor_Shift_Right(void)
{
	WriteIns(CURSOR_SHIFT_RIGHT);
	return 0;
}

long LCD_Cursor_To_Position(unsigned long line, unsigned long position)
{
	unsigned long i;

	switch(line)
	{
		case 1:
			WriteIns(one);
			break;

		case 2:
			WriteIns(two);
			break;

		case 3:
			WriteIns(three);
			break;

		case 4:
			WriteIns(four);
			break;

		default:
			WriteIns(one);
	}

	if( (position < 1) || (position > 20) )
	{
		position = 1;
	}
	position -= 1;

	for( i = 0 ; i < position ; i++ )
	{
		LCD_Cursor_Shift_Right();
	}

	return 0;
}

/*
static long LCD_Read_Data(unsigned char *data)
{
	unsigned long temp = 0;

	LCD_Set_Data_Pin_Read();

	temp = ROM_GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_7);
	LCDDataBus.dataAll = temp;
	LCDData.BIT_2 = LCDDataBus.PF_7;

	temp = ROM_GPIOPinRead(GPIO_PORTS_BASE, GPIO_PIN_5 | GPIO_PIN_4);
	LCDDataBus.dataAll = temp;
	LCDData.BIT_6 = LCDDataBus.PS_4;
	LCDData.BIT_7 = LCDDataBus.PS_5;

	temp = ROM_GPIOPinRead(GPIO_PORTR_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3);
	LCDDataBus.dataAll = temp;
	LCDData.BIT_3 = LCDDataBus.PR_3;
	LCDData.BIT_0 = LCDDataBus.PR_4;
	LCDData.BIT_1 = LCDDataBus.PR_5;
	LCDData.BIT_4 = LCDDataBus.PR_6;
	LCDData.BIT_5 = LCDDataBus.PR_7;

	*data = LCDData.dataAll;

	return 0;
}
*/

/*
void CheckBusy(void)
{
	Data_BUS = 0xff; //°T¸¹¥ÑhighÅÜ¬°low¤ñ¸û®e©ö,©Ò¥H¥þ³¡³]¬°high.
	RS = 0;
	RW = 1;
	do
	{
	  Enable = 1;
	  busy_f = busy;
	  Enable = 0;
	 }while(busy_f);
}
*/

/*
volatile unsigned long counter_busy = 0;
volatile unsigned long busy = 0;
void CheckBusy(void)
{
	LCD_Set_Data_Pin_Read();

	Set_RS(WRITE_0_LCD);
	Set_RW(WRITE_1_LCD);

	do
	{
	  counter_busy++;
      Set_EN(WRITE_1_LCD);
	  ROM_SysCtlDelay(MICRO_DELAY);
      Set_EN(WRITE_0_LCD);
      busy = ROM_GPIOPinRead(GPIO_PORTS_BASE, GPIO_PIN_5) >> 5;
	  ROM_SysCtlDelay(MICRO_DELAY);
	}while(busy);

	Set_RS(WRITE_1_LCD);
	Set_RW(WRITE_1_LCD);

}

static long LCD_Set_Data_Pin_Read(void)
{
	// DATA_2
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_7);
	// DATA_0 - DATA_1 - DATA_3 - DATA_4 - DATA_5
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTR_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3);
	// DATA_6 - DATA_7
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTS_BASE, GPIO_PIN_5 | GPIO_PIN_4);

	return 0;
}

*/
