/******************************************************************************
 *
 * @brief     This file is part of the TouchGFX 4.4.1 evaluation distribution.
 *
 * @author    Draupner Graphics A/S <http://www.touchgfx.com>
 *
 ******************************************************************************
 *
 * @section Copyright
 *
 * Copyright (C) 2014 Draupner Graphics A/S <http://www.touchgfx.com>.
 * All rights reserved.
 *
 * TouchGFX is protected by international copyright laws and the knowledge of
 * this source code may not be used to write a similar product. This file may
 * only be used in accordance with a license and should not be re-
 * distributed in any way without the prior permission of Draupner Graphics.
 *
 * This is licensed software for evaluation use, any use must strictly comply
 * with the evaluation license agreement provided with delivery of the
 * TouchGFX software.
 *
 * The evaluation license agreement can be seen on www.touchgfx.com
 *
 * @section Disclaimer
 *
 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Draupner Graphics A/S has
 * no obligation to support this software. Draupner Graphics A/S is providing
 * the software "AS IS", with no express or implied warranties of any kind,
 * including, but not limited to, any implied warranties of merchantability
 * or fitness for any particular purpose or warranties against infringement
 * of any proprietary rights of a third party.
 *
 * Draupner Graphics A/S can not be held liable for any consequential,
 * incidental, or special damages, or any other relief, or for any claim by
 * any third party, arising from your use of this software.
 *
 *****************************************************************************/
 
  
 /**
 * Port SSD2541 CTP controller to TouchGFX.
 * Date: 7th Dec 2015
 * Programmer: John Leung @ TechToys Co.
 */
#include <SSD2541TouchController.hpp>

extern "C"
{
	#include "stm32f4xx_i2c.h"
}

#ifndef NO_USING_NAMESPACE_TOUCHGFX
using namespace touchgfx;
#endif

/**
* Software delay tuned for only 180MHz system clock
*/
static void DelayMs(__IO uint32_t ms)
{
  __IO uint32_t index = 0; 
  for(index = (45000 * ms); index != 0; index--)
  {
		;
  }
}

#ifdef USE_SSD2805_S6D04D2
/**
* @brief 	touchgfx::SSD2541TouchController::init() sets alternate function for I2C
*					and then perform low level I2C initialization. Finally, the array of initialization
*					sent to SSD2541 controller.
* @param	None
* @return	None
*/
void SSD2541TouchController::init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	//Enable peripheral clock using RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2Cx, ENABLE)
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2Cn, ENABLE);
	
	//enable RCC for SCL, SDA, & RST
	//(IRQ) is not used in touchGFX
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_SCL | RCC_AHB1Periph_SDA | RCC_AHB1Periph_RST, ENABLE);
#ifdef RELEASE_3
	GPIO_InitStructure.GPIO_Pin = CTP_RST_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;	//output push-pull mode
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_RST, ENABLE);
	GPIO_Init(CTP_RST_BASE, &GPIO_InitStructure);
	
	//hardware reset
	GPIO_WriteBit(CTP_RST_BASE, CTP_RST_PIN, Bit_SET);		
	delayMs(1);
	GPIO_WriteBit(CTP_RST_BASE, CTP_RST_PIN, Bit_RESET);
	delayMs(1);
	GPIO_WriteBit(CTP_RST_BASE, CTP_RST_PIN, Bit_SET);	
	delayMs(10);
#endif
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	
	//GPIOA8 = SCL3
	GPIO_PinAFConfig(CTP_SCL_BASE, CTP_SCL_SOURCE, GPIO_AF_I2C3);
	GPIO_InitStructure.GPIO_Pin = CTP_SCL_PIN;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//GPIOC9 = SDA3
	GPIO_PinAFConfig(CTP_SDA_BASE, CTP_SDA_SOURCE, GPIO_AF_I2C3);
	GPIO_InitStructure.GPIO_Pin = CTP_SDA_PIN;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	I2C_InitTypeDef	I2C_InitStructure;
	
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Disable ;
	I2C_InitStructure.I2C_DutyCycle =  I2C_DutyCycle_2;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;	//not relevant in master mode
	I2C_InitStructure.I2C_ClockSpeed = 400000;
	
	I2C_Init(CTP_I2C_CH, &I2C_InitStructure);
	I2C_Cmd(CTP_I2C_CH, ENABLE);
	
	uint16_t TableSize = (sizeof(SSD2541CfgTbl)/sizeof(InitMap));
	uint16_t index;
	
	for(index=0; index < TableSize; index++)
	{
		if(SSD2541CfgTbl[index].command==REG_NOP)
		{
			DelayMs(SSD2541CfgTbl[index].val);
		}
		else
		{
			//Note:SSD2541CfgTbl[index].val is a 16-bit value
			SSD2541TouchController::writeRegister(I2C_ADDR, SSD2541CfgTbl[index].command, SSD2541CfgTbl[index].val);
		}
	}
}

/*
//	This method does not allow us to return more than a single coordinate
//	Should be enhanced to track multiple fingers. => HAL TYPES.
// 	Depending on TouchControl setup - allow tracking of

1) Coordinates for Fingers <= MAX NR OF FINGERS
2) Gestures for Fingers <= MAX NR OF FINGERS

*/
static Finger finger;
bool SSD2541TouchController::sampleTouch(int32_t& x, int32_t& y)
{
  //For each configured and valid finger, collect 1) Coordinates  2) Gestures (if configured)
  uint8_t fingersOnDisplay = touchDetected();
   if(fingersOnDisplay)
  {
    //Get sample for finger 1 and extract data
      if (getSample ((Finger *)&finger))
      {
          x = finger.x;
          y = finger.y;

          //Apply transformations
          transformTouchData(x,y);

          return true;
      }
  }
  return false;
}

void SSD2541TouchController::transformTouchData(int32_t& x, int32_t& y)
{

  /* Example of linear transformations for various touch controller configurations
  Screen width in example is 480 and height is 272.                             */

  /***************************************
   X-axis is flipped
  ****************************************
  * (0,272)                      (480,272)
  *
  *
  *
  * (0,0)                          (480,0)
  ***************************************/
  // y = 272 - y;

  /***************************************
    Y-axis flipped
  ****************************************
  * (480,0)                          (0,0)
  *
  *
  *
  * (480,272)                      (0,272)
  ***************************************/
  //x = 480 - x

  //If transformation is configured, apply it to sampled coordinates
  switch(linearTransform)
  {
  case FLIP_X_AXIS:
    {
      y = displayHeight - y;
    }
    break;
  case FLIP_Y_AXIS:
    {
      x = displayWidth - x;
    }
    break;
  case NO_TRANSFORM:
    //do nothing
   break;
  }

  //If user defined a custom method for transformation elsewhere
  if(transformFunc!=0)
  {
    transformFunc(x,y,finger);
  }
}

/**
 * @fn		uint8_t touchgfx::SSD2541TouchController::touchDetected (void)
 * @brief	Touch Status return by reading STATUS REGISTER R79h
 * @param	None
 * @return	Register value in 16-bit width with DATA[1] read into higher byte
 *
 * 			Register value R79h contains the bits for Finger00- Finger09 plus
 * 			few abnormality bits.
 * 			Example, TouchStatusGet() = 0x1000 if Finger00 is detected
 * 			TouchStatusGet() = 0x3000 if Finger01 & Finger00 is detected, i.e. two fingers.
 * 			Finger01 is not valid without Finger00, therefore it is not possible to get 0x2000.
 *			Only higher byte is returned in this function for Finger00-Finger03
 */
uint8_t SSD2541TouchController::touchDetected (void) {
	
  uint8_t touched[2] = {0,0};

  //Read 2 bytes of REG_TOUCH_STATUS register into touched
	SSD2541TouchController::readRegister(I2C_ADDR, REG_TOUCH_STATUS, touched, 2);

  //bits[7:4] define the number of fingers on display.
	return (touched[1] & MASK_NR_OF_FINGERS);
  //return touched & MASK_NR_OF_FINGERS;
}

/**
 * @fn			touchgfx::SSD2541TouchController::getSample(Finger *touchData) 
 * @brief 	Only a single finger is supported by reading register at 0x7C (REG_FINGER00).
 *					param[0]	|	param[1]	|	param[2]				|	param[3]
 *					x-[7:0]		|	y-[7:0]		| x[11:8]y[11:8]	|	weight index[7:0]
 * @param		Pointer to Finger  *touchData
 * @return	true if a valid touch is detected
 *					false if no touch is detected
 * 					Only one finger is supported for small display like 1.54" LCD
 */
bool SSD2541TouchController::getSample(Finger *touchData) {
	static uint8_t param[4] = {0,0,0,0};
	
	//Read 4 bytes from REG_FINGER00 in param[] array
	SSD2541TouchController::readRegister(I2C_ADDR, REG_FINGER00, param, 4);
	
  //Use weight index to determine if a valid touch is detected
  if (param[3]!=0x00)
  {
    touchData->x = (((uint16_t)param[2]&0xF0)<<4)|param[0] ;
    touchData->y = (((uint16_t)param[2]&0x0F)<<8)|param[1];
		touchData->z = param[3];
    //Touchdata was valid
    return true;
  }
  //Touchdata was valid
  return false;
}

		 /**
     * @fn void SSD2541TouchController::readRegister(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t cnt);
     *
     * @brief Reads the specified register on the device with the specified address.
     *				Reference: https://github.com/g4lvanix/STM32F4-examples/blob/master/I2C%20Master/main.c
     * @param addr       The I2C device address.
     * @param reg        The register.
     * @param [out] data Pointer to buffer in which to place the result.
     * @param cnt        Size of buffer in bytes.
     *
     * @return None
     */
void SSD2541TouchController::readRegister(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t cnt)
{
	if(cnt==0) return;
	
	//wait until I2C channel is not busy. Watch out an infinite loop here!
	while(I2C_GetFlagStatus(CTP_I2C_CH, I2C_FLAG_BUSY)!=RESET)
		;
	
	//generate start bit
	I2C_GenerateSTART(CTP_I2C_CH, ENABLE);

	//wait for EV5 --> Slave has acknowledged start condition
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_MODE_SELECT));
	
	//write slave address
	I2C_Send7bitAddress(CTP_I2C_CH, addr<<1, I2C_Direction_Transmitter);	
	/**
	* wait for EV6, check if  
	* either Slave has acknowledged Master transmitter or 
	* Master receiver mode, depending on the transmission 
	* direction 
	*/  
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
	
	I2C_SendData(CTP_I2C_CH, reg);
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//I2C_EVENT_MASTER_BYTE_TRANSMITTED
	
	I2C_GenerateSTART(CTP_I2C_CH, ENABLE);
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_MODE_SELECT));
	//Send slave address with I2C direction as READ
	I2C_Send7bitAddress(CTP_I2C_CH, addr<<1, I2C_Direction_Receiver);	
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
	
	//deduct the last byte
	cnt-=1;

	while(cnt--)
	{
			//enable ack by assuming cnt>0 after deduct 1
			I2C_AcknowledgeConfig(CTP_I2C_CH, ENABLE);		
			//wait until a byte has been received
			while( !I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_BYTE_RECEIVED) );
			//read data from data register and store in array pointed by *data
			//generate an ack, too
			*data++ = I2C_ReceiveData(CTP_I2C_CH);	
	}
		//disable ack
		I2C_AcknowledgeConfig(CTP_I2C_CH, DISABLE);
		//wait until a byte has been received
		while( !I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_BYTE_RECEIVED) );
		//read the last byte from data register and store into array pointed to by *data
		*data = I2C_ReceiveData(CTP_I2C_CH);
		
		//finally generate a stop bit
		I2C_GenerateSTOP(CTP_I2C_CH, ENABLE);
}

    /**
     * @fn void SSD2541TouchController::writeRegister(uint8_t addr, uint8_t reg, uint16_t val);
     *
     * @brief Writes the specified value (16-bit) in a register.
     *				Reference: https://github.com/g4lvanix/STM32F4-examples/blob/master/I2C%20Master/main.c
     * @param addr The I2C device address.
     * @param reg  The register.
     * @param val  value to send in 16-bit
     *
     * @return None
     */
void SSD2541TouchController::writeRegister(uint8_t addr, uint8_t reg, uint16_t val)
{
	//wait until I2C channel is not busy. Watch out an infinite loop here!
	while(I2C_GetFlagStatus(CTP_I2C_CH, I2C_FLAG_BUSY)!=RESET)
		;
	
	I2C_GenerateSTART(CTP_I2C_CH, ENABLE);
	//wait for EV5 --> Slave has acknowledged start condition
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_MODE_SELECT))
		;
	
	I2C_Send7bitAddress(CTP_I2C_CH, addr<<1, I2C_Direction_Transmitter);
	/**
	* wait for EV6, check if  
	* either Slave has acknowledged Master transmitter or 
	* Master receiver mode, depending on the transmission 
	* direction 
	*/  
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
		;
	
	I2C_SendData(CTP_I2C_CH, reg);
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(CTP_I2C_CH, (uint8_t)(val>>8));	//send high byte first
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(CTP_I2C_CH, (uint8_t)val);				//send low byte next
	while(!I2C_CheckEvent(CTP_I2C_CH, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_GenerateSTOP(CTP_I2C_CH, ENABLE);
}

/**
* @fn			void SSD2541TouchController::delayMs(__IO uint32_t ms)
* @brief 	Software delay tuned for only 180MHz system clock
* @param	ms		delay in milli seconds
*/
void SSD2541TouchController::delayMs(__IO uint32_t ms)
{
  __IO uint32_t index = 0; 
  for(index = (45000 * ms); index != 0; index--)
  {
		;
  }
}

#endif	//USE_SSD2805_S6D04D2

