/*
 * tca8418_keypad.c
 *
 *  Created on: gg/mmm/ccaa
 *      Author: cognome.nome
 */

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

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

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

#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom_map.h"
#include "driverlib/rom.h"
#include "tca8418_keypad.h"
#include "drivers/tca8418_keypad.h"
#include "main.h"
#include "my_timer.h"
#include "my_GPIO.h"
#include "my_LED.h"

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

#define VALUE_REG_KP_GPIO1      0x1F  // 00011111
#define	VALUE_REG_KP_GPIO2      0x1E  // 00011110
#define	VALUE_REG_KP_GPIO3      0x00  // 00000000

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

typedef struct
{
	union
	{
		struct
		{
			unsigned char CFG_KE_IEN	    :  1;
			unsigned char CFG_GPI_IEN	    :  1;
			unsigned char CFG_K_LCK_IEN	    :  1;
			unsigned char CFG_OVR_FLOW_IEN	:  1;
			unsigned char CFG_INT_CFG		:  1;
			unsigned char CFG_OVR_FLOW_M	:  1;
			unsigned char CFG_GPI_E_CFG	    :  1;
			unsigned char CFG_AI	        :  1;
		};
		unsigned char ConfigBitsAll;
	};
}TConfigBits;

typedef struct
{
	union
	{
		struct
		{
			unsigned char KEY_number	    :  7;
			unsigned char KEY_pressed	    :  1;
		};
		unsigned char KeyEventAll;
	};
}TKeyEvent;

typedef struct
{
	union
	{
		struct
		{
			unsigned char INT_STAT_K_INT	    :  1;
			unsigned char INT_STAT_GPI_INT	    :  1;
			unsigned char INT_STAT_K_LCK_INT	:  1;
			unsigned char INT_STAT_OVR_FLOW_INT	:  1;
			unsigned char INT_STAT_CAD_INT	    :  1;
			unsigned char INT_STAT_DUMMY	    :  3;
		};
		unsigned char InterruptEventAll;
	};
}TInterruptEvent;

typedef enum
{
	RESERVED_8418       = 0x00,
	REG_CFG             = 0x01,
	REG_INT_STAT        = 0x02,
	REG_KEY_LCK_EC      = 0x03,
	REG_KEY_EVENT_A     = 0x04,
	REG_KEY_EVENT_B     = 0x05,
	REG_KEY_EVENT_C     = 0x06,
	REG_KEY_EVENT_D     = 0x07,
	REG_KEY_EVENT_E     = 0x08,
	REG_KEY_EVENT_F     = 0x09,
	REG_KEY_EVENT_G     = 0x0A,
	REG_KEY_EVENT_H     = 0x0B,
	REG_KEY_EVENT_I     = 0x0C,
	REG_KEY_EVENT_J     = 0x0D,
	REG_KP_LCK_TIMER    = 0x0E,
	REG_UNLOCK1         = 0x0F,
	REG_UNLOCK2         = 0x10,
	REG_GPIO_INT_STAT1  = 0x11,
	REG_GPIO_INT_STAT2  = 0x12,
	REG_GPIO_INT_STAT3  = 0x13,
	REG_GPIO_DAT_STAT1  = 0x14,
	REG_GPIO_DAT_STAT2  = 0x15,
	REG_GPIO_DAT_STAT3  = 0x16,
	REG_GPIO_DAT_OUT1   = 0x17,
	REG_GPIO_DAT_OUT2   = 0x18,
	REG_GPIO_DAT_OUT3   = 0x19,
	REG_GPIO_INT_EN1    = 0x1A,
	REG_GPIO_INT_EN2    = 0x1B,
	REG_GPIO_INT_EN3    = 0x1C,
	REG_KP_GPIO1        = 0x1D,
	REG_KP_GPIO2        = 0x1E,
	REG_KP_GPIO3        = 0x1F,
	REG_GPI_EM1         = 0x20,
	REG_GPI_EM2         = 0x21,
	REG_GPI_EM3         = 0x22,
	REG_GPIO_DIR1       = 0x23,
	REG_GPIO_DIR2       = 0x24,
	REG_GPIO_DIR3       = 0x25,
	REG_GPIO_INT_LVL1   = 0x26,
	REG_GPIO_INT_LVL2   = 0x27,
	REG_GPIO_INT_LVL3   = 0x28,
	REG_DEBOUNCE_DIS1   = 0x29,
	REG_DEBOUNCE_DIS2   = 0x2A,
	REG_DEBOUNCE_DIS3   = 0x2B,
	REG_GPIO_PULL1      = 0x2C,
	REG_GPIO_PULL2      = 0x2D,
	REG_GPIO_PULL3      = 0x2E
}ECommand8418;

/*
typedef struct
{
	volatile unsigned long counter_key_press; // contatore tasti premuti
	volatile unsigned long counter_key_release; // contatore tasti rilasciati
	EKeyboard              KeyPressed;     // Tasto premuto
	EKeyboard              KeyPressedPrev; // Tasto premuto precedente
	volatile unsigned long LedLetNum;      // LED lettere numeri
	volatile unsigned long KeyPressNum;    // Contatore pressione tasti
	volatile unsigned long KeyPressMod;    // Contatore pressione tasti in modulo
	volatile unsigned long lettera_ok;     // E' una lettera
	volatile unsigned long lettera_pronta; // Ho una lettera disponibile
	volatile unsigned long tasto_pronto;   // Non è una lettera
 	char                   lettera;

}TManageKeyb;
*/

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

static TConfigBits ConfigBits;
static TKeyEvent KeyEvent;
static TInterruptEvent InterruptEvent;
//static TManageKeyb ManageKeyb;

static volatile unsigned long GPIOJIntHandlerCounter = 0;
static volatile unsigned long Timer4IntCounter = 0;
static volatile unsigned long abil = 0;


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

static void InitInterruptGPIOJ(void)
{
	//----------------------------------------------------
	// PJ5 - interrupt dalla tastiera
	// interrupt sul falling edge del segnale
	//----------------------------------------------------
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTJ_BASE, GPIO_PIN_5);
	GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_5, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); // Push-pull with weak pull-up
    ROM_GPIOIntTypeSet(GPIO_PORTJ_BASE, GPIO_PIN_5, GPIO_FALLING_EDGE);
    ROM_GPIOIntEnable(GPIO_PORTJ_BASE,GPIO_INT_PIN_5);
    ROM_IntEnable(INT_GPIOJ);
	//----------------------------------------------------
}


/* Configurazione iniziale */
static long I2C_init(void)
{

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C4);

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);

    ROM_GPIOPinConfigure(GPIO_PG6_I2C4SCL);
    ROM_GPIOPinConfigure(GPIO_PG7_I2C4SDA);

    ROM_GPIOPinTypeI2CSCL(GPIO_PORTG_BASE, GPIO_PIN_6);
    ROM_GPIOPinTypeI2C(GPIO_PORTG_BASE, GPIO_PIN_7);

    // Enable and initialize the I2C0 master module.  Use the system clock for
    // the I2C0 module.  The last parameter sets the I2C data transfer rate.
    // If false the data rate is set to 100kbps and if true the data rate will
    // be set to 400kbps.  For this example we will use a data rate of 100kbps.
    I2CMasterInitExpClk(I2C4_BASE, g_ui32SysClock, true);

	return 0;
}

static long key_mapping(void)
{
	switch(KeyEvent.KEY_number)
	{
		case 4:
			ManageKeyb.KeyPressed = UNO;
			ManageKeyb.lettera_ok = 1;
			break;

		case 5:
			ManageKeyb.KeyPressed = DUE;
			ManageKeyb.lettera_ok = 1;
			break;
		case 2:
			ManageKeyb.KeyPressed = TRE;
			ManageKeyb.lettera_ok = 1;
			break;

		case 35:
			ManageKeyb.KeyPressed = FRECCIA_SU;
			ManageKeyb.lettera_ok = 0;
			break;

		case 22:
			ManageKeyb.KeyPressed = LET_NUM;
			ManageKeyb.lettera_ok = 0;
			ManageKeyb.LedLetNum = !ManageKeyb.LedLetNum;
			Set_LED_LET_NUM(ManageKeyb.LedLetNum);
			break;

		case 44:
			ManageKeyb.KeyPressed = TASTO_2F;
			ManageKeyb.lettera_ok = 0;
			break;

		case 13:
			ManageKeyb.KeyPressed = QUATTRO;
			ManageKeyb.lettera_ok = 1;
			break;

		case 14:
			ManageKeyb.KeyPressed = CINQUE;
			ManageKeyb.lettera_ok = 1;
			break;

		case 15:
			ManageKeyb.KeyPressed = SEI;
			ManageKeyb.lettera_ok = 1;
			break;

		case 32:
			ManageKeyb.KeyPressed = FRECCIA_GIU;
			ManageKeyb.lettera_ok = 0;
			break;

		case 33:
			ManageKeyb.KeyPressed = CLR;
			ManageKeyb.lettera_ok = 0;
			break;

		case 25:
			ManageKeyb.KeyPressed = TARA;
			ManageKeyb.lettera_ok = 0;
			break;

		case 12:
			ManageKeyb.KeyPressed = SETTE;
			ManageKeyb.lettera_ok = 1;
			break;

		case 23:
			ManageKeyb.KeyPressed = OTTO;
			ManageKeyb.lettera_ok = 1;
			break;

		case 24:
			ManageKeyb.KeyPressed = NOVE;
			ManageKeyb.lettera_ok = 1;
			break;

		case  3:
			ManageKeyb.KeyPressed = ZERO;
			ManageKeyb.lettera_ok = 1;
			break;

		case 34:
			ManageKeyb.KeyPressed = ENTER;
			ManageKeyb.lettera_ok = 0;
			break;

		case 45:
			ManageKeyb.KeyPressed = START;
			ManageKeyb.lettera_ok = 0;
			break;

		case 42:
			ManageKeyb.KeyPressed = STOP;
			ManageKeyb.lettera_ok = 0;
			break;

		default:
			ManageKeyb.KeyPressed = KEY_NONE;
			ManageKeyb.lettera_ok = 0;
			break;
	}

	return 0;
}

static long InitTimer4(void)
{
    // The Timer4 peripheral must be enabled for use.
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER4);

    // Configure 32-bit periodic timer A.
    ROM_TimerConfigure(TIMER4_BASE, TIMER_CFG_PERIODIC);
    ROM_TimerLoadSet(TIMER4_BASE, TIMER_A, g_ui32SysClock/100); // 10 msec

    // Setup the interrupts for the timer timeouts.
    ROM_TimerIntDisable(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
    ROM_IntEnable(INT_TIMER4A);

    // Enable the timers.
    ROM_TimerEnable(TIMER4_BASE, TIMER_A);

	return 0;
}

static long AssignChar0_3(unsigned long is_zero)
{
	switch(ManageKeyb.KeyPressed)
	{
		case UNO:
			ManageKeyb.lettera = ',';
			break;

		case FRECCIA_SU:
			break;

		case LET_NUM:
			break;

		case TASTO_2F:
			break;

		case FRECCIA_GIU:
			break;

		case CLR:
			break;

		case TARA:
			break;

		case ZERO:
			ManageKeyb.lettera = ' ';
			break;

		case ENTER:
			break;

		case START:
			break;

		case STOP:
			break;

		case DUE:
			ManageKeyb.lettera = 'A';
			break;

		case TRE:
			ManageKeyb.lettera = 'D';
			break;

		case QUATTRO:
			ManageKeyb.lettera = 'G';
			break;

		case CINQUE:
			ManageKeyb.lettera = 'J';
			break;

		case SEI:
			ManageKeyb.lettera = 'M';
			break;

		case SETTE:
			if(is_zero)
			{
				ManageKeyb.lettera = 'P';
			}
			else
			{
				ManageKeyb.lettera = 'S';
			}
			break;

		case OTTO:
			ManageKeyb.lettera = 'T';
			break;

		case NOVE:
			if(is_zero)
			{
				ManageKeyb.lettera = 'W';
			}
			else
			{
				ManageKeyb.lettera = 'Z';
			}
			break;
	}
	return 0;
}

static long AssignChar1(void)
{
	switch(ManageKeyb.KeyPressed)
	{
		case UNO:
			ManageKeyb.lettera = '.';
			break;

		case FRECCIA_SU:
			break;

		case LET_NUM:
			break;

		case TASTO_2F:
			break;

		case FRECCIA_GIU:
			break;

		case CLR:
			break;

		case TARA:
			break;

		case ZERO:
			ManageKeyb.lettera = '_';
			break;

		case ENTER:
			break;

		case START:
			break;

		case STOP:
			break;

		case DUE:
			ManageKeyb.lettera = 'B';
			break;

		case TRE:
			ManageKeyb.lettera = 'E';
			break;

		case QUATTRO:
			ManageKeyb.lettera = 'H';
			break;

		case CINQUE:
			ManageKeyb.lettera = 'K';
			break;

		case SEI:
			ManageKeyb.lettera = 'N';
			break;

		case SETTE:
			ManageKeyb.lettera = 'Q';
			break;

		case OTTO:
			ManageKeyb.lettera = 'U';
			break;

		case NOVE:
			ManageKeyb.lettera = 'X';
			break;
	}

	return 0;
}

static long AssignChar2(void)
{
	switch(ManageKeyb.KeyPressed)
	{
		case UNO:
			ManageKeyb.lettera = '?';
			break;

		case FRECCIA_SU:
			break;

		case LET_NUM:
			break;

		case TASTO_2F:
			break;

		case FRECCIA_GIU:
			break;

		case CLR:
			break;

		case TARA:
			break;

		case ZERO:
			ManageKeyb.lettera = '-';
			break;

		case ENTER:
			break;

		case START:
			break;

		case STOP:
			break;

		case DUE:
			ManageKeyb.lettera = 'C';
			break;

		case TRE:
			ManageKeyb.lettera = 'F';
			break;

		case QUATTRO:
			ManageKeyb.lettera = 'I';
			break;

		case CINQUE:
			ManageKeyb.lettera = 'L';
			break;

		case SEI:
			ManageKeyb.lettera = 'O';
			break;

		case SETTE:
			ManageKeyb.lettera = 'R';
			break;

		case OTTO:
			ManageKeyb.lettera = 'V';
			break;

		case NOVE:
			ManageKeyb.lettera = 'Y';
			break;
	}

	return 0;
}


static inline void tca8418_reset(unsigned long value)
{
	ROM_GPIOPinWrite(GPIO_PORTJ_BASE, GPIO_PIN_4, value);
}

static inline void KeybManage_Write_Timer_Enable(void)
{
	if(abil == 0)
	{
		ROM_TimerIntClear(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
		ROM_TimerIntEnable(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
		abil = 1;
	}
}

static inline void KeybManage_Write_Timer_Disable(void)
{
	if(abil == 1)
	{
		ROM_TimerIntDisable(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
		ROM_TimerIntClear(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
		abil = 0;
	}
}

static long tca8418_write_byte(ECommand8418 command, unsigned char data)
{

	// SET DIRECTION AND ADDRESS WRITE
	ROM_I2CMasterSlaveAddrSet(I2C4_BASE, 0x34, false);

	// COMMAND
	ROM_I2CMasterDataPut(I2C4_BASE,command);
	ROM_I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_BURST_SEND_START);
	while(ROM_I2CMasterBusy(I2C4_BASE))
	{
	}

	// DATA
	ROM_I2CMasterDataPut(I2C4_BASE,data);
	ROM_I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
	while(ROM_I2CMasterBusy(I2C4_BASE))
	{
	}

	return 0;
}

static long tca8418_read_byte(ECommand8418 command, unsigned char *data)
{
	// SET DIRECTION AND ADDRESS WRITE
	ROM_I2CMasterSlaveAddrSet(I2C4_BASE, 0x34, false);

	// COMMAND
	ROM_I2CMasterDataPut(I2C4_BASE,command);
	ROM_I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_SINGLE_SEND);
	while(ROM_I2CMasterBusy(I2C4_BASE))
	{
	}

	// SET DIRECTION AND ADDRESS READ
	ROM_I2CMasterSlaveAddrSet(I2C4_BASE, 0x34, true);
	ROM_I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
	while(ROM_I2CMasterBusy(I2C4_BASE))
	{
	}
	// READ DATA
	*data = ROM_I2CMasterDataGet(I2C4_BASE);

	return 0;
}

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

TManageKeyb ManageKeyb;

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

void GPIOJIntHandler(void)
{
	unsigned long ulInts;

    // Get and clear the current interrupt source(s)
    ulInts = ROM_GPIOIntStatus(GPIO_PORTJ_BASE, true);
    ROM_GPIOIntClear(GPIO_PORTJ_BASE, ulInts);

    GPIOJIntHandlerCounter++;

    if(ulInts & GPIO_PIN_5)
    {
    	tca8418_read_byte(REG_INT_STAT, &InterruptEvent.InterruptEventAll);
    	tca8418_read_byte(REG_KEY_EVENT_A, &KeyEvent.KeyEventAll);
    	if(KeyEvent.KEY_pressed)
    	{
    		ManageKeyb.counter_key_press++;
    		key_mapping();
    		if((ManageKeyb.lettera_ok == 1) && (ManageKeyb.LedLetNum == LED_ON))
        	{
    			ManageKeyb.tasto_pronto = 0;
        		ManageKeyb.KeyPressMod = ManageKeyb.KeyPressNum%4;
        		ManageKeyb.KeyPressNum++;
    			if(ManageKeyb.KeyPressedPrev != ManageKeyb.KeyPressed)
    			{
    				ManageKeyb.KeyPressMod = 0;
    				ManageKeyb.KeyPressNum = 1;
    			}
    			KeybManage_Write_Timer_Enable();
        	}
    		else
        	{
    			ManageKeyb.tasto_pronto = 1;
    			ManageKeyb.lettera_pronta = 0;
           	}
    	}
    	else
    	{
    		ManageKeyb.counter_key_release++;
    	}
    	tca8418_write_byte(REG_INT_STAT, 0x01);
    }

}

void Timer4_KeypadManage(void)
{
	static char lettera_precedente = 0;
	static volatile unsigned long counter_lettera = 0;

	ROM_TimerIntClear(TIMER4_BASE, TIMER_TIMA_TIMEOUT);

    Timer4IntCounter++;

    if(ManageKeyb.lettera_pronta == 0)
    {
    	switch(ManageKeyb.KeyPressMod)
		{
			case 0:
				AssignChar0_3(1);
				break;

			case 1:
				AssignChar1();
				break;

			case 2:
				AssignChar2();
				break;

			case 3:
				AssignChar0_3(0);
				break;
		}

		if(ManageKeyb.lettera == lettera_precedente)
		{
			counter_lettera++;
		}
		else
		{
			counter_lettera = 0;
		}
		if(counter_lettera >= 100) // 1 secondo
		{   // reset
			ManageKeyb.KeyPressNum = 0;
			ManageKeyb.lettera_pronta = 1;
			counter_lettera = 0;
			KeybManage_Write_Timer_Disable();
		}

		lettera_precedente = ManageKeyb.lettera;
		ManageKeyb.KeyPressedPrev = ManageKeyb.KeyPressed;
	}
}

long tca8418_init(void)
{
	memset(&ConfigBits,0,sizeof(TConfigBits));
	memset(&KeyEvent,0,sizeof(TKeyEvent));
	memset(&InterruptEvent,0,sizeof(TInterruptEvent));
	memset(&ManageKeyb,0,sizeof(TManageKeyb));
	ManageKeyb.KeyPressed = KEY_NONE;
	ManageKeyb.KeyPressedPrev = KEY_NONE;
	ManageKeyb.LedLetNum = LED_OFF;

	//RESET_8418 - PJ4
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTJ_BASE, GPIO_PIN_4);

	//------------------------
	// RESET TASTIERA
	tca8418_reset(WRITE_0);
	ROM_SysCtlDelay(MILLI_DELAY);
	tca8418_reset(WRITE_1);
	ROM_SysCtlDelay(MILLI_DELAY);
	//------------------------

	I2C_init();

	ConfigBits.CFG_KE_IEN = 1;
	InterruptEvent.INT_STAT_K_INT = 1;

	// confugire rows and columns
	tca8418_write_byte(REG_KP_GPIO1, VALUE_REG_KP_GPIO1);
	tca8418_write_byte(REG_KP_GPIO2, VALUE_REG_KP_GPIO2);
	tca8418_write_byte(REG_KP_GPIO3, VALUE_REG_KP_GPIO3);

	// interrupt to micro enable - PJ5
	tca8418_write_byte(REG_CFG, ConfigBits.ConfigBitsAll);

	InitInterruptGPIOJ();

	// interrupt a tempo per gestione tasti
	InitTimer4();

	return 0;
}

long Key_is_pressed(void)
{
	if(ManageKeyb.counter_key_press == ManageKeyb.counter_key_release)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

long Key_ready(void)
{
	if(ManageKeyb.tasto_pronto == 1)
	{
		ManageKeyb.tasto_pronto = 0;
		return 1;
	}
	else
	{
		return 0;
	}
}

EKeyboard Key_last_pressed(void)
{
	return ManageKeyb.KeyPressed;
}

long Key_char_ready(void)
{
	if(ManageKeyb.lettera_pronta == 1)
	{
		ManageKeyb.lettera_pronta = 0;
		return 1;
	}
	else
	{
		return 0;
	}
}

char Key_last_char_pressed(void)
{
	return ManageKeyb.lettera;
}
