/**
  ******************************************************************************
  * @file    adafruit_802.c
  * @author  MCD Application Team
  * @brief   This file provides set of firmware functions to manage:
  *          - Joystick available on Adafruit 1.8" TFT LCD shield (reference ID 802)
  *
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

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

/** @addtogroup BSP
  * @{
  */

/** @addtogroup ADAFRUIT_802
  * @{
  */

/** @addtogroup ADAFRUIT_802_LOW_LEVEL
  * @{
  */

/** @defgroup ADAFRUIT_802_LOW_LEVEL_Private_Variables LOW LEVEL Private Variables
  * @{
  */
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
static uint32_t IsJoyMspCbValid = 0;
#endif

static ADC_HandleTypeDef hJoyHandle;
/**
  * @}
  */

/** @defgroup ADAFRUIT_802_LOW_LEVEL_Private_FunctionPrototypes LOW LEVEL Private FunctionPrototypes
  * @{
  */
static void JOY1_MspInit(ADC_HandleTypeDef *hadc);
static void JOY1_MspDeInit(ADC_HandleTypeDef *hadc);
/**
  * @}
  */

/** @addtogroup ADAFRUIT_802_LOW_LEVEL_Exported_Functions
  * @{
  */

/**
  * @brief  Configures the joystick.
  * @param  JOY Joystick to be initialized
  * @param  JoyMode Button mode.
  *          This parameter is not used as the ADC pin should be always configured
  *          in analog mode.
  * @param  JoyPins There are no physical pins but voltage levels
  * @retval BSP status
  */
int32_t ADAFRUIT_802_JOY_Init(JOY_TypeDef JOY, JOYMode_TypeDef JoyMode, JOYPin_TypeDef JoyPins)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(JoyPins);
  UNUSED(JoyMode);

  int32_t ret = BSP_ERROR_NONE;

  if(JOY >= JOYn)
  {
    ret = BSP_ERROR_WRONG_PARAM;
  }
  else
  {
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 0)
    /* Init the UART Msp */
    JOY1_MspInit(&hJoyHandle);
#else
    if(IsJoyMspCbValid == 0U)
    {
      if(ADAFRUIT_802_JOY_RegisterDefaultMspCallbacks(JOY) != BSP_ERROR_NONE)
      {
        return BSP_ERROR_MSP_FAILURE;
      }
    }
#endif

    if(MX_ADAFRUIT_802_ADC_Init(&hJoyHandle) != HAL_OK)
    {
      ret = BSP_ERROR_PERIPH_FAILURE;
    }
    else
    {
#if defined(ADC_CALFACT_CALFACT_S)
      /* Run ADC calibration */
#if defined(ADC_CR_ADCALLIN)
      if(HAL_ADCEx_Calibration_Start(&hJoyHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
      {
        ret =  BSP_ERROR_PERIPH_FAILURE;
      }
      else
      {
        if(HAL_ADCEx_Calibration_Start(&hJoyHandle, ADC_CALIB_OFFSET_LINEARITY, ADC_SINGLE_ENDED) != HAL_OK)
        {
          ret =  BSP_ERROR_PERIPH_FAILURE;
        }
      }
#else
      if(HAL_ADCEx_Calibration_Start(&hJoyHandle, ADC_SINGLE_ENDED) != HAL_OK)
      {
        ret =  BSP_ERROR_PERIPH_FAILURE;
      }
#endif
#endif /* defined(ADC_CALFACT_CALFACT_S) */
    }
  }

  return ret;
}

/**
  * @brief  DeInit joystick GPIOs.
  * @param  JOY Joystick to be de-initialized
  * @param  JoyPins There are no physical pins but voltage levels
  * @retval BSP status
  */
int32_t ADAFRUIT_802_JOY_DeInit(JOY_TypeDef JOY, JOYPin_TypeDef JoyPins)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(JoyPins);

  int32_t  ret = BSP_ERROR_NONE;

  /* ADC configuration */
  hJoyHandle.Instance = ADAFRUIT_802_ADCx;

  if(JOY >= JOYn)
  {
    ret = BSP_ERROR_WRONG_PARAM;
  }
  else
  {
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 0)
    JOY1_MspDeInit(&hJoyHandle);
#endif /* (USE_HAL_UART_REGISTER_CALLBACKS == 0) */

    if(HAL_ADC_DeInit(&hJoyHandle) != HAL_OK)
    {
      ret = BSP_ERROR_PERIPH_FAILURE;
    }
  }

  return ret;
}

/**
  * @brief  Returns the Joystick key pressed.
  * @param  JOY Joystick to get its pins' state
  * @retval BSP error code if value is negative or one of the following values:
  *           - JOY_NONE   : 3.3 V / 4095
  *           - JOY_SEL    : 1.055 V / 1308
  *           - JOY_DOWN   : 0.71 V / 88
  *           - JOY_LEFT   : 3.0 V / 3720
  *           - JOY_RIGHT  : 0.595 V / 737
  *           - JOY_UP     : 1.65 V / 2046
  */
int32_t ADAFRUIT_802_JOY_GetState(JOY_TypeDef JOY)
{
  int32_t ret;
  uint32_t  keyconvertedvalue;

  if(JOY >= JOYn)
  {
    ret = BSP_ERROR_WRONG_PARAM;
  }/* Start the conversion process */
  else if(HAL_ADC_Start(&hJoyHandle) != HAL_OK)
  {
    ret = BSP_ERROR_PERIPH_FAILURE;
  }/* Wait for the end of conversion */
  else if(HAL_ADC_PollForConversion(&hJoyHandle, ADAFRUIT_802_ADCx_POLL_TIMEOUT) != HAL_OK)
  {
    ret = BSP_ERROR_PERIPH_FAILURE;
  }/* Check if the continuous conversion of regular channel is finished */
  else if((HAL_ADC_GetState(&hJoyHandle) & HAL_ADC_STATE_REG_EOC) != HAL_ADC_STATE_REG_EOC)
  {
    ret = BSP_ERROR_BUSY;
  }
  else
  {
    /* Get the converted value of regular channel */
    keyconvertedvalue = HAL_ADC_GetValue(&hJoyHandle);

    if((keyconvertedvalue > 2010U) && (keyconvertedvalue < 2500U))
    {
      ret = (int32_t)JOY_UP;
    }
    else if((keyconvertedvalue > 680U) && (keyconvertedvalue < 900U))
    {
      ret = (int32_t)JOY_RIGHT;
    }
    else if((keyconvertedvalue > 1270U) && (keyconvertedvalue < 1500U))
    {
      ret = (int32_t)JOY_SEL;
    }
    else if((keyconvertedvalue > 50U) && (keyconvertedvalue < 200U))
    {
      ret = (int32_t)JOY_DOWN;
    }
    else if((keyconvertedvalue > 3400U) && (keyconvertedvalue < 3760U))
    {
      ret = (int32_t)JOY_LEFT;
    }
    else
    {
      ret = (int32_t)JOY_NONE;
    }
  }

  /* Return the code of the Joystick key pressed */
  return ret;
}

/**
  * @brief  Configures ADC peripheral
  * @param  hadc ADC handle
  * @retval HAL error code
  */
__weak HAL_StatusTypeDef MX_ADAFRUIT_802_ADC_Init(ADC_HandleTypeDef *hadc)
{
  HAL_StatusTypeDef ret = HAL_OK;
  ADC_ChannelConfTypeDef sConfig;

  /* ADC configuration */
  hadc->Instance                   = ADAFRUIT_802_ADCx;
  hadc->Init.ClockPrescaler        = ADAFRUIT_802_ADCx_PRESCALER;
  hadc->Init.Resolution            = ADC_RESOLUTION_12B;
#if defined(ADC_CR2_ALIGN)
  hadc->Init.DataAlign             = ADC_DATAALIGN_RIGHT;
#endif
  hadc->Init.ScanConvMode          = (uint32_t)DISABLE;
#if defined(ADC_EOC_SINGLE_CONV)
  hadc->Init.EOCSelection          = ADC_EOC_SINGLE_CONV;
#endif
  hadc->Init.ContinuousConvMode    = DISABLE;
  hadc->Init.NbrOfConversion       = 1;
  hadc->Init.DiscontinuousConvMode = DISABLE;
#if defined(ADC_CFGR_DISCNUM)
  hadc->Init.NbrOfDiscConversion   = 0;
#endif
  hadc->Init.ExternalTrigConv      = ADC_SOFTWARE_START;
  hadc->Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;
#if defined(ADC_CR2_DMA)
  hadc->Init.DMAContinuousRequests = DISABLE;
#endif
#if defined(ADC_SMPR_SMP1) && defined(ADC_SMPR_SMP2)
  hadc->Init.SamplingTimeCommon1 = ADC_SAMPLETIME_39CYCLES_5;
  hadc->Init.SamplingTimeCommon2 = ADC_SAMPLETIME_39CYCLES_5;
#endif
#if defined(ADC_CFGR2_LFTRIG)
  hadc->Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
#endif

  if (HAL_ADC_Init(hadc) != HAL_OK)
  {
    ret =  HAL_ERROR;
  }
  else
  {
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
    sConfig.Channel      = ADAFRUIT_802_ADCx_CHANNEL;
    sConfig.Rank         = ADAFRUIT_802_ADCx_RANK;
    sConfig.SamplingTime = ADAFRUIT_802_ADCx_SAMPLETIME;
#if defined(ADC_OFR1_OFFSET1)
    sConfig.Offset       = 0;
#endif
#if defined(ADC_SINGLE_ENDED)
    sConfig.SingleDiff   = ADC_SINGLE_ENDED;
#endif
#if defined(ADC_OFFSET_NONE)
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
#endif
    if (HAL_ADC_ConfigChannel(hadc, &sConfig) != HAL_OK)
    {
      ret =  HAL_ERROR;
    }
  }

  return ret;
}

#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
/**
  * @brief Register Default ADCx Bus Msp Callbacks
  * @param  JOY Joystick to be configured.
  *          This parameter can be JOY1
  * @retval BSP status
  */
int32_t ADAFRUIT_802_JOY_RegisterDefaultMspCallbacks(JOY_TypeDef JOY)
{
  int32_t ret = BSP_ERROR_NONE;

  if(JOY >= JOYn)
  {
    ret = BSP_ERROR_WRONG_PARAM;
  }
  else
  {
    __HAL_ADC_RESET_HANDLE_STATE(&hJoyHandle);

    /* Register default MspInit/MspDeInit Callback */
    if(HAL_ADC_RegisterCallback(&hJoyHandle, HAL_ADC_MSPINIT_CB_ID, JOY1_MspInit) != HAL_OK)
    {
      ret = BSP_ERROR_PERIPH_FAILURE;
    }
    else if(HAL_ADC_RegisterCallback(&hJoyHandle, HAL_ADC_MSPDEINIT_CB_ID, JOY1_MspDeInit) != HAL_OK)
    {
      ret = BSP_ERROR_PERIPH_FAILURE;
    }
    else
    {
      IsJoyMspCbValid = 1U;
    }
  }

  /* BSP status */
  return ret;
}

/**
  * @brief Register ADCx Bus Msp Callback registering
  * @param  JOY Joystick to be configured.
  *          This parameter can be JOY1
  * @param Callbacks     pointer to ADCx MspInit/MspDeInit callback functions
  * @retval BSP status
  */
int32_t ADAFRUIT_802_JOY_RegisterMspCallbacks(JOY_TypeDef JOY, ADAFRUIT_802_JOY_Cb_t *Callback)
{
  int32_t ret = BSP_ERROR_NONE;

  if(JOY >= JOYn)
  {
    ret = BSP_ERROR_WRONG_PARAM;
  }
  else
  {
    __HAL_ADC_RESET_HANDLE_STATE(&hJoyHandle);

    /* Register MspInit/MspDeInit Callbacks */
    if(HAL_ADC_RegisterCallback(&hJoyHandle, HAL_ADC_MSPINIT_CB_ID, Callback->pMspInitCb) != HAL_OK)
    {
      ret = BSP_ERROR_PERIPH_FAILURE;
    }
    else if(HAL_ADC_RegisterCallback(&hJoyHandle, HAL_ADC_MSPDEINIT_CB_ID, Callback->pMspDeInitCb) != HAL_OK)
    {
      ret = BSP_ERROR_PERIPH_FAILURE;
    }
    else
    {
      IsJoyMspCbValid = 1U;
    }
  }

  /* BSP status */
  return ret;
}
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */

/**
  * @}
  */

/** @defgroup ADAFRUIT_802_LOW_LEVEL_Private_Functions LOW LEVEL Private Functions
  * @{
  */
/**
  * @brief  Initializes ADC MSP.
  * @param  hadc ADC handle
  * @retval None
  */
static void JOY1_MspInit(ADC_HandleTypeDef *hadc)
{
  GPIO_InitTypeDef  GPIO_InitStruct;
#if defined (RCC_ADCCLKSOURCE_SYSCLK) || defined (RCC_ADC12CLKSOURCE_SYSCLK)
  RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct;
#endif

  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc);

  /*** Configure the GPIOs ***/
  /* Enable GPIO clock */
  ADAFRUIT_802_ADCx_GPIO_CLK_ENABLE();

  /* Configure the selected ADC Channel as analog input */
  GPIO_InitStruct.Pin  = ADAFRUIT_802_ADCx_GPIO_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
#if defined (RCC_ADCCLKSOURCE_SYSCLK) || defined (RCC_ADC12CLKSOURCE_SYSCLK)
  GPIO_InitStruct.Mode  = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
#endif
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(ADAFRUIT_802_ADCx_GPIO_PORT, &GPIO_InitStruct);

  /*** Configure the ADC peripheral ***/
  /* Enable ADC clock */
  ADAFRUIT_802_ADCx_CLK_ENABLE();

#if defined (RCC_ADCCLKSOURCE_SYSCLK)
  /* Configure SYSCLK as source clock for ADC */
  RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  RCC_PeriphCLKInitStruct.AdcClockSelection    = RCC_ADCCLKSOURCE_SYSCLK;
  HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
#elif defined (RCC_ADC12CLKSOURCE_SYSCLK)
  RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
  RCC_PeriphCLKInitStruct.Adc12ClockSelection    = RCC_ADC12CLKSOURCE_SYSCLK;
  HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
#endif
}

/**
  * @brief  DeInitializes ADC MSP.
  * @param  hadc ADC handle
  * @note ADC DeInit does not disable the GPIO clock
  * @retval None
  */
static void JOY1_MspDeInit(ADC_HandleTypeDef *hadc)
{
  GPIO_InitTypeDef  GPIO_InitStruct;

  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc);

  /*** DeInit the ADC peripheral ***/
  /* Disable ADC clock */
  ADAFRUIT_802_ADCx_CLK_DISABLE();

  /* Configure the selected ADC Channel as analog input */
  GPIO_InitStruct.Pin = ADAFRUIT_802_ADCx_GPIO_PIN ;
  HAL_GPIO_DeInit(ADAFRUIT_802_ADCx_GPIO_PORT, GPIO_InitStruct.Pin);
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

