2015-02-13 12:55 AM
Hi all,
I´m trying to implement a Independent Watchdog functionality on my STM32L0.In HAL description pdf I´ve read the following sentence that give me some doubts on me:STM32L0xx devices provide the capability to measure theLSI frequency (LSI clock connected internally to TIM5 CH4 input capture). The measuredvalue can be used to have an IWDG timeout with an acceptable accuracy.1.- Has anyone the code example to do that?2.- Can I use the LSI clock only to IWDG while I am using the HSE with my application?Best regards2016-12-16 08:32 AM
Hi,
a sample code you can find in ST examples (ex. STM32Cube_FW_F0_V1.7.0/Projects/STM32F042K6-Nucleo/Examples/IWDG/IWDG_Reset/). Basically, you need to configure the timer as input capture, then measure two rising edges and compute the frequency. In the example is it nicely shown. I use Cube generator for the basic code structure so here is generated output:
tim.c
void MX_TIM21_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
htimInstance = TIM21;
htimInit.Prescaler = 0;
htimInit.CounterMode = TIM_COUNTERMODE_UP;
htimInit.Period = 65535;
htimInit.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_IC_Init(&htim21) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim21, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV8;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim21, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIMEx_RemapConfig(&htim21, TIM21_TI1_MCO) != HAL_OK)
{
Error_Handler();
}
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
The interrupt is in msp file.
Computation is done in TIM_IC_CaptureCallback like this
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
uint32_t lsiperiod = 0;
/* Get the Input Capture value */
tmpCC4[uwCaptureNumber++] = HAL_TIM_ReadCapturedValue(&Input_Handle, TIM_CHANNEL_1);
if (uwCaptureNumber >= 2)
{
/* Compute the period length */
lsiperiod = (uint16_t)(0xFFFF - tmpCC4[0] + tmpCC4[1] + 1);
/* Frequency computation */
uwLsiFreq = (uint32_t) SystemCoreClock / lsiperiod;
uwLsiFreq *= 8;
}
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
Required variables for computation are defined as
uint16_t tmpCC4[2] = {0, 0};
__IO uint32_t uwLsiFreq = 0;
__IO uint32_t uwCaptureNumber = 0;�?�?�?
And finally start the timer to measure the frequency
HAL_TIM_IC_Start_IT(&Input_Handle, TIM_CHANNEL_1);
while(uwCaptureNumber < 2);
HAL_TIM_IC_Stop_IT(&Input_Handle, TIM_CHANNEL_1);�?�?�?
2016-12-20 07:09 AM
Hi,
the LSI is the only clock source option for the IWDG, because it has to be independent on the core frequency and its source. Thus it will be working even if the main clock fails. With HSE and timer combination you will measure the LSI frequency very precisely.
2016-12-20 08:26 AM
Just a math tip here, but you can get better precision like this
uint16_t lsiperiod; // 16-bit
/* Compute the period length */
lsiperiod = tmpCC4[1] - tmpCC4[0]; // math wraps itself/* Frequency computation */
uwLsiFreq = (SystemCoreClock * 8) / (uint32_t)lsiperiod;2016-12-20 08:29 AM
With HSE and timer combination you will measure the LSI frequency very precisely.
Agreed, however I seen several ST examples where doing the *8 at the end results in the loss of 3-bits of precision. They'll always be zeros.
For example this SPL code, the multiplication ideally needs to occur before the division when working with integers, though you should watch the scale of the source clock. This is an example where the use of doubles might be more appropriate.
STM32F4xx_DSP_StdPeriph_Lib_V1.6.1\Project\STM32F4xx_StdPeriph_Examples\RTC\RTC_LSI\main.c
/* Get PCLK1 prescaler */
if ((RCC->CFGR & RCC_CFGR_PPRE1) == 0)
{
/* PCLK1 prescaler equal to 1 => TIMCLK = PCLK1 */
return ((RCC_ClockFreq.PCLK1_Frequency / uwPeriodValue) * 8);
}
else
{ /* PCLK1 prescaler different from 1 => TIMCLK = 2 * PCLK1 */
return (((2 * RCC_ClockFreq.PCLK1_Frequency) / uwPeriodValue) * 8) ;
}�?�?�?�?�?�?�?�?�?�?