  /**
  ******************************************************************************
  * @file    k_calibration.c
  * @author  MCD Application Team
  * @brief   This example code shows how to calibrate the touchscreen.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright © 2016 STMicroelectronics International N.V. 
  * All rights reserved.</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without 
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice, 
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/** @addtogroup STM32F4xx_HAL_Examples
  * @{
  */

/** @addtogroup BSP
  * @{
  */

/* Imported variables */
extern TS_StateTypeDef TS_State;
#define CALIB_RES(val,pte1,ptm1,pte2,ptm2) ((((val-ptm2)*(pte1-pte2))/(ptm1-ptm2))+pte2)

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static uint8_t ts_calibration_done = 0;
static int16_t aPhysX[2], aPhysY[2], aLogX[2], aLogY[2];

typedef union
{
  struct
  {
    uint32_t     A1 : 15; 
    uint32_t     B1 : 16;
    uint32_t     Reserved : 1; 
  }b;
  uint32_t d32;
}CALIBRATION_Data1Typedef; 

typedef union
{
  struct
  {
    uint32_t      A2 : 15;
    uint32_t      B2 : 16; 
    uint32_t      IsCalibrated : 1;
  }b;
  uint32_t d32;
  
}CALIBRATION_Data2Typedef;

CALIBRATION_Data1Typedef data1;
CALIBRATION_Data2Typedef data2;

int16_t  A1, A2, B1, B2, C1, C2, D1, D2;

/* Private function prototypes -----------------------------------------------*/
static void TouchscreenCalibration_SetHint(void);
static void TouchScreen_Calibration_GetPhysValues(int16_t LogX, int16_t LogY, int16_t * pPhysX, int16_t * pPhysY) ;
static void TouchScreen_Calibration_WaitForPressedState(uint8_t Pressed) ;
/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Performs the TS calibration
  * @param  None
  * @retval Status (TS_OK = 0/ TS_ERROR = 1 / TS_TIMEOUT = 1 / TS_DEVICE_NOT_FOUND = 3)
  */
uint8_t Touchscreen_Calibration(void)
{
  uint8_t ts_status = TS_OK;
  uint8_t i;
  uint16_t ts_SizeX;
  uint16_t ts_SizeY;

  ts_SizeX = BSP_LCD_GetXSize();
  ts_SizeY = BSP_LCD_GetYSize();

  data1.d32 = k_BkupRestoreParameter(RTC_BKP_DR0);
  data2.d32 = k_BkupRestoreParameter(RTC_BKP_DR1); 
    
  if(data2.b.IsCalibrated == 0)
  {  
  TouchscreenCalibration_SetHint();

  /* Start touchscreen internal calibration and configuration + start */
  ts_status = BSP_TS_InitEx(ts_SizeX, ts_SizeY, LCD_ORIENTATION_LANDSCAPE_ROT180);//TS_OK;
  if (ts_status != TS_OK)
  {
    BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
    BSP_LCD_SetTextColor(LCD_COLOR_RED);
    BSP_LCD_DisplayStringAt(0, 240 - 95, (uint8_t *)"ERROR", CENTER_MODE);
	  BSP_LCD_DisplayStringAt(0, 240 - 80, (uint8_t *)"Touchscreen cannot be calibrated", CENTER_MODE);
    if(ts_status == TS_ERROR)
    {
    	BSP_LCD_DisplayStringAt(0, 240 - 65, (uint8_t *)"Touchscreen undefined error", CENTER_MODE);
    }
    else if(ts_status == TS_TIMEOUT)
    {
    	BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize() - 65, (uint8_t *)"Touchscreen Timeout", CENTER_MODE);
    }
    else
    {
    	/* TS_DEVICE_NOT_FOUND */
    	BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize() - 65, (uint8_t *)"Touchscreen Not Found", CENTER_MODE);
    }
  }
  else
  {
    /* Get touch points for SW calibration processing */
    aLogX[0] = 20;
    aLogY[0] = 20;
    aLogX[1] = BSP_LCD_GetXSize() - 20;
    aLogY[1] = BSP_LCD_GetYSize() - 20;

    for (i = 0; i < 2; i++)
    {
      TouchScreen_Calibration_GetPhysValues(aLogX[i], aLogY[i], &aPhysX[i], &aPhysY[i]);
      if(i == 1)
      {
        if((aPhysX[0] <= (aPhysX[1] + 100)) && (aPhysY[1] <= (aPhysY[0] + 100)))
        {
          i = 0;
        }
      }
    }
          
    data1.b.A1 = aPhysX[0]; 
    data1.b.B1 = aPhysX[1];
    k_BkupSaveParameter(RTC_BKP_DR0, data1.d32);
    
    data2.b.A2 = aPhysY[0];  
    data2.b.B2 = aPhysY[1];
    data2.b.IsCalibrated = 1;
    k_BkupSaveParameter(RTC_BKP_DR1, data2.d32);
  }
  }
  ts_calibration_done = 1;
  
  return (ts_status);
}

/**
  * @brief  Display calibration hint
  * @param  None
  * @retval None
  */
static void TouchscreenCalibration_SetHint(void)
{
  /* Clear the LCD */
  BSP_LCD_Clear(LCD_COLOR_WHITE);

  /* Set Touchscreen Demo description */
  BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
  BSP_LCD_SetBackColor(LCD_COLOR_WHITE);

  BSP_LCD_SetFont(&Font20);
  BSP_LCD_DisplayStringAt(0, 100, (uint8_t *)"Touchscreen", CENTER_MODE);
  BSP_LCD_DisplayStringAt(0, 120, (uint8_t *)"Calibration", CENTER_MODE);
}


/**
  * @brief  Get Physical position
  * @param  LogX : logical X position
  * @param  LogY : logical Y position
  * @param  pPhysX : Physical X position
  * @param  pPhysY : Physical Y position
  * @retval None
  */
static void TouchScreen_Calibration_GetPhysValues(int16_t LogX, int16_t LogY, int16_t * pPhysX, int16_t * pPhysY)
{
  /* Draw the ring */

  BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
  BSP_LCD_FillCircle(LogX, LogY, 20);
  BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
  BSP_LCD_FillCircle(LogX, LogY, 10);

  /* Wait until pressed state on the touch panel */
  TouchScreen_Calibration_WaitForPressedState(1);

  /* Return as physical touch values the positions of first touch, even if double touched occurred */
  *pPhysX = TS_State.touchX[0];
  *pPhysY = TS_State.touchY[0];


    /* Wait until touch is released on touch panel */
    TouchScreen_Calibration_WaitForPressedState(0);

  BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
  BSP_LCD_FillCircle(LogX, LogY, 20);
}

/**
  * @brief  TouchScreen_Calibration_WaitForPressedState : wait until a particular press/depress action
  *         The function is managing anti-rebound : that is the awaited state when detected
  *         needs to be stable for a sufficient time (timeout time), otherwise a new sense to search
  *         for awaited state is performed. When awaited state is found and state is stable for timeout
  *         duration, the function is exited.
  * @param  uint8_t Pressed :  Awaited pressed state
  *         - Await touch (single/multiple) detection if Pressed == 1
  *         - Await no touch detection if Pressed == 0
  * @retval None
  */
static void TouchScreen_Calibration_WaitForPressedState(uint8_t Pressed)
{
  uint16_t TimeStart = 0;
  uint8_t  status = TS_OK;
  uint32_t exitFirstLevelWhileLoopReq = 0;  /* By default no exit request from first level while loop  */
  uint32_t exitSecondLevelWhileLoopReq = 0; /* By default no exit request from second level while loop */
  
  /* First level while loop entry */
  do
  {
    /* reset exit second level while loop in case it was set */
    exitSecondLevelWhileLoopReq = 0;

    /* Sense of touch state from touch IC until get the awaited state in parameter 'Pressed' */
    status = BSP_TS_GetState(&TS_State);
      
    if(status == TS_OK)
    {
      if (((Pressed == 0) && (TS_State.touchDetected == 0)) ||
          ((Pressed == 1) && ((TS_State.touchDetected == 1) || (TS_State.touchDetected == 2))))
      {
        /* Got awaited press state */
        /* Record in 'TimeStart' the time of awaited touch event for anti-rebound calculation */
        /* The state should persist for a minimum sufficient time */
        TimeStart = HAL_GetTick();

        /* Is state of the touch changing ? */
        /* Second level while loop entry */
        do
        {
          /* New sense of touch state from touch IC : to evaluate if state was stable */
          status = BSP_TS_GetState(&TS_State);
              if(status == TS_OK)
              {
                /* Is there a state change compared since having found the awaited state ? */
                if (((Pressed == 0) && ((TS_State.touchDetected == 1) || (TS_State.touchDetected == 2))) ||
                    ((Pressed == 1) && ((TS_State.touchDetected == 0))))
                {            
                    /* Too rapid state change => anti-rebound management : restart first touch search */
                    exitSecondLevelWhileLoopReq = 1; /* exit request from second level while loop */
                  }
            else if ((HAL_GetTick() - 100) > TimeStart)
            {
              /* State have not changed for the timeout duration (stable touch for 100 ms) */
              /* This means the touch state is stable : can exit function */

              /* found valid touch, exit both while levels */
              exitSecondLevelWhileLoopReq = 1;
              exitFirstLevelWhileLoopReq  = 1;
            }

            /* Wait 10 ms before next sense of touch at next loop iteration */
            HAL_Delay(10);

          } /* of if(status == TS_OK) */
        }
        while (!exitSecondLevelWhileLoopReq);

      } /* of if (((Pressed == 0) && .... */

    } /* of if(status == TS_OK) */

    if(!exitFirstLevelWhileLoopReq)
    {
      /* Wait some time before next sense of touch at next loop iteration */
      HAL_Delay(10);
    }
  }
  while (!exitSecondLevelWhileLoopReq);
}

/**
  * @brief  Calibrate x position (to obtain X = calibrated(x))
  * @param  x : X position
  * @retval calibrated x
  */
uint16_t TouchScreen_Get_Calibrated_X(uint16_t x)
{
   int32_t ret=(CALIB_RES(x,20,data1.b.A1,220,data1.b.B1) - 10);
   if (ret<0) ret=0;
   else if  (ret>240) ret=239;
   return ret;     
}

/**
  * @brief  Calibrate Y position
  * @param  y : Y position
  * @retval calibrated y
  */
uint16_t TouchScreen_Get_Calibrated_Y(uint16_t y)
{
   int32_t ret=(CALIB_RES(y,20,data2.b.A2,220,data2.b.B2) + 10);
   if (ret<0) ret=0;
   else if  (ret>240) ret=239;
   return ret;
}

/**check if the TS is calibrated
  * @param  None
* @retval calibration state (1 : calibrated / 0: no)
  */
uint8_t TouchScreen_IsCalibrationDone(void)
{
  return (ts_calibration_done);
}

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
