#include "Touch.h"

const touch_config_t  STMPE811_cfg = {
	"STMPE811", LCD_VDC5_CH0_PANEL, RESISTIVE,
    {I_2_C, TPIIC_SDA, TPIIC_SCL, NC, NC, NC, NC, 100000},
    {INT_ON_EDGE, FALLING_OR_ACTIVE_LO, TPIRQ_PIN}
};

bool	Touch::new_data;


/**************************************************************************//**
 * @brief       Constructor of the Touch class
 * @param[in]   pointer to Config structure (touch_config_t)
******************************************************************************/
//SPI(tp_cfg->interface.mosi, tp_cfg->interface.miso, tp_cfg->interface.sclk, tp_cfg->interface.ssel),
Touch::Touch( const touch_config_t * tp_cfg ) : I2C(tp_cfg->interface.sda, tp_cfg->interface.scl), InterruptIn(tp_cfg->activity_irq.pin)
{
	if(tp_cfg == NULL)
		touch_cfg = &STMPE811_cfg;
	else
		touch_cfg = tp_cfg;

	x = y = z = 0;	  adc_x = adc_y = adc_z = 0;
	xyz_data = screen_data; adc_data = raw_data;
	last_xyz_idx = FIFO_DEPTH;
	new_data = false;
	calib.data.flag = 0;

} /* End of constructor */


/**************************************************************************//**
 * @brief       Touch controller initialization
 * @retval      error code
******************************************************************************/
Touch::init_err_t Touch::Init( void )
{
	init_err_t tp_err;

	if(touch_cfg->type == RESISTIVE)
	{
		tp_err = Clb_Setup();

		if(tp_err != TOUCH_OK)
			return tp_err;
	}

	if(touch_cfg->interface.type == I_2_C)
		I2C::frequency(touch_cfg->interface.freq);
	//else
		//SPI::frequency(touch_cfg->interface.freq);

	tp_err = Drv_Setup();

	if(tp_err != TOUCH_OK)
		return tp_err;

	if(touch_cfg->activity_irq.polarity == FALLING_OR_ACTIVE_LO)
	{
		rise(NULL);
		fall(&Irq_Alert);
	}
	else
	{
		rise(&Irq_Alert);
		fall(NULL);
	}

	if(tp_err == TOUCH_OK)
		enable_irq();

	return tp_err;
} /* End of method Init() */


/**************************************************************************//**
 * @brief       Set Calibration data
 * @retval      error code
******************************************************************************/
Touch::init_err_t Touch::Clb_Setup()
{
	if(touch_cfg->name == "STMPE811")
	{
		#ifndef __USE_DEFAULT_CALIBRATION_DATA__
			// extract calibration info from lcdpanel EEPROM
			char adr = 0;

			if( (touch_cfg->interface.sda == EEIIC_SDA) || (touch_cfg->interface.scl == EEIIC_SCL))
			{	// EEPROM is on the same I2C channel no need to initialize a new one !
				if(I2C::write(EE_CALIB_DEVICE_ADDR, (const char *)&adr, 1, true) != 0)
					return TOUCH_INIT_ERR;
				if(I2C::read(EE_CALIB_DEVICE_ADDR, (char*)calib.KX08, sizeof(calib.KX08)) != 0)
					return TOUCH_INIT_ERR;
			}
			else
			{
				I2C clb_eeprom(EEIIC_SDA, EEIIC_SCL);

				clb_eeprom.frequency(100000);
				if(clb_eeprom.write(EE_CALIB_DEVICE_ADDR, (const char *)&adr, 1, true) != 0)
					return TOUCH_INIT_ERR;
				if(clb_eeprom.read(EE_CALIB_DEVICE_ADDR, (char*)calib.KX08, sizeof(calib.KX08)) != 0)
					return TOUCH_INIT_ERR;
			}
		#endif

		if(calib.data.flag != 1)
		{	// load default calibration info
			unsigned char clb[] = {TPCALIBRATION_DATA};
			memcpy(calib.KX08, clb, sizeof(clb));
		}

		return TOUCH_OK;
	}
	else
		return TOUCH_UNSUPP_ERR;
} /* End of method Clb_Setup() */


/**************************************************************************//**
 * @brief       Set Touch Controller settings
 * @retval      error code
******************************************************************************/
Touch::init_err_t Touch::Drv_Setup()
{
	if(touch_cfg->name == "STMPE811")
	{
		unsigned char i, initdata[][2] = { INIT_DATA };

		for(i=0; i<(sizeof(initdata)>>1); i++)
		{
			if(initdata[i][0] == INT_CTRL)
			{	// reconfigure interrupt if needed
				initdata[i][1] = 0x01;
				initdata[i][1] |= (touch_cfg->activity_irq.trigger == INT_ON_EDGE)? 0x02 : 0x00;
				initdata[i][1] |= (touch_cfg->activity_irq.polarity == RISING_OR_ACTIVE_HI)? 0x04 : 0x00;
			}

			if ((I2C::write(STMPE811_DEVICE_ADDR, (const char *)&initdata[i][0], 2)) != 0)
				return TOUCH_INIT_ERR;

			while (I2C::write(STMPE811_DEVICE_ADDR, (const char *)initdata, 0) != 0);  // ACK polling
		}

		return TOUCH_OK;
	}
	else
		return TOUCH_UNSUPP_ERR;
}


/**************************************************************************//**
 * @brief       Get one sample of data
 * @param[in]   * raw	: pointer to ring buffer to store the samples
******************************************************************************/
void Touch::Get_Data( unsigned long long * raw )
{
	if(touch_cfg->name == "STMPE811")
	{
		int idx = last_xyz_idx;
		unsigned char  i, packed_sample[16];
		unsigned short raw_x, raw_y;
		unsigned char  raw_z;

		i = TSC_DATA_FIFO;
		I2C::write(STMPE811_DEVICE_ADDR, (const char *)&i, 1, true);
		I2C::read(STMPE811_DEVICE_ADDR, (char *)packed_sample, sizeof(packed_sample));
		for(i=0; i<4; i++)
		{
			raw_x = (unsigned short)((packed_sample[(i*4)+0]<<4) | (packed_sample[(i*4)+1]>>4));
			raw_y = (unsigned short)(((0x0F & packed_sample[(i*4)+1])<<8) | packed_sample[(i*4)+2]);
			raw_z = packed_sample[(i*4)+3];

			idx = ((idx+1) < FIFO_DEPTH)? idx+1 : 0;
			raw[idx] = (unsigned long long)((raw_z<<32) + (raw_y<<16) + raw_x);
		}
	}
} /* End of method Get_Data() */


/**************************************************************************//**
 * @brief       Get all available samples of data
 * @param[in]   * raw	: pointer to ring buffer to store the samples
 * @retval      		  samples count
******************************************************************************/
int Touch::Get_Fifo( unsigned long long * raw )
{
	if(touch_cfg->name == "STMPE811")
	{
	  int idx = last_xyz_idx;
	  unsigned char packets;

	  packets = FIFO_SIZE;
	  I2C::write(STMPE811_DEVICE_ADDR, (const char *)&packets, 1, true);
	  packets = 0;
	  I2C::read(STMPE811_DEVICE_ADDR, (char *)&packets, 1);
	  if(packets)
	  {
		unsigned char  packed_sample[FIFO_DEPTH*4];
		unsigned short raw_x, raw_y, i;
		unsigned char  raw_z;

		raw_z = TSC_DATA_FIFO;
		I2C::write(STMPE811_DEVICE_ADDR, (const char *)&raw_z, 1, true);
		I2C::read(STMPE811_DEVICE_ADDR, (char *)packed_sample, packets*4);

	    for(i=0; i<packets; i++)
	    {
	      raw_x = (unsigned short)((packed_sample[(i*4)+0]<<4) | (packed_sample[(i*4)+1]>>4));
	      raw_y = (unsigned short)(((0x0F & packed_sample[(i*4)+1])<<8) | packed_sample[(i*4)+2]);
	      raw_z = packed_sample[(i*4)+3];

	      idx = ((idx+1) < FIFO_DEPTH)? idx+1 : 0;
	      raw[idx] = (raw_z<<32) + (raw_y<<16) + raw_x;
	    }

	    return packets;
	  }
	  return 0;
	}
	else
		return 0;
} /* End of method Get_Fifo() */


/**************************************************************************//**
 * @brief       Coordinates Transfer function
 * @param[in]   points		: number of samples which have to become meaningful
******************************************************************************/
void Touch::Get_XYZ( int points)
{
	if(touch_cfg->name == "STMPE811")
	{
		int i, idx;

		for(i=0; i<points; i++)
		{
			idx = ((last_xyz_idx+1) < FIFO_DEPTH)? last_xyz_idx+1 : 0;
			screen_data[idx].axis.x = (signed short)(calib.data.KX1*((signed short)raw_data[idx].axis.x)+calib.data.KX2*((signed short)raw_data[idx].axis.y)+calib.data.KX3+0.5);
			screen_data[idx].axis.y = (signed short)(calib.data.KY1*((signed short)raw_data[idx].axis.x)+calib.data.KY2*((signed short)raw_data[idx].axis.y)+calib.data.KY3+0.5);
			//screen_data[idx].axis.z = 0;
			last_xyz_idx = idx;
			//printf("\r\n REC: idx-> %d", idx);
			//printf("\r\n TH: x-> %d, y-> %d, dots-> %d", screen_data[idx].axis.x, screen_data[idx].axis.y, idx);
		}

		x = screen_data[last_xyz_idx].axis.x; adc_x = raw_data[last_xyz_idx].axis.x;
		y = screen_data[last_xyz_idx].axis.y; adc_y = raw_data[last_xyz_idx].axis.y;
		//z = screen_data[last_xyz_idx].axis.z; adc_z = raw_data[last_xyz_idx].axis.z;
		//printf("\r\n TH: x-> %d, y-> %d, dots-> %d", x, y, last_xyz_idx);

	}
} /* End of method Get_XYZ() */


/**************************************************************************//**
 * @brief       IRQ interrupt handler : indicates "New Data available" which activates i2c data transfer in Handle_touch()
******************************************************************************/
void Touch::Irq_Alert( void ) {	new_data = true; } /* End of method Irq_Alert() */


/**************************************************************************//**
 * @brief       Get index of the last sample in the ring buffer
 * @retval      idx
******************************************************************************/
int Touch::Get_Last_Idx( void ) { return last_xyz_idx; }


/**************************************************************************//**
 * @brief       Pull the new samples if new data is available
 * @param[in]   * pts 				: pointer to a variable to put the count of the new samples
 * @retval      Status of the pen 	(Stylus position up/down)
******************************************************************************/
bool Touch::Handle_touch( unsigned char *pts )
{
	static bool PenDown = false;
	unsigned char TP_IntStat = 0, rec[2];
	int dots = 0;

	*pts=0;

 if(new_data)
 {
	TP_IntStat = INT_STA;
	I2C::write(STMPE811_DEVICE_ADDR, (const char *)&TP_IntStat, 1, true);
	TP_IntStat = 0;
	I2C::read(STMPE811_DEVICE_ADDR, (char *)&TP_IntStat, 1);

	if(TP_IntStat & INT_FIFO_TH)
	{
		Get_Data(&raw_data[0].dot);
		Get_XYZ(4);

		*pts = 4;

		//Data = 1;
		//SET_TP_STATE(TP_FLAG_PEN_DATA_TH);
		//printf("\r\n TH: x-> %d, y-> %d", screen_data[3].axis.x, screen_data[3].axis.y);
	}

	if(TP_IntStat & INT_TOUCH_DET)
	{
		dots = Get_Fifo(&raw_data[0].dot);
		if(dots)
		{
			Get_XYZ(dots);
		    //Data = 1;
		    //CLR_TP_STATE(TP_FLAG_PEN_DATA_TH); //SET_TP_STATE(TP_FLAG_DATA_READY);
		    //printf("\r\n Touch Sample: x-> %d, y-> %d", screen_data[v-1].axis.x, screen_data[v-1].axis.y);
		}
		*pts = dots;

		PenDown = (PenDown)? false : true;
	}

	if(TP_IntStat & INT_FIFO_OFLOW)
	{
		rec[0] = FIFO_STA;
		rec[1] = 0x01;		// Clear FIFO
		I2C::write(STMPE811_DEVICE_ADDR, (const char *)rec, 2);

		rec[1] = 0x00;		// Reset FIFO
		I2C::write(STMPE811_DEVICE_ADDR, (const char *)rec, 2);
	}

	rec[0] = INT_STA;
	rec[1] = TP_IntStat;
	I2C::write(STMPE811_DEVICE_ADDR, (const char *)rec, 2);

	new_data = false;
 }

  return PenDown;
}

/**************************************************************************//**
 * @brief       Destructor of the Touch class
******************************************************************************/
Touch::~Touch( )
{
	touch_cfg = NULL;
	xyz_data = NULL;
	adc_data = NULL;
}

/* End of file */

