/** ****************************************************************************** * @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****/