/**
******************************************************************************
* @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
*
*
© COPYRIGHT 2014 STMicroelectronics
*
* 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
/* 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****/