/**
  ******************************************************************************
  * @file    tsl_linrot.c
  * @author  MCD Application Team
  * @version V2.0.0
  * @date    22-April-2014
  * @brief   This file contains all functions to manage Linear and Rotary sensors.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2014 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "tsl_linrot.h"
#include "tsl_globals.h"

#if TSLPRM_TOTAL_LNRTS > 0

/* Private typedefs ----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/

/* Private macros ------------------------------------------------------------*/

#define THIS_OBJ_TYPE             TSL_Globals.This_Obj->Type

#define THIS_STATEID              TSL_Globals.This_LinRot->p_Data->StateId
#define THIS_RAW_POSITION         TSL_Globals.This_LinRot->p_Data->RawPosition
#define THIS_POSITION             TSL_Globals.This_LinRot->p_Data->Position
#define THIS_CHANGE               TSL_Globals.This_LinRot->p_Data->Change
#define THIS_POSCHANGE            TSL_Globals.This_LinRot->p_Data->PosChange
#define THIS_COUNTER_DEB          TSL_Globals.This_LinRot->p_Data->CounterDebounce
#define THIS_COUNTER_DIR          TSL_Globals.This_LinRot->p_Data->CounterDirection
#define THIS_COUNTER_DTO          TSL_Globals.This_LinRot->p_Data->CounterDTO
#define THIS_DXSLOCK              TSL_Globals.This_LinRot->p_Data->DxSLock
#define THIS_DIRECTION            TSL_Globals.This_LinRot->p_Data->Direction

#define THIS_PROXIN_TH            TSL_Globals.This_LinRot->p_Param->ProxInTh
#define THIS_PROXOUT_TH           TSL_Globals.This_LinRot->p_Param->ProxOutTh
#define THIS_DETECTIN_TH          TSL_Globals.This_LinRot->p_Param->DetectInTh
#define THIS_DETECTOUT_TH         TSL_Globals.This_LinRot->p_Param->DetectOutTh
#define THIS_CALIB_TH             TSL_Globals.This_LinRot->p_Param->CalibTh

#define THIS_RESOLUTION           TSL_Globals.This_LinRot->p_Param->Resolution
#define THIS_DIR_CHG_POS          TSL_Globals.This_LinRot->p_Param->DirChangePos

#define THIS_COUNTER_DEB_CALIB     TSL_Globals.This_LinRot->p_Param->CounterDebCalib
#define THIS_COUNTER_DEB_PROX      TSL_Globals.This_LinRot->p_Param->CounterDebProx
#define THIS_COUNTER_DEB_DETECT    TSL_Globals.This_LinRot->p_Param->CounterDebDetect
#define THIS_COUNTER_DEB_RELEASE   TSL_Globals.This_LinRot->p_Param->CounterDebRelease
#define THIS_COUNTER_DEB_ERROR     TSL_Globals.This_LinRot->p_Param->CounterDebError
#define THIS_COUNTER_DEB_DIRECTION TSL_Globals.This_LinRot->p_Param->CounterDebDirection

#define THIS_NB_CHANNELS           TSL_Globals.This_LinRot->NbChannels
#define THIS_SCT_COMP              TSL_Globals.This_LinRot->SctComp
#define THIS_POS_CORR              TSL_Globals.This_LinRot->PosCorr

#if TSLPRM_DTO > 0
#define DTO_GET_TIME  {TSL_linrot_DTOGetTime();}
#else
#define DTO_GET_TIME
#endif

/* Private variables ---------------------------------------------------------*/

//================================================================
// See AN2869 for more details on Linear and Rotary sensors design
//================================================================

//==============================================================================
// 3 CHANNELS - LINEAR - MONO - 0/255 at extremities
// i.e. CH1 CH2 CH3
//==============================================================================
#if TSLPRM_USE_3CH_LIN_M1 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_3CH_LIN_M1[3][3] =
{
// sec = 1     2     3
//   j = 0     1     2
    {    0,  -96,    0 }, // maj = 1; i = 0
    {   32,    0, -160 }, // maj = 2; i = 1
    {    0,   96,    0 }  // maj = 3; i = 2
};
#endif

//==============================================================================
// 3 CHANNELS - LINEAR - MONO
// i.e. CH1 CH2 CH3
//==============================================================================
#if TSLPRM_USE_3CH_LIN_M2 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_3CH_LIN_M2[3][3] =
{
// sec = 1     2     3
//   j = 0     1     2
    {    0, -192,    0 }, // maj = 1; i = 0
    {   64,    0, -320 }, // maj = 2; i = 1
    {    0,  192,    0 }  // maj = 3; i = 2
};
#endif

//==============================================================================
// 3 CHANNELS - LINEAR - HALF-ENDED
// i.e. CH1 CH2 CH3 CH1
//==============================================================================
#if TSLPRM_USE_3CH_LIN_H > 0
CONST TSL_tsignPosition_T TSL_POSOFF_3CH_LIN_H[3][3] =
{
// sec = 1     2     3
//   j = 0     1     2
    {    0,  -96,  160 }, // maj = 1; i = 0
    {   32,    0, -160 }, // maj = 2; i = 1
    { -224,   96,    0 }  // maj = 3; i = 2
};
#endif

//==============================================================================
// 3 CHANNELS - ROTARY - MONO
// i.e. CH1 CH2 CH3
//==============================================================================
#if TSLPRM_USE_3CH_ROT_M > 0
CONST TSL_tsignPosition_T TSL_POSOFF_3CH_ROT_M[3][3] =
{
// sec = 1     2     3
//   j = 0     1     2
    {    0,  -64,  107 }, // maj = 1; i = 0
    {   21,    0, -107 }, // maj = 2; i = 1
    { -149,   64,    0 }  // maj = 3; i = 2
};
#endif

//==============================================================================
// 4 CHANNELS - LINEAR - MONO - 0/255 at extremities
// i.e. CH1 CH2 CH3 CH4
//==============================================================================
#if TSLPRM_USE_4CH_LIN_M1 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_4CH_LIN_M1[4][4] =
{
// sec = 1     2     3     4
//   j = 0     1     2     3
    {    0,  -64,    0,    0 }, // maj = 1; i = 0
    {   21,    0, -107,    0 }, // maj = 2; i = 1
    {    0,   64,    0, -149 }, // maj = 3; i = 2
    {    0,    0,  107,    0 }  // maj = 4; i = 3
};
#endif

//==============================================================================
// 4 CHANNELS - LINEAR - MONO
// i.e. CH1 CH2 CH3 CH4
//==============================================================================
#if TSLPRM_USE_4CH_LIN_M2 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_4CH_LIN_M2[4][4] =
{
// sec = 1     2     3     4
//   j = 0     1     2     3
    {    0,  -96,    0,    0 }, // maj = 1; i = 0
    {   32,    0, -160,    0 }, // maj = 2; i = 1
    {    0,   96,    0, -224 }, // maj = 3; i = 2
    {    0,    0,  160,    0 }  // maj = 4; i = 3
};
#endif

//==============================================================================
// 4 CHANNELS - LINEAR - HALF-ENDED
// i.e. CH1 CH2 CH3 CH4 CH1
//==============================================================================
#if TSLPRM_USE_4CH_LIN_H > 0
CONST TSL_tsignPosition_T TSL_POSOFF_4CH_LIN_H[4][4] =
{
// sec = 1     2     3     4
//   j = 0     1     2     3
    {    0,  -64,    0,  149 }, // maj = 1; i = 0
    {   21,    0, -107,    0 }, // maj = 2; i = 1
    {    0,   64,    0, -149 }, // maj = 3; i = 2
    { -192,    0,  107,    0 }  // maj = 4; i = 3
};
#endif

//==============================================================================
// 4 CHANNELS - ROTARY - MONO
// i.e. CH1 CH2 CH3 CH4
//==============================================================================
#if TSLPRM_USE_4CH_ROT_M > 0
CONST TSL_tsignPosition_T TSL_POSOFF_4CH_ROT_M[4][4] =
{
// sec = 1     2     3     4
//   j = 0     1     2     3
    {    0,  -48,    0,  112 }, // maj = 1; i = 0
    {   16,    0,  -80,    0 }, // maj = 2; i = 1
    {    0,   48,    0, -112 }, // maj = 3; i = 2
    { -144,    0,   80,    0 }  // maj = 4; i = 3
};
#endif

//==============================================================================
// 5 CHANNELS - LINEAR - MONO - 0/255 at extremities
// i.e. CH1 CH2 CH3 CH4 CH5
//==============================================================================
#if TSLPRM_USE_5CH_LIN_M1 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_5CH_LIN_M1[5][5] =
{
// sec = 1     2     3     4     5
//   j = 0     1     2     3     4
    {    0,  -48,    0,    0,    0 }, // maj = 1; i = 0
    {   16,    0,  -80,    0,    0 }, // maj = 2; i = 1
    {    0,   48,    0, -112,    0 }, // maj = 3; i = 2
    {    0,    0,   80,    0, -144 }, // maj = 4; i = 3
    {    0,    0,    0,  112,    0 }  // maj = 5; i = 4
};
#endif

//==============================================================================
// 5 CHANNELS - LINEAR - MONO
// i.e. CH1 CH2 CH3 CH4 CH5
//==============================================================================
#if TSLPRM_USE_5CH_LIN_M2 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_5CH_LIN_M2[5][5] =
{
// sec = 1     2     3     4     5
//   j = 0     1     2     3     4
    {    0,  -64,    0,    0,    0 }, // maj = 1; i = 0
    {   21,    0, -107,    0,    0 }, // maj = 2; i = 1
    {    0,   64,    0, -149,    0 }, // maj = 3; i = 2
    {    0,    0,  107,    0, -192 }, // maj = 4; i = 3
    {    0,    0,    0,  149,    0 }  // maj = 5; i = 4
};
#endif

//==============================================================================
// 5 CHANNELS - LINEAR - HALF-ENDED
// i.e. CH1 CH2 CH3 CH4 CH5 CH1
//==============================================================================
#if TSLPRM_USE_5CH_LIN_H > 0
CONST TSL_tsignPosition_T TSL_POSOFF_5CH_LIN_H[5][5] =
{
// sec = 1     2     3     4     5
//   j = 0     1     2     3     4
    {    0,  -48,    0,    0,  144 }, // maj = 1; i = 0
    {   16,    0,  -80,    0,    0 }, // maj = 2; i = 1
    {    0,   48,    0, -112,    0 }, // maj = 3; i = 2
    {    0,    0,   80,    0, -144 }, // maj = 4; i = 3
    { -176,    0,    0,  112,    0 }  // maj = 5; i = 4
};
#endif

//==============================================================================
// 5 CHANNELS - ROTARY - MONO
// i.e. CH1 CH2 CH3 CH4 CH5
//==============================================================================
#if TSLPRM_USE_5CH_ROT_M > 0
CONST TSL_tsignPosition_T TSL_POSOFF_5CH_ROT_M[5][5] =
{
// sec = 1     2     3     4     5
//   j = 0     1     2     3     4
     {   0,  -38,    0,    0,  115 }, // maj = 1; i = 0
     {  13,    0,  -64,    0,    0 }, // maj = 2; i = 1
     {   0,   38,    0,  -90,    0 }, // maj = 3; i = 2
     {   0,    0,   64,    0, -115 }, // maj = 4; i = 3
     {-141,    0,    0,   90,    0 }  // maj = 5; i = 4
};
#endif

//==============================================================================
// 5 CHANNELS - ROTARY - DUAL
// i.e. CH1 CH2 CH3 CH4 CH5 CH1 CH3 CH5 CH2 CH4
//==============================================================================
#if TSLPRM_USE_5CH_ROT_D > 0
CONST TSL_tsignPosition_T TSL_POSOFF_5CH_ROT_D[5][5] =
{
// sec = 1     2     3     4     5
//   j = 0     1     2     3     4
     {   0,  -19,  -83,  122,   58 }, // maj = 1; i = 0
     {   6,    0,  -32, -122,   96 }, // maj = 2; i = 1
     {  70,   19,    0,  -45,  -96 }, // maj = 3; i = 2
     {-134,  109,   32,    0,  -58 }, // maj = 4; i = 3
     { -70, -109,   83,   45,    0 }  // maj = 5; i = 4
};
#endif

//==============================================================================
// 6 CHANNELS - LINEAR - MONO - 0/255 at extremities
// i.e. CH1 CH2 CH3 CH4 CH5 CH6
//==============================================================================
#if TSLPRM_USE_6CH_LIN_M1 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_6CH_LIN_M1[6][6] =
{
// sec = 1     2     3     4     5     6
//   j = 0     1     2     3     4     5
     {   0,  -38,    0,    0,    0,    0 }, // maj = 1; i = 0
     {  13,    0,  -64,    0,    0,    0 }, // maj = 2; i = 1
     {   0,   38,    0,  -90,    0,    0 }, // maj = 3; i = 2
     {   0,    0,   64,    0, -115,    0 }, // maj = 4; i = 3
     {   0,    0,    0,   90,    0, -141 }, // maj = 5; i = 4
     {   0,    0,    0,    0,  115,    0 }  // maj = 6; i = 5
};
#endif

//==============================================================================
// 6 CHANNELS - LINEAR - MONO
// i.e. CH1 CH2 CH3 CH4 CH5 CH6
//==============================================================================
#if TSLPRM_USE_6CH_LIN_M2 > 0
CONST TSL_tsignPosition_T TSL_POSOFF_6CH_LIN_M2[6][6] =
{
// sec = 1     2     3     4     5     6
//   j = 0     1     2     3     4     5
     {   0,  -48,    0,    0,    0,    0 }, // maj = 1; i = 0
     {  16,    0,  -80,    0,    0,    0 }, // maj = 2; i = 1
     {   0,   48,    0, -112,    0,    0 }, // maj = 3; i = 2
     {   0,    0,   80,    0, -144,    0 }, // maj = 4; i = 3
     {   0,    0,    0,  112,    0, -176 }, // maj = 5; i = 4
     {   0,    0,    0,    0,  144,    0 }  // maj = 6; i = 5
};
#endif

//==============================================================================
// 6 CHANNELS - LINEAR - HALF-ENDED
// i.e. CH1 CH2 CH3 CH4 CH5 CH6 CH1
//==============================================================================
#if TSLPRM_USE_6CH_LIN_H > 0
CONST TSL_tsignPosition_T TSL_POSOFF_6CH_LIN_H[6][6] =
{
// sec = 1     2     3     4     5     6
//   j = 0     1     2     3     4     5
     {   0,  -38,    0,    0,    0,  141 }, // maj = 1; i = 0
     {  13,    0,  -64,    0,    0,    0 }, // maj = 2; i = 1
     {   0,   38,    0,  -90,    0,    0 }, // maj = 3; i = 2
     {   0,    0,   64,    0, -115,    0 }, // maj = 4; i = 3
     {   0,    0,    0,   90,    0, -141 }, // maj = 5; i = 4
     {-166,    0,    0,    0,  115,    0 }  // maj = 6; i = 5
};
#endif

//==============================================================================
// 6 CHANNELS - ROTARY - MONO
// i.e. CH1 CH2 CH3 CH4 CH5 CH6
//==============================================================================
#if TSLPRM_USE_6CH_ROT_M > 0
CONST TSL_tsignPosition_T TSL_POSOFF_6CH_ROT_M[6][6] =
{
// sec = 1     2     3     4     5     6
//   j = 0     1     2     3     4     5
     {   0,  -32,    0,    0,    0,  117 }, // maj = 1; i = 0
     {  11,    0,  -53,    0,    0,    0 }, // maj = 2; i = 1
     {   0,   32,    0,  -75,    0,    0 }, // maj = 3; i = 2
     {   0,    0,   53,    0,  -96,    0 }, // maj = 4; i = 3
     {   0,    0,    0,   75,    0, -117 }, // maj = 5; i = 4
     {-139,    0,    0,    0,   96,    0 }  // maj = 6; i = 5
};
#endif

//------------------
// Common parameters
//------------------

#define DIRECTION_CHANGE_MAX_DISPLACEMENT (255)
#define DIRECTION_CHANGE_TOTAL_STEPS      (256)
#define RESOLUTION_CALCULATION            (8)

static TSL_tNb_T CalibDiv;

/* Private functions prototype -----------------------------------------------*/

void TSL_linrot_DTOGetTime(void);
void TSL_linrot_ProcessCh_All_SetStatus(TSL_ObjStatus_enum_T sts);
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DataReady(void);
TSL_Status_enum_T TSL_linrot_ProcessCh_All_AcqStatus(TSL_AcqStatus_enum_T sts);
TSL_Status_enum_T TSL_linrot_ProcessCh_One_AcqStatusError(void);
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DeltaBelowEquMinus(TSL_tThreshold_T th, TSL_tIndex_T coeff);
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DeltaAboveEqu(TSL_tThreshold_T th, TSL_tIndex_T coeff);
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DeltaAbove(TSL_tThreshold_T th, TSL_tIndex_T coeff);
TSL_Status_enum_T TSL_linrot_ProcessCh_All_DeltaBelowEqu(TSL_tThreshold_T th, TSL_tIndex_T coeff);
void TSL_linrot_ProcessCh_All_ClearRef(void);
TSL_tDelta_T TSL_linrot_NormDelta(TSL_ChannelData_T *ch, TSL_tIndex_T idx);


//==============================================================================
// "Object methods" functions
//==============================================================================

/**
  * @brief  Init parameters with default values from configuration file
  * @param  None
  * @retval None
  */
void TSL_linrot_Init(void)
{
  // Thresholds
#if TSLPRM_USE_PROX > 0
  THIS_PROXIN_TH    = TSLPRM_LINROT_PROX_IN_TH;
  THIS_PROXOUT_TH   = TSLPRM_LINROT_PROX_OUT_TH;
#endif
  THIS_DETECTIN_TH  = TSLPRM_LINROT_DETECT_IN_TH;
  THIS_DETECTOUT_TH = TSLPRM_LINROT_DETECT_OUT_TH;
  THIS_CALIB_TH     = TSLPRM_LINROT_CALIB_TH;

  // Debounce counters
  THIS_COUNTER_DEB_CALIB   = TSLPRM_DEBOUNCE_CALIB;
#if TSLPRM_USE_PROX > 0
  THIS_COUNTER_DEB_PROX    = TSLPRM_DEBOUNCE_PROX;
#endif
  THIS_COUNTER_DEB_DETECT  = TSLPRM_DEBOUNCE_DETECT;
  THIS_COUNTER_DEB_RELEASE = TSLPRM_DEBOUNCE_RELEASE;
  THIS_COUNTER_DEB_ERROR   = TSLPRM_DEBOUNCE_ERROR;

  // Other parameters for linear/rotary only
  THIS_RESOLUTION            = TSLPRM_LINROT_RESOLUTION;
  THIS_DIR_CHG_POS           = TSLPRM_LINROT_DIR_CHG_POS;
  THIS_COUNTER_DEB_DIRECTION = TSLPRM_LINROT_DIR_CHG_DEB;

  // Initial state
  TSL_linrot_SetStateCalibration(TSLPRM_CALIB_DELAY);
}


/**
  * @brief  Process the State Machine
  * @param  None
  * @retval None
  */
void TSL_linrot_Process(void)
{
  TSL_StateId_enum_T prev_state_id;

  // Check if at least one channel has a data ready
  if ((TSL_linrot_ProcessCh_One_DataReady() == TSL_STATUS_OK) || (THIS_STATEID == TSL_STATEID_OFF))
  {

    prev_state_id = THIS_STATEID;

#if TSLPRM_TOTAL_LINROTS > 0
    if ((TSL_Globals.This_Obj->Type == TSL_OBJ_LINEAR) ||
        (TSL_Globals.This_Obj->Type == TSL_OBJ_ROTARY))
    {
      // Launch the object state function
      TSL_Globals.This_LinRot->p_SM[THIS_STATEID].StateFunc();
    }
#endif

#if TSLPRM_TOTAL_LINROTS_B > 0
    if ((TSL_Globals.This_Obj->Type == TSL_OBJ_LINEARB) ||
        (TSL_Globals.This_Obj->Type == TSL_OBJ_ROTARYB))
    {
      // Launch the TSL_Params state function
      TSL_Params.p_LinRotSM[THIS_STATEID].StateFunc();
    }
#endif

    // Check if the new state has changed
    if (THIS_STATEID == prev_state_id)
    {
      THIS_CHANGE = TSL_STATE_NOT_CHANGED;
    }
    else
    {
      THIS_CHANGE = TSL_STATE_CHANGED;
    }

#if TSLPRM_USE_DXS > 0
    if (THIS_STATEID != TSL_STATEID_DETECT)
    {
      THIS_DXSLOCK = TSL_FALSE;
    }
    if (THIS_STATEID == TSL_STATEID_TOUCH)
    {
      THIS_STATEID = TSL_STATEID_DETECT;
    }
#endif

  }
}


/**
  * @brief  Calculate the position
  * @param  None
  * @retval Status Return OK if position calculation is correct
  * @note   The position is calculated only if the number of channels is greater than 2
  */
TSL_Status_enum_T TSL_linrot_CalcPos(void)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  TSL_tDelta_T norm_delta;
  static TSL_tDelta_T delta1;
  static TSL_tDelta_T delta2;
  static TSL_tDelta_T delta3;
  static TSL_tIndex_T index1;
  static TSL_tIndex_T index2;
  TSL_tNb_T minor;
  TSL_tNb_T major;
  TSL_tNb_T sector_computation = 0;
  TSL_tNb_T position_correction = 0;
  TSL_tsignPosition_T new_position = 0;
  TSL_tPosition_T u_new_position = 0;

  delta1 = 0;
  delta2 = 0;
  delta3 = 0;

  index1 = 0;
  index2 = 0;

  // The position change flag will be set only if a new position is detected.
  THIS_POSCHANGE = TSL_STATE_NOT_CHANGED;

  // The position is calculated only if the number of channels is greater than 2
  if (THIS_NB_CHANNELS < 3)
  {
    return TSL_STATUS_ERROR;
  }

  //--------------------------------------------------------------------------
  // Sort the channels' delta
  //   - delta1 and index1 = biggest
  //   - delta2 and index2 = middle
  //   - delta3 and index3 = lowest
  //--------------------------------------------------------------------------
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {

#if TSLPRM_LINROT_USE_NORMDELTA > 0
    norm_delta = TSL_linrot_NormDelta(p_Ch, idx); // Normalize the Delta
#else
    norm_delta = p_Ch->Delta; // Take only the Delta
#endif

    // The Delta must be positive only otherwise it is noise
    if (norm_delta < 0) {norm_delta = 0;}

    if (norm_delta > delta1)
    {
      delta3 = delta2;
      delta2 = delta1;
      delta1 = norm_delta;
      index2 = index1;
      index1 = idx;
    }
    else
    {
      if (norm_delta > delta2)
      {
        delta3 = delta2;
        delta2 = norm_delta;
        index2 = idx;
      }
      else
      {
        if (norm_delta > delta3)
        {
          delta3 = norm_delta;
        }
      }
    }

    p_Ch++; // Next channel

  } // for all channels

  // Noise filter: we need at least two significant Delta measurements
  if (delta2 < ((TSL_tThreshold_T)(THIS_DETECTOUT_TH >> 1) - 1))
  {
    return TSL_STATUS_ERROR;
  }

  //----------------------------------------------------------------------------
  // Position calculation...
  //----------------------------------------------------------------------------

  /*----------------------------------------------------------------------------
    B = Biggest signal measured (Delta1/Index1)
    M = Middle signal measured (Delta2/Index2)
    S = Smallest signal measured (Delta3/Index3)
    
    - The equation to find the position is:
      Position = Offset +/- [ Sector_Size x ( Major / (Major + Minor) ) ]
   
    - The Offset is the position of the middle of the Middle signal segment.
      All the Offset values are stored in the ROM table Table_POSITION_OFFSET.
   
    - Major = Biggest - Smallest signals
      Minor = Middle - Smallest signals
   
    - The Sector_Size depends of the number of channels used
  ----------------------------------------------------------------------------*/

  // Calculates the Major and Minor parameters
  minor = (TSL_tNb_T)(delta2 - delta3); // Middle - Smallest signals
  major = (TSL_tNb_T)(delta1 - delta3); // Biggest - Smallest signals

  // Select the offset position in the position offset constant table
  // Equal to: new_position = TABLE_POSITION_OFFSET_xCH_xxx[index1][index2];
  new_position = *(TSL_Globals.This_LinRot->p_PosOff + (index1 * THIS_NB_CHANNELS) + index2);
  sector_computation = THIS_SCT_COMP;
  position_correction = THIS_POS_CORR;

  // Calculates: [ Sector_Size x ( Major / (Major + Minor) ) ]
  sector_computation = major * sector_computation;
  sector_computation = sector_computation / (major + minor);

  // Use the sign bit from position table to define the interpretation direction.
  // The NewPosition is multiplied by 2 because the Offset stored in the ROM
  // table is divided by 2...
  if (new_position > 0) // Means Offset is > 0 in the position table
  {
    new_position = (TSL_tsignPosition_T)(new_position << 1);
    new_position += sector_computation;
  }
  else // means Offset is <= 0 in the ROM table
  {
    new_position = (TSL_tsignPosition_T)((-new_position) << 1);
    new_position -= sector_computation;
  }

  // Position is calculated differently if LINEAR or ROTARY sensor
  if ((THIS_OBJ_TYPE == TSL_OBJ_LINEAR) || (THIS_OBJ_TYPE == TSL_OBJ_LINEARB))
  {

    // First adjustment used to shift all the values to obtain the "zero"
    if (new_position > 0)
    {
      new_position -= position_correction;
    }
    else
    {
      new_position = new_position + (256 - position_correction);
    }

    // Second adjustment used to clamp the values at both ends of sensor
    if (new_position < 0)
    {
      new_position = 0;
    }

    if (new_position > 255)
    {
      new_position = 255;
    }

  }
  else // ROTARY sensor: keep only the low byte
  {
    new_position = (TSL_tPosition_T)new_position;
  }

  //----------------------------------------------------------------------------
  // Direction Change Process
  //----------------------------------------------------------------------------

  if (THIS_DIRECTION == TSL_TRUE) // Anticlockwise direction ...
  {

    // Check Direction changed and Position overflow from 0x00 to 0xFF not realized !
    if (((TSL_tPosition_T)new_position > THIS_RAW_POSITION) && (((TSL_tPosition_T)new_position - THIS_RAW_POSITION) < DIRECTION_CHANGE_MAX_DISPLACEMENT))
    {
      if (new_position < (uint16_t)(THIS_RAW_POSITION + THIS_DIR_CHG_POS))
      {
        THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
        return TSL_STATUS_ERROR;
      }
      else
      {
        THIS_COUNTER_DIR--;
        if (!THIS_COUNTER_DIR)
        {
          THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
          THIS_DIRECTION = TSL_FALSE;  // New direction accepted: clockwise.
        }
        else
        {
          return TSL_STATUS_ERROR;
        }
      }
    }

    // Check position overflow from 0xFF to 0x00 to be filtered !
    if ((new_position + DIRECTION_CHANGE_MAX_DISPLACEMENT) < THIS_RAW_POSITION)
    {
      if ((new_position + DIRECTION_CHANGE_TOTAL_STEPS) < (uint16_t)(THIS_RAW_POSITION + THIS_DIR_CHG_POS))
      {
        THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
        return TSL_STATUS_ERROR;
      }
      else
      {
        THIS_COUNTER_DIR--;
        if (!THIS_COUNTER_DIR)
        {
          THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
          THIS_DIRECTION = TSL_FALSE;  // New direction accepted: clockwise.
        }
        else
        {
          return TSL_STATUS_ERROR;
        }
      }
    }

  }
  else // Clockwise direction... DEFAULT SETTING !
  {

    // Check Direction changed and Position overflow from 0xFF to 0x00 not realized !
    if (((TSL_tPosition_T)new_position < THIS_RAW_POSITION) && ((THIS_RAW_POSITION - (TSL_tPosition_T)new_position) < DIRECTION_CHANGE_MAX_DISPLACEMENT))
    {
      if ((new_position + THIS_DIR_CHG_POS) > THIS_RAW_POSITION)
      {
        THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
        return TSL_STATUS_ERROR;
      }
      else
      {
        THIS_COUNTER_DIR--;
        if (!THIS_COUNTER_DIR)
        {
          THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
          THIS_DIRECTION = TSL_TRUE;  // New direction accepted: anticlockwise.
        }
        else
        {
          return TSL_STATUS_ERROR;
        }
      }
    }

    // Check position overflow from 0x00 to 0xFF to be filtered !
    if (new_position > (uint16_t)(THIS_RAW_POSITION + DIRECTION_CHANGE_MAX_DISPLACEMENT))
    {
      if ((new_position + THIS_DIR_CHG_POS) > (uint16_t)(THIS_RAW_POSITION + DIRECTION_CHANGE_TOTAL_STEPS))
      {
        THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
        return TSL_STATUS_ERROR;
      }
      else
      {
        THIS_COUNTER_DIR--;
        if (!THIS_COUNTER_DIR)
        {
          THIS_COUNTER_DIR = THIS_COUNTER_DEB_DIRECTION;
          THIS_DIRECTION = TSL_TRUE;  // New direction accepted: anticlockwise.
        }
        else
        {
          return TSL_STATUS_ERROR;
        }
      }
    }

  }

  //----------------------------------------------------------------------------
  // Final result...
  //----------------------------------------------------------------------------

  // The Raw Position is always updated
  // The Position is updated only if different from the previous one

  THIS_RAW_POSITION = (TSL_tPosition_T)new_position;

  u_new_position = (TSL_tPosition_T)((TSL_tPosition_T)new_position >> (RESOLUTION_CALCULATION - THIS_RESOLUTION));

  if (THIS_POSITION == u_new_position)
  {
    return TSL_STATUS_ERROR;
  }
  else
  {
    THIS_POSITION = u_new_position;
    THIS_POSCHANGE = TSL_STATE_CHANGED;
    return TSL_STATUS_OK;
  }

}


//==============================================================================
// Utility functions
//==============================================================================

/**
  * @brief  Go in Calibration state
  * @param[in] delay Delay before calibration starts (stabilization of noise filter)
  * @retval None
  */
void TSL_linrot_SetStateCalibration(TSL_tCounter_T delay)
{
  THIS_STATEID = TSL_STATEID_CALIB;
  THIS_CHANGE = TSL_STATE_CHANGED;
  TSL_linrot_ProcessCh_All_SetStatus(TSL_OBJ_STATUS_ON);

  switch (TSL_Params.NbCalibSamples)
  {
    case 4:
      CalibDiv = 2;
      break;
    case 16:
      CalibDiv = 4;
      break;
    default:
      TSL_Params.NbCalibSamples =  8;
      CalibDiv = 3;
      break;
  }

  // If a noise filter is used, the counter must be initialized to a value
  // different from 0 in order to stabilize the filter.
  THIS_COUNTER_DEB = (TSL_tCounter_T)(delay + (TSL_tCounter_T)TSL_Params.NbCalibSamples);
  TSL_linrot_ProcessCh_All_ClearRef();
}


/**
  * @brief  Go in Off state with sensor "off"
  * @param  None
  * @retval None
  */
void TSL_linrot_SetStateOff(void)
{
  THIS_STATEID = TSL_STATEID_OFF;
  THIS_CHANGE = TSL_STATE_CHANGED;
  TSL_linrot_ProcessCh_All_SetStatus(TSL_OBJ_STATUS_OFF);
}


/**
  * @brief  Go in Off state with sensor in "Burst mode only"
  * @param  None
  * @retval None
  */
void TSL_linrot_SetStateBurstOnly(void)
{
  THIS_STATEID = TSL_STATEID_OFF;
  THIS_CHANGE = TSL_STATE_CHANGED;
  TSL_linrot_ProcessCh_All_SetStatus(TSL_OBJ_STATUS_BURST_ONLY);
}


/**
  * @brief  Return the current state identifier
  * @param  None
  * @retval State id
  */
TSL_StateId_enum_T TSL_linrot_GetStateId(void)
{
  return(THIS_STATEID);
}


/**
  * @brief  Return the current state mask
  * @param  None
  * @retval State mask
  */
TSL_StateMask_enum_T TSL_linrot_GetStateMask(void)
{
  TSL_StateMask_enum_T state_mask = TSL_STATEMASK_UNKNOWN;

#if TSLPRM_TOTAL_LINROTS > 0
  if ((TSL_Globals.This_Obj->Type == TSL_OBJ_LINEAR) ||
      (TSL_Globals.This_Obj->Type == TSL_OBJ_ROTARY))
  {
    state_mask = TSL_Globals.This_LinRot->p_SM[THIS_STATEID].StateMask;
  }
#endif

#if TSLPRM_TOTAL_LINROTS_B > 0
  if ((TSL_Globals.This_Obj->Type == TSL_OBJ_LINEARB) ||
      (TSL_Globals.This_Obj->Type == TSL_OBJ_ROTARYB))
  {
    state_mask = TSL_Params.p_LinRotSM[THIS_STATEID].StateMask;
  }
#endif

  return state_mask;
}


/**
  * @brief  Return the Change flag
  * @param  None
  * @retval Change flag status
  */
TSL_tNb_T TSL_linrot_IsChanged(void)
{
  return(THIS_CHANGE);
}


//==============================================================================
// State machine functions
//==============================================================================

#if TSLPRM_USE_PROX > 0
/**
  * @brief  Debounce Release processing (previous state = Proximity)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebReleaseProxStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_PROX; // Go back to the previous state
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
    {
      THIS_STATEID = TSL_STATEID_PROX; // Go back to the previous state
    }
    else
    {
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
      // else stay in Debounce Release
    }
  }
}
#endif // if TSLPRM_USE_PROX > 0


/**
  * @brief  Debounce Release processing (previous state = Detect)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebReleaseDetectStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_DETECT; // Go back to the previous state
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_DETECTOUT_TH, 1) == TSL_STATUS_OK)
    {
      THIS_STATEID = TSL_STATEID_DETECT;
    }
    else
    {
#if TSLPRM_USE_PROX > 0
      if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        return;
      }
#endif
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
      // else stay in Debounce Release
    }
  }
}


/**
  * @brief  Debounce Release processing (previous state = Touch)
  * Same as Debounce Release Detect processing
  * @param  None
  * @retval None
  */
void TSL_linrot_DebReleaseTouchStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_TOUCH; // Go back to the previous state
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_DETECTOUT_TH, 1) == TSL_STATUS_OK)
    {
      THIS_STATEID = TSL_STATEID_TOUCH;
    }
    else
    {
#if TSLPRM_USE_PROX > 0
      if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        return;
      }
#endif
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
      // else stay in Debounce Release
    }
  }
}


/**
  * @brief  Release state processing
  * @param  None
  * @retval None
  */
void TSL_linrot_ReleaseStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_COUNTER_DEB = THIS_COUNTER_DEB_ERROR;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_ERROR;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_ERROR_RELEASE;
    }
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_DETECTIN_TH, 1) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_DETECT;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_DETECT;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_DETECT;
      }
      return;
    }

#if TSLPRM_USE_PROX > 0
    if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_PROXIN_TH, 0) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_PROX;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_PROX;
      }
      return;
    }
#endif

    // Check delta for re-calibration
    if (TSL_linrot_ProcessCh_One_DeltaBelowEquMinus(THIS_CALIB_TH, 1) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_CALIB;
      if (THIS_COUNTER_DEB == 0)
      {
        TSL_linrot_SetStateCalibration(0);
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_CALIB;
      }
    }
  }
}


/**
  * @brief  Debounce Calibration processing (previous state = Release)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebCalibrationStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_RELEASE; // Go back to the previous state
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaBelowEquMinus(THIS_CALIB_TH, 1) == TSL_STATUS_OK) // Still below recalibration threshold
    {
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        TSL_linrot_SetStateCalibration(0);
      }
      // else stay in Debounce Calibration
    }
    else // Go back to previous state
    {
      THIS_STATEID = TSL_STATEID_RELEASE;
    }
  }
}


/**
  * @brief  Calibration state processing
  * @param  None
  * @retval None
  */
void TSL_linrot_CalibrationStateProcess(void)
{
  TSL_tMeas_T new_meas;
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch;

#if TSLPRM_CALIB_DELAY > 0
  // Noise filter stabilization time
  if (THIS_COUNTER_DEB > (TSL_tCounter_T)TSL_Params.NbCalibSamples)
  {
    THIS_COUNTER_DEB--;
    return; // Skip the sample
  }
#endif

  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_COUNTER_DEB = THIS_COUNTER_DEB_ERROR;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_ERROR;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_ERROR_CALIB;
    }
  }
  else // Acquisition is OK or has NOISE
  {
    // Process all channels
    p_Ch = TSL_Globals.This_LinRot->p_ChD;

    for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
    {

      // Get the new measure or Calculate it
#if TSLPRM_USE_MEAS > 0
      new_meas = p_Ch->Meas;
#else // Calculate it
      new_meas = TSL_acq_ComputeMeas(p_Ch->Ref, p_Ch->Delta);
#endif

      // Verify the first Reference value
      if (THIS_COUNTER_DEB == (TSL_tCounter_T)TSL_Params.NbCalibSamples)
      {
        if (TSL_acq_TestFirstReferenceIsValid(p_Ch, new_meas))
        {
          p_Ch->Ref = new_meas;
        }
        else
        {
          p_Ch->Ref = 0;
          return;
        }
      }
      else
      {
        // Add the measure in temporary Reference
        p_Ch->Ref += new_meas;

        // Check reference overflow
        if (p_Ch->Ref < new_meas)
        {
          p_Ch->Ref = 0; // Suppress the bad reference
          THIS_STATEID = TSL_STATEID_ERROR;
          return;
        }
      }

      p_Ch++; // Next channel
    }

    // Check that we have all the needed measurements
    if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
    if (THIS_COUNTER_DEB == 0)
    {
      // Process all channels
      p_Ch = TSL_Globals.This_LinRot->p_ChD;
      for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
      {
        // Divide temporary Reference by the number of samples
        p_Ch->Ref >>= CalibDiv;
        p_Ch->RefRest = 0;
        p_Ch->Delta = 0;
        p_Ch++; // Next channel
      }
      THIS_STATEID = TSL_STATEID_RELEASE;
    }
  }
}


#if TSLPRM_USE_PROX > 0
/**
  * @brief  Debounce Proximity processing (previous state = Release)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebProxStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_RELEASE;
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_DETECTIN_TH, 1) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_DETECT;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_DETECT;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_DETECT;
      }
      return;
    }

    if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_PROXIN_TH, 0) == TSL_STATUS_OK)
    {
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      // else stay in Debounce Proximity
    }
    else
    {
      THIS_STATEID = TSL_STATEID_RELEASE;
    }
  }
}
#endif


#if TSLPRM_USE_PROX > 0
/**
  * @brief  Debounce Proximity processing (previous state = Detect)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebProxDetectStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_DETECT;
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_DETECTOUT_TH, 1) == TSL_STATUS_OK)
    {
      THIS_STATEID = TSL_STATEID_DETECT;
      return;
    }

    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
    {
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      // else stay in Debounce Proximity
    }
    else
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_RELEASE;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_RELEASE_DETECT;
      }
    }
  }
}
#endif


#if TSLPRM_USE_PROX > 0
/**
  * @brief  Debounce Proximity processing (previous state = Touch)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebProxTouchStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_TOUCH;
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_DETECTOUT_TH, 1) == TSL_STATUS_OK)
    {
      THIS_STATEID = TSL_STATEID_TOUCH;
      return;
    }

    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
    {
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      // else stay in Debounce Proximity
    }
    else
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_RELEASE;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_RELEASE_TOUCH;
      }
    }
  }
}
#endif


#if TSLPRM_USE_PROX > 0
/**
  * @brief  Proximity state processing
  * @param  None
  * @retval None
  */
void TSL_linrot_ProxStateProcess(void)
{
#if TSLPRM_DTO > 0
  TSL_tTick_sec_T tick_detected;
#endif

  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_COUNTER_DEB = THIS_COUNTER_DEB_ERROR;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_ERROR;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_ERROR_PROX;
    }
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_DETECTIN_TH, 1) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_DETECT;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_DETECT;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_DETECT;
      }
      return;
    }

    if (TSL_linrot_ProcessCh_All_DeltaBelowEqu(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_RELEASE;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_RELEASE_PROX;
      }
      return;
    }

    // Stay in Proximity state
#if TSLPRM_DTO > 0
    //------------------------------------
    // Detection Time Out (DTO) processing
    //------------------------------------
    if ((TSL_Params.DTO > 1) && (TSL_Params.DTO < 64))
    {
      tick_detected = THIS_COUNTER_DTO; // Get the detected time previously saved
      // Enter in calibration state if the DTO duration has elapsed
      if (TSL_tim_CheckDelay_sec(TSL_Params.DTO, &tick_detected) == TSL_STATUS_OK)
      {
        TSL_linrot_SetStateCalibration(0);
      }
    }
#endif

  }
}
#endif


/**
  * @brief  Debounce Detect processing (previous state = Release or Proximity)
  * @param  None
  * @retval None
  */
void TSL_linrot_DebDetectStateProcess(void)
{
  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_STATEID = TSL_STATEID_RELEASE;
  }
  else // Acquisition is OK or has NOISE
  {
    if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_DETECTIN_TH, 1) == TSL_STATUS_OK)
    {
      if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_DETECT;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      // else stay in Debounce Detect
    }
    else
    {
#if TSLPRM_USE_PROX > 0
      if (TSL_linrot_ProcessCh_One_DeltaAboveEqu(THIS_PROXIN_TH, 0) == TSL_STATUS_OK)
      {
        THIS_COUNTER_DEB = THIS_COUNTER_DEB_PROX;
        if (THIS_COUNTER_DEB == 0)
        {
          THIS_STATEID = TSL_STATEID_PROX;
          DTO_GET_TIME; // Take current time for DTO processing
        }
        else
        {
          THIS_STATEID = TSL_STATEID_DEB_PROX;
        }
      }
      else
      {
        THIS_STATEID = TSL_STATEID_RELEASE;
      }
#else
      THIS_STATEID = TSL_STATEID_RELEASE;
#endif
    }
  }
}


/**
  * @brief  Detect state processing
  * @param  None
  * @retval None
  */
void TSL_linrot_DetectStateProcess(void)
{
#if TSLPRM_DTO > 0
  TSL_Status_enum_T pos_sts;
  TSL_tTick_sec_T tick_detected;
#endif

  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_COUNTER_DEB = THIS_COUNTER_DEB_ERROR;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_ERROR;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_ERROR_DETECT;
    }
  }
  else // Acquisition is OK or has NOISE
  {

    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_DETECTOUT_TH, 1) == TSL_STATUS_OK)
    {
      //-------------------
      // Calculate position
      //-------------------
      if ((THIS_OBJ_TYPE == TSL_OBJ_LINEAR) || (THIS_OBJ_TYPE == TSL_OBJ_ROTARY))
      {
        // Call the specific method
#if TSLPRM_DTO > 0
        pos_sts = TSL_Globals.This_LinRot->p_Methods->CalcPosition();
#else
        TSL_Globals.This_LinRot->p_Methods->CalcPosition();
#endif
      }
      else // TSL_OBJ_LINEARB or TSL_OBJ_ROTARYB
      {
        // Call the default method
#if TSLPRM_DTO > 0
        pos_sts = TSL_Params.p_LinRotMT->CalcPosition();
#else
        TSL_Params.p_LinRotMT->CalcPosition();
#endif
      }
#if TSLPRM_DTO > 0
      //------------------------------------
      // Detection Time Out (DTO) processing
      // Only if the Position has NOT changed
      //-------------------------------------
      if (pos_sts == TSL_STATUS_OK)
      {
        DTO_GET_TIME; // Take current time
      }
      else
      {
        if ((TSL_Params.DTO > 1) && (TSL_Params.DTO < 64))
        {
          tick_detected = THIS_COUNTER_DTO; // Get the detected time previously saved
          // Enter in calibration state if the DTO duration has elapsed
          if (TSL_tim_CheckDelay_sec(TSL_Params.DTO, &tick_detected) == TSL_STATUS_OK)
          {
            TSL_linrot_SetStateCalibration(0);
          }
        }
      }
#endif
      return; // Normal operation, stay in Detect state
    }

#if TSLPRM_USE_PROX > 0
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_PROX;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_PROX_DETECT;
      }
      return;
    }
#endif

    THIS_COUNTER_DEB = THIS_COUNTER_DEB_RELEASE;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_RELEASE;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_RELEASE_DETECT;
    }

  }
}


/**
  * @brief  Touch state processing
  * Same as Detect state
  * @param  None
  * @retval None
  */
void TSL_linrot_TouchStateProcess(void)
{
#if TSLPRM_DTO > 0
  TSL_Status_enum_T pos_sts;
  TSL_tTick_sec_T tick_detected;
#endif

  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    THIS_COUNTER_DEB = THIS_COUNTER_DEB_ERROR;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_ERROR;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_ERROR_TOUCH;
    }
  }
  else // Acquisition is OK or has NOISE
  {

    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_DETECTOUT_TH, 1) == TSL_STATUS_OK)
    {
      //-------------------
      // Calculate position
      //-------------------
      if ((THIS_OBJ_TYPE == TSL_OBJ_LINEAR) || (THIS_OBJ_TYPE == TSL_OBJ_ROTARY))
      {
        // Call the specific method
#if TSLPRM_DTO > 0
        pos_sts = TSL_Globals.This_LinRot->p_Methods->CalcPosition();
#else
        TSL_Globals.This_LinRot->p_Methods->CalcPosition();
#endif
      }
      else // TSL_OBJ_LINEARB or TSL_OBJ_ROTARYB
      {
        // Call the default method
#if TSLPRM_DTO > 0
        pos_sts = TSL_Params.p_LinRotMT->CalcPosition();
#else
        TSL_Params.p_LinRotMT->CalcPosition();
#endif
      }
#if TSLPRM_DTO > 0
      //------------------------------------
      // Detection Time Out (DTO) processing
      // Only if the Position has NOT changed
      //-------------------------------------
      if (pos_sts == TSL_STATUS_OK)
      {
        DTO_GET_TIME; // Take current time
      }
      else
      {
        if ((TSL_Params.DTO > 1) && (TSL_Params.DTO < 64))
        {
          tick_detected = THIS_COUNTER_DTO; // Get the detected time previously saved
          // Enter in calibration state if the DTO duration has elapsed
          if (TSL_tim_CheckDelay_sec(TSL_Params.DTO, &tick_detected) == TSL_STATUS_OK)
          {
            TSL_linrot_SetStateCalibration(0);
          }
        }
      }
#endif
      return; // Normal operation, stay in Touch state
    }

#if TSLPRM_USE_PROX > 0
    if (TSL_linrot_ProcessCh_One_DeltaAbove(THIS_PROXOUT_TH, 0) == TSL_STATUS_OK)
    {
      THIS_COUNTER_DEB = THIS_COUNTER_DEB_PROX;
      if (THIS_COUNTER_DEB == 0)
      {
        THIS_STATEID = TSL_STATEID_PROX;
        DTO_GET_TIME; // Take current time for DTO processing
      }
      else
      {
        THIS_STATEID = TSL_STATEID_DEB_PROX_TOUCH;
      }
      return;
    }
#endif

    THIS_COUNTER_DEB = THIS_COUNTER_DEB_RELEASE;
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_RELEASE;
    }
    else
    {
      THIS_STATEID = TSL_STATEID_DEB_RELEASE_TOUCH;
    }

  }
}


/**
  * @brief  Debounce error state processing
  * @param  None
  * @retval None
  */
void TSL_linrot_DebErrorStateProcess(void)
{
  volatile TSL_StateMask_enum_T mask;

  if (TSL_linrot_ProcessCh_One_AcqStatusError() == TSL_STATUS_OK) // Acquisition error (min or max)
  {
    if (THIS_COUNTER_DEB > 0) {THIS_COUNTER_DEB--;}
    if (THIS_COUNTER_DEB == 0)
    {
      THIS_STATEID = TSL_STATEID_ERROR;
    }
  }
  else // Acquisition is OK or has NOISE
  {
    // Get state mask
    mask = TSL_linrot_GetStateMask();
    // Mask Error and Debounce bits
#ifdef _RAISONANCE_
    mask &= ~(TSL_STATE_DEBOUNCE_BIT_MASK | TSL_STATE_ERROR_BIT_MASK);
#else
    mask &= (TSL_StateMask_enum_T)(~(TSL_STATE_DEBOUNCE_BIT_MASK | TSL_STATE_ERROR_BIT_MASK));
#endif
    // Go back to the previous state
    switch (mask)
    {
      case TSL_STATEMASK_RELEASE :
        THIS_STATEID = TSL_STATEID_RELEASE;
        break;
      case TSL_STATEMASK_PROX :
        THIS_STATEID = TSL_STATEID_PROX;
        break;
      case TSL_STATEMASK_DETECT :
        THIS_STATEID = TSL_STATEID_DETECT;
        break;
      case TSL_STATEMASK_TOUCH :
        THIS_STATEID = TSL_STATEID_TOUCH;
        break;
      default:
        TSL_linrot_SetStateCalibration(0);
        break;
    }
  }
}


//==============================================================================
// Private functions
//==============================================================================

/**
  * @brief  Get the current time in second and affect it to the DTO counter (Private)
  * @param  None
  * @retval None
  */
void TSL_linrot_DTOGetTime(void)
{
  disableInterrupts();
  THIS_COUNTER_DTO = (TSL_tCounter_T)TSL_Globals.Tick_sec;
  enableInterrupts();
}


/**
  * @brief  Set all channels status to ON, OFF or BURST ONLY
  * @param  sts  Channel status
  * @retval None
  */
void TSL_linrot_ProcessCh_All_SetStatus(TSL_ObjStatus_enum_T sts)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  // Init channels status
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {
    p_Ch->Flags.ObjStatus = sts;
    p_Ch++;
  }
}


/**
  * @brief  Check if at least one channel has a data ready
  * @param  None
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DataReady(void)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  TSL_Status_enum_T retval = TSL_STATUS_ERROR;
  // Return OK if at least one channel has a data ready
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {
    if (p_Ch->Flags.DataReady == TSL_DATA_READY)
    {
      p_Ch->Flags.DataReady = TSL_DATA_NOT_READY; // The new data is processed
      retval = TSL_STATUS_OK;
    }
    p_Ch++;
  }
  return retval;
}


/**
  * @brief  Check if all channels are equal to the status passed
  * @param  sts  Status to be checked
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_All_AcqStatus(TSL_AcqStatus_enum_T sts)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  // Return OK if ALL channels have the correct acq status
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {
    if (p_Ch->Flags.AcqStatus != sts)
    {
      return TSL_STATUS_ERROR;
    }
    p_Ch++;
  }
  return TSL_STATUS_OK;
}


/**
  * @brief  Check if at least one channel is in error
  * @param  None
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_One_AcqStatusError(void)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  // Return OK if at least one channel is in acquisition error min or max
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {
    if (p_Ch->Flags.AcqStatus & TSL_ACQ_STATUS_ERROR_MASK)
    {
      return TSL_STATUS_OK;
    }
    p_Ch++;
  }
  return TSL_STATUS_ERROR;
}


/**
  * @brief  Check if at least one channel is below or equal a threshold (inverted)
  * @param  th Threshold
  * @param  coeff Enable or Disable the multiplier coefficient on threshold
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DeltaBelowEquMinus(TSL_tThreshold_T th, TSL_tIndex_T coeff)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  TSL_tDelta_T norm_delta;

#if TSLPRM_COEFF_TH > 0
  uint16_t lth;
  if (coeff)
  {
    lth = (uint16_t)((uint16_t)th << TSLPRM_COEFF_TH);
  }
  else
  {
    lth = th;
  }
#endif
  
  // Return OK if at least one channel is below or equal the threshold
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {

#if TSLPRM_LINROT_USE_NORMDELTA > 0
    norm_delta = TSL_linrot_NormDelta(p_Ch, idx); // Normalize the Delta
#else
    norm_delta = p_Ch->Delta; // Take only the Delta
#endif

#if TSLPRM_COEFF_TH > 0
    if (norm_delta <= -lth) // Warning!!! The threshold is inverted
#else
    if (norm_delta <= -th) // Warning!!! The threshold is inverted
#endif
    {
      return TSL_STATUS_OK;
    }
    
    p_Ch++;
  }
  
  return TSL_STATUS_ERROR;
}


/**
  * @brief  Check if at least one channel is above or equal a threshold
  * @param  th Threshold
  * @param  coeff Enable or Disable the multiplier coefficient on threshold
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DeltaAboveEqu(TSL_tThreshold_T th, TSL_tIndex_T coeff)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  TSL_tDelta_T norm_delta;

#if TSLPRM_COEFF_TH > 0
  uint16_t lth;
  if (coeff)
  {
    lth = (uint16_t)((uint16_t)th << TSLPRM_COEFF_TH);
  }
  else
  {
    lth = th;
  }
#endif

  // Return OK if at least one channel is above or equal the threshold
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {

#if TSLPRM_LINROT_USE_NORMDELTA > 0
    norm_delta = TSL_linrot_NormDelta(p_Ch, idx); // Normalize the Delta
#else
    norm_delta = p_Ch->Delta; // Take only the Delta
#endif

#if TSLPRM_COEFF_TH > 0
    if (norm_delta >= lth)
#else
    if (norm_delta >= th)
#endif
    {
#if TSLPRM_COEFF_TH > 0
      if (norm_delta < 0)
      {
        p_Ch++;
        continue;
      }
#endif
      return TSL_STATUS_OK;
    }
    p_Ch++;
  }
  return TSL_STATUS_ERROR;
}


/**
  * @brief  Check if at least one channel is stricly above a threshold
  * @param  th Threshold
  * @param  coeff Enable or Disable the multiplier coefficient on threshold
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_One_DeltaAbove(TSL_tThreshold_T th, TSL_tIndex_T coeff)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  TSL_tDelta_T norm_delta;

#if TSLPRM_COEFF_TH > 0
  uint16_t lth;
  if (coeff)
  {
    lth = (uint16_t)((uint16_t)th << TSLPRM_COEFF_TH);
  }
  else
  {
    lth = th;
  }
#endif

  // Return OK if at least one channel is above the threshold
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {

#if TSLPRM_LINROT_USE_NORMDELTA > 0
    norm_delta = TSL_linrot_NormDelta(p_Ch, idx); // Normalize the Delta
#else
    norm_delta = p_Ch->Delta; // Take only the Delta
#endif

#if TSLPRM_COEFF_TH > 0
    if (norm_delta > lth)
#else
    if (norm_delta > th)
#endif
    {
#if TSLPRM_COEFF_TH > 0
      if (norm_delta < 0)
      {
        p_Ch++;
        continue;
      }
#endif
      return TSL_STATUS_OK;
    }
    p_Ch++;
  }
  return TSL_STATUS_ERROR;
}


/**
  * @brief  Check if all channels are below or equal a threshold
  * @param  th Threshold
  * @param  coeff Enable or Disable the multiplier coefficient on threshold
  * @retval Status
  */
TSL_Status_enum_T TSL_linrot_ProcessCh_All_DeltaBelowEqu(TSL_tThreshold_T th, TSL_tIndex_T coeff)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  TSL_tDelta_T norm_delta;

#if TSLPRM_COEFF_TH > 0
  uint16_t lth;
  if (coeff)
  {
    lth = (uint16_t)((uint16_t)th << TSLPRM_COEFF_TH);
  }
  else
  {
    lth = th;
  }
#endif

  // Return OK if ALL channels are below or equal the threshold
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {

#if TSLPRM_LINROT_USE_NORMDELTA > 0
    norm_delta = TSL_linrot_NormDelta(p_Ch, idx); // Normalize the Delta
#else
    norm_delta = p_Ch->Delta; // Take only the Delta
#endif

#if TSLPRM_COEFF_TH > 0
    if (norm_delta > lth)
#else
    if (norm_delta > th)
#endif
    {
#if TSLPRM_COEFF_TH > 0
      if (norm_delta < 0)
      {
        p_Ch++;
        continue;
      }
#endif
      return TSL_STATUS_ERROR;
    }
    p_Ch++;
  }
  return TSL_STATUS_OK;
}


/**
  * @brief  Clear the Reference and ReferenceRest for all channels
  * @param  None
  * @retval None
  */
void TSL_linrot_ProcessCh_All_ClearRef(void)
{
  TSL_tIndex_T idx;
  TSL_ChannelData_T *p_Ch = TSL_Globals.This_LinRot->p_ChD;
  for (idx = 0; idx < THIS_NB_CHANNELS; idx++)
  {
    p_Ch->Ref = 0;
    p_Ch->RefRest = 0;
    p_Ch++;
  }
}


/**
  * @brief  Normalize a Delta value
  * @param  ch Pointer to the current channel
  * @param  idx Index of the channel
  * @retval Normalized Delta value
  */
TSL_tDelta_T TSL_linrot_NormDelta(TSL_ChannelData_T *ch, TSL_tIndex_T idx)
{
  uint32_t tmpdelta = ch->Delta;

  // Apply coefficient
  if (TSL_Globals.This_LinRot->p_DeltaCoeff[idx] != 0x0100)
  {
    tmpdelta = (uint32_t)(tmpdelta * TSL_Globals.This_LinRot->p_DeltaCoeff[idx]);
    tmpdelta = tmpdelta >> (uint8_t)8;
  }

  return (TSL_tDelta_T)tmpdelta;
}

#endif
// #if TSLPRM_TOTAL_LNRTS > 0

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