/**
  ******************************************************************************
  * @file    Project/STM32L0_Internal_RC_Oscillators_Calibration/Src/hsi16.c
  * @author  MCD Application Team
  * @version V0.1.0
  * @date    17-December-2014
  * @brief   This file provides all the HSI measurement and calibration firmware functions.
  ******************************************************************************
  * @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 "hsi16.h"
#include <stdio.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define HSI16_MEASURE_FREQUENCY_TABLE

// Timer related Defines
#define CAPTURE_START        			((uint32_t) 0x00000001)
#define CAPTURE_ONGOING      			((uint32_t) 0x00000002)
#define CAPTURE_COMPLETED    			((uint32_t) 0x00000003)
#define CAPTURE_READY_FOR_NEW 			((uint32_t) 0x00000004)

#define __TIMx_CLK_ENABLE()  			__HAL_RCC_TIM16_CLK_ENABLE()
#define	TIMx 					TIM16
#define	TIM_CHANNEL_y				TIM_CHANNEL_1
#define	HAL_TIM_ACTIVE_CHANNEL_y                HAL_TIM_ACTIVE_CHANNEL_1
#define	TIM_TIMx_GPIO				TIM16_TI1_GPIO
#define	TIM_TIMx_LSE				TIM_TIM16_TI1_LSE
#define	TIM_TIMx_MCO				TIM16_TI1_MCO
#define	TIMx_IRQn				TIM16_IRQn

#define INITIAL_ERROR				((uint32_t)99999000)

/* Exported macro ------------------------------------------------------------*/
#define __HAL_GET_TIM_PRESCALER(__HANDLE__)     ((__HANDLE__)->Instance->PSC)
#define ABS_RETURN(x)         			((x < 0) ? (-x) : (x))

#define HSI16_TIMx_COUNTER_PRESCALER            ((uint32_t)0)
/* The signal in input capture is divided by 8 */
#define HSI16_TIMx_IC_DIVIDER                   TIM_ICPSC_DIV8

/* The LSE is divided by 8 => LSE/8 = 32768/8 = 4096 */
#define REFERENCE_FREQUENCY                     ((uint32_t)4096) /*!< The reference frequency value in Hz */

/* Number of measurements in the loop */
#define HSI16_NUMBER_OF_LOOPS                   ((uint32_t)10)

/* Timeout to avoid endless loop */
#define HSI16_TIMEOUT          			((uint32_t)0xFFFFFF)
	
/* Get actual trimming settings of HSI16 */	
#define GET_HSI16_TRIMMING_VALUE()              ((RCC->ICSCR & RCC_ICSCR_HSITRIM) >> 8)	

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

TIM_HandleTypeDef       TimHandle; /* Timer handler declaration */

static uint16_t  LSIFrequency = LSI_VALUE;
static uint32_t  __IO CaptureState = 0;
static uint32_t  __IO Capture = 0;
static uint32_t  IC1ReadValue1 = 0, IC1ReadValue2 = 0;

#ifdef HSI16_MEASURE_FREQUENCY_TABLE
int32_t  aFrequenceChangeTable[128]; /* 2^7 positions*/
#endif

/* Private function prototypes -----------------------------------------------*/
void HSI16_TIMx_ConfigForCalibration(void);
void HSI16_RCC_AdjustCalibrationValue(uint8_t InternOsc, uint8_t TrimmingValue);
uint32_t HSI16_FreqMeasure(void);
void HSI16_MeasurementInit(void);

void CLK_ConfigForCalibration(void);
void GPIO_ConfigForCalibration(void);
/* Private functions ---------------------------------------------------------*/

/** @addtogroup STM32L0xx_AN4631
  * @{
  */

/**
  * @brief  Calibrates internal oscillators HSI to the minimum computed error.
  *         The system clock source is checked:
  *           - If HSI oscillator is used as system clock source, HSI is calibrated
  *             and the new HSI value is returned.
  *           - Otherwise function returns 0.
  * @param  None.
  * @retval The optimum computed frequency of HSI oscillator.
  *         Returning 0 means that the system clock source is not HSI.
  */
uint32_t HSI16_CalibrateMinError(void)
{
  uint32_t  measuredfrequency = 0;
  uint32_t  sysclockfrequency = 0;
  uint32_t  optimumfrequency = 0;
  uint32_t  frequencyerror = 0;
  uint32_t  optimumfrequencyerror = INITIAL_ERROR; /* Large value */
  uint32_t  numbersteps = 0;         /* Number of steps: size of trimming bits */
  uint32_t  trimmingvalue = 0;
  uint32_t  optimumcalibrationvalue = 0;

  /* Set measurement environment */
  HSI16_MeasurementInit();

  /* Get system clock frequency */
  sysclockfrequency = HAL_RCC_GetSysClockFreq();

  /* HSI16TRIM is 7-bit length */
  numbersteps = 128; /* number of steps is 2^7 = 128 */

  /* Internal Osc frequency measurement for numbersteps */
  for (trimmingvalue = 0; trimmingvalue < numbersteps; trimmingvalue++)
  {

    /* Set the Intern Osc trimming bits to trimmingvalue */
    HSI16_RCC_AdjustCalibrationValue(__HAL_RCC_GET_SYSCLK_SOURCE(), trimmingvalue);

    /* Get actual frequency value */
    measuredfrequency = HSI16_FreqMeasure();

    /* Compute current frequency error corresponding to the current trimming value:
    measured value is subtracted from the typical one */
    frequencyerror = ABS_RETURN((int32_t) (measuredfrequency - sysclockfrequency));

    /* Get the nearest frequency value to typical one */
    if (optimumfrequencyerror > frequencyerror)
    {
      optimumfrequencyerror = frequencyerror;
      optimumcalibrationvalue = trimmingvalue;
      optimumfrequency = measuredfrequency;
    }

  }

  /* Set trimming bits corresponding to the nearest frequency */
  HSI16_RCC_AdjustCalibrationValue(__HAL_RCC_GET_SYSCLK_SOURCE(), optimumcalibrationvalue);
  /* Return the intern oscillator frequency after calibration */
  printf("calilbration value : %d", optimumcalibrationvalue);
  measuredfrequency = HSI16_FreqMeasure();
  printf(":   frequency : %d\n", measuredfrequency);
  return (optimumfrequency);

}

/**
  * @brief  Calibrates the internal oscillator (HSI only) with the maximum allowed
  *         error value set by user.
  *         If this value was not found, this function sets the oscillator
  *         to default value.
  * @param  MaxAllowedError: maximum absolute value allowed of the HSI frequency
  *                          error given in Hz.
  * @param  Freq: returns value of calibrated frequency
  * @retval ErrorStatus:
  *            - SUCCESS: a frequency error =< MaxAllowedError was found.
  *            - ERROR: a frequency error =< MaxAllowedError was not found.
  */
ErrorStatus HSI16_CalibrateFixedError(uint32_t MaxAllowedError, uint32_t* Freq)
{
  uint32_t  measuredfrequency;
  uint32_t  frequencyerror = 0;
  uint32_t  sysclockfrequency = 0;
  uint32_t  trimmingindex = 0;
  uint32_t  trimmingvalue = 0;
  uint32_t  numbersteps;
  int32_t   sign = 1;
  ErrorStatus calibrationstatus = ERROR;

  /* HSI16TRIM is 7-bit length */
  numbersteps = 128; /* number of steps is 2^7 = 128 */

  /* Set measurement environment */
  HSI16_MeasurementInit();

  /* Get system clock frequency */
  sysclockfrequency = HAL_RCC_GetSysClockFreq();

  /* Start frequency measurement for current trimming value */
  measuredfrequency = 0;

  /* RC Frequency measurement for different values */
  for (trimmingindex = 0; trimmingindex < numbersteps; trimmingindex++)
  {
    /* Compute trimming value */
    trimmingvalue = trimmingvalue + (trimmingindex * sign);
    sign *= (-1);

    /* Set the HSI16TRIM register to trimmingvalue to be ready for measurement */
    __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(trimmingvalue);

    /* Get actual frequency value */
    measuredfrequency = HSI16_FreqMeasure();

    /* Compute current frequency error corresponding to the current trimming value:
       measured value is subtracted from the typical one */
    frequencyerror = ABS_RETURN((int32_t) (measuredfrequency - sysclockfrequency));

    /* Check if frequency error is less or equal to value set by the user */
    if (frequencyerror <= MaxAllowedError)
    {
      calibrationstatus = SUCCESS; /* The calibration has succeed */
      break; /* stop searching and measurements for frequencies */
    }
  }

  /* Save the new HSI value */
  *Freq = measuredfrequency;

  /* If the frequency error set by the user was not found */
  if (calibrationstatus == ERROR)
  {
    /* Set the HSI16TRIM register to default value */
    __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(numbersteps / 2);
  }

  /* Return the calibration status: ERROR or SUCCESS */
  return (calibrationstatus);
}

#ifdef HSI16_MEASURE_FREQUENCY_TABLE
/**
  * @brief  For all possible trimming values change of frequency is measured
  * @retval None.
  */
void HSI16_GetCurve(void)
{
  uint32_t output;
  uint32_t measuredfrequency;
  uint32_t trimmingindex = 0;
  uint32_t trimmingindexorig;
  //uint32_t orig_frequency;
  uint32_t numbersteps;
  uint32_t x;

  /* Set measurement environment */
  HSI16_MeasurementInit();

  /* HSI16TRIM is 7-bit length */
  numbersteps = 128; /* number of steps is 2^7 = 128 */

  /* Keep original values */
  trimmingindexorig = GET_HSI16_TRIMMING_VALUE();
  //orig_frequency = HSI16_FreqMeasure();

  /* RC Frequency measurement for different values */
  for (trimmingindex = 0; trimmingindex < numbersteps; trimmingindex++)
  {
    /* Set the HSI16TRIM register to trimmingvalue to be ready for measurement */
    __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(trimmingindex);
    /* Start measuring Internal Oscillator frequency */
    output = 2;
    while(output == 2)
    {
      output = HSI16_FreqMeasure();
    }
    measuredfrequency = 0;
    measuredfrequency += output;

    /* Compute current frequency error corresponding to the current trimming value:
      measured value is subtracted from the typical one */
    aFrequenceChangeTable[trimmingindex] =   (int32_t)measuredfrequency;
    //aFrequenceChangeTable[trimmingindex] =   ((int32_t)(measuredfrequency - orig_frequency));
    #ifdef PRINT_FREQUENCY_MEASURE_RESULT
    printf(" %3d, %d\n", trimmingindex, aFrequenceChangeTable[trimmingindex]);
    #endif
  }

  /* Set back the original frequency value */
  __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(trimmingindexorig);
}
#endif

#ifdef HSI16_MEASURE_FREQUENCY_TABLE
/**
 * @brief  Adjust calibration value (writing to trimming bits) of selected oscillator.
  * @param  Freq: pointer to an uint32_t variable that will contain the value
  *               of the internal oscillator frequency after calibration.
  * @retval ErrorStatus:
  *            - SUCCESS: successful calibration
  *            - ERROR: if frequency could not be calibrated
  */
ErrorStatus HSI16_CalibrateCurve(uint32_t* Freq)
{

  uint32_t measuredfrequency;
  uint32_t optimumcalibrationvalue;
  uint32_t i;
  uint32_t frequencyerror;
  uint32_t numbersteps = 128;
  uint32_t optimumfrequencyerror = INITIAL_ERROR; /* Large value */
  ErrorStatus returnvalue = ERROR;

  /* HSI16TRIM is 7-bit length */
  numbersteps = 128; /* number of steps is 2^7 = 128 */

  /* Get position */
  measuredfrequency = HSI16_FreqMeasure();

  /* Find the closest difference */
  for (i = 0; i < numbersteps; i++)
  {
    frequencyerror = ABS_RETURN((int32_t) (HSI_VALUE - (int32_t)(measuredfrequency + aFrequenceChangeTable[i])));

    /* Get the nearest frequency value to typical one */
    if (frequencyerror < optimumfrequencyerror)
    {
      optimumfrequencyerror = frequencyerror;
      optimumcalibrationvalue = i;
    }
  }

  if (optimumfrequencyerror != INITIAL_ERROR)
  {
    __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(optimumcalibrationvalue);
    /* Save the HSI measured value */
    *Freq = measuredfrequency + aFrequenceChangeTable[optimumcalibrationvalue];
    returnvalue = SUCCESS;
  }

  return returnvalue;
}
#endif

/**
  * @brief Measures actual value of HSI
  * @param  None.
  * @retval Actual HSI frequency
  */
static uint32_t frequencyMW;
static int32_t loopCounter;

uint32_t HSI16_FreqMeasure(void)
{
  uint32_t  measuredfrequency;
  //uint32_t  timeout = HSI16_TIMEOUT;

  /* Start frequency measurement for current trimming value */

  /* Start measuring Internal Oscillator frequency */
    
  // EDIT ECS:
  // state machine einbauen um Blocken des Programmablaufes durch die while() Schleife zu verhindern
  // state machine ist schon da (globale var "CaptureState")
  
  if(CaptureState == CAPTURE_READY_FOR_NEW)
  {
    CaptureState = CAPTURE_START;

    /* Enable capture 1 interrupt */
    HAL_TIM_IC_Start_IT(&TimHandle, TIM_CHANNEL_y);

    /* Enable the TIMx IRQ channel */
    HAL_NVIC_EnableIRQ(TIMx_IRQn);
    // Return Capture Start
    return CAPTURE_ONGOING;
  }

  else if(CaptureState != CAPTURE_COMPLETED)
  {
    // Return Capture Ongoing
    return CAPTURE_ONGOING;
  }
  /* Wait for end of capture: two consecutive captures */
  
  else if(CaptureState == CAPTURE_COMPLETED)
  {
    
    /* Disable IRQ channel */
    HAL_NVIC_DisableIRQ(TIMx_IRQn);

    /* Disable TIMx */
    HAL_TIM_IC_Stop_IT(&TimHandle, TIM_CHANNEL_y);

    CaptureState = CAPTURE_READY_FOR_NEW;

    if (loopCounter != 0)
    {
        /* Compute the frequency (the Timer prescaler isn't included) */
        frequencyMW += (uint32_t) (REFERENCE_FREQUENCY * Capture);
    }

    if(loopCounter < HSI16_NUMBER_OF_LOOPS)
    {
      /* Increment loop counter */
      loopCounter++;
      return CAPTURE_ONGOING;
    }
    /* END of Measurement */
    else
    {
      measuredfrequency = 0;
      loopCounter = 0;
      /* Compute the average value corresponding the current trimming value */
      measuredfrequency = (uint32_t)((__HAL_GET_TIM_PRESCALER(&TimHandle) + 1) * (frequencyMW / HSI16_NUMBER_OF_LOOPS));
      frequencyMW = 0;
      return measuredfrequency;
    }
  }
  return 0;
}

/**
  * @brief Configures all the necessary peripherals necessary from frequency calibration.
  * @param  None.
  * @retval None.
  */
void HSI16_MeasurementInit(void)
{

  /* Configure the GPIO ports before starting calibration process */
  //GPIO_ConfigForCalibration();

  /* Configure clock before starting calibration process */
  //CLK_ConfigForCalibration();

  /* Configure TIMx before starting calibration process */
  HSI16_TIMx_ConfigForCalibration();
  CaptureState = CAPTURE_READY_FOR_NEW;
}

/**
  * @brief Configures the TIMx in input capture to measure HSI frequency.
  * @param  None.
  * @retval None.
  */
void HSI16_TIMx_ConfigForCalibration(void)
{
  TIM_IC_InitTypeDef      ic_config; /* Timer Input Capture Configuration Structure declaration */

  /* Enable TIMx clock */
  __TIMx_CLK_ENABLE();

  /* Set TIMx instance */
  TimHandle.Instance = TIMx;

  /* Reset TIMx registers */
  HAL_TIM_IC_DeInit(&TimHandle);

  /* Initialize TIMx peripheral as follows:
       + Period = 0xFFFF
       + Prescaler = 0
       + ClockDivision = 0
       + Counter direction = Up
  */
  TimHandle.Init.Period            = 0xFFFF;
  TimHandle.Init.Prescaler         = HSI16_TIMx_COUNTER_PRESCALER;
  TimHandle.Init.ClockDivision     = 0;
  TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
  if (HAL_TIM_IC_Init(&TimHandle) != HAL_OK)
  {
    /* Initialization Error */
    while(1);
  }

  /* Configure the Input Capture of channel y */
  ic_config.ICPolarity  = TIM_ICPOLARITY_RISING;
  ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;
  ic_config.ICPrescaler = HSI16_TIMx_IC_DIVIDER;
  ic_config.ICFilter    = 0;
  if (HAL_TIM_IC_ConfigChannel(&TimHandle, &ic_config, TIM_CHANNEL_y) != HAL_OK)
  {
    /* Configuration Error */
    while(1);
  }

// EDIT ECS START
  // Timer Input Source Selection
  // LSE als Timer Input
  if (HAL_TIMEx_TISelection(&TimHandle, TIM_TIM16_TI1_LSE, TIM_CHANNEL_1) != HAL_OK)
  {
    while(1);
  }
// EDIT ECS END

  /* Configure the NVIC for TIMx */
  HAL_NVIC_SetPriority(TIMx_IRQn, 0, 0);

  /* Disable the TIMx global Interrupt */
  HAL_NVIC_DisableIRQ(TIMx_IRQn);

}


/**
  * @brief  Adjust calibration value (writing to trimming bits) of selected oscillator.
  * @param  InternOsc: Internal Oscillator source: HSI
  * @param  TrimmingValue: calibration value to be written in trimming bits.
  * @retval None.
  */
void HSI16_RCC_AdjustCalibrationValue(uint8_t InternOsc, uint8_t TrimmingValue)
{
  __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(TrimmingValue);
}

/**
  * @brief  Configures LSE to be used as RTC clock source
  * @param  None.
  * @retval None.
  */
void CLK_ConfigForCalibration(void)
{

  /* Enable the LSE OSC */
  __HAL_RCC_LSE_CONFIG(RCC_LSE_ON);

  /* Wait till LSE is ready */
  while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET)
  {}
}


/**
  * @brief  Conversion complete callback in non blocking mode
  * @param  htim : hadc handle
  * @retval None
  */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{

  if ((htim->Channel) == HAL_TIM_ACTIVE_CHANNEL_y)
  {
    if (CaptureState == CAPTURE_START)
    {
      /* Get the 1st Input Capture value */
      IC1ReadValue1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_y);
      //htim->Instance->CNT = 0;
      CaptureState = CAPTURE_ONGOING;
    }
    else if (CaptureState == CAPTURE_ONGOING)
    {
      /* Get the 2nd Input Capture value */
      IC1ReadValue2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_y);

      // Timer interrupt ausschalten sonst treten Fehler auf
      HAL_NVIC_DisableIRQ(TIMx_IRQn);
      HAL_TIM_IC_Stop_IT(&TimHandle, TIM_CHANNEL_y);
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);

      /* Capture computation */
      if (IC1ReadValue2 > IC1ReadValue1)
      {
        Capture = (IC1ReadValue2 - IC1ReadValue1);
        if(REFERENCE_FREQUENCY * Capture < 13000000)
        {
          printf("Value not valid\n");
          printf("Frequency Measured = %d\n", REFERENCE_FREQUENCY * Capture);
          //while(1);
        }
      }
      else if (IC1ReadValue2 < IC1ReadValue1)
      {
        Capture = ((0xFFFF - IC1ReadValue1) + IC1ReadValue2);
        if(REFERENCE_FREQUENCY * Capture < 13000000)
        {
          printf("Value not valid\n");
          printf("Frequency Measured = %d\n", REFERENCE_FREQUENCY * Capture);
        }
      }
      else
      {
        /* If capture values are equal, we have reached the limit of frequency
        measures */
        while(1);
      }

      CaptureState = CAPTURE_COMPLETED;
    }
  }
}

/**
  * @}
  */

void frequencyErrorTest(void)
{   
  uint32_t  HSIFrequencyBeforeCalib, highVal, lowVal;
  highVal = 0;
  lowVal = 20000000;
  HSI16_MeasurementInit();
  while(1)
  {
    HSIFrequencyBeforeCalib = 2;
    while(HSIFrequencyBeforeCalib == 2)
    {
      HSIFrequencyBeforeCalib = HSI16_FreqMeasure();
    }
    
    if(HSIFrequencyBeforeCalib > highVal)
    {
      highVal = HSIFrequencyBeforeCalib;
      printf("highest frequency %d\n",highVal);
      printf("lowest  frequency %d\n",lowVal);
    }
    if(HSIFrequencyBeforeCalib < lowVal)
    {
      lowVal = HSIFrequencyBeforeCalib;
      printf("highest frequency %d\n",highVal);
      printf("lowest  frequency %d\n",lowVal);
    }
  }
}
/******************* (C) COPYRIGHT 2014 STMicroelectronics *****END OF FILE****/
