cancel
Showing results for 
Search instead for 
Did you mean: 

TIM21, INPUT CAPTURE mode, System clock at 2Mhz or below caused hardfault?

chai2145
Associate II

Hi ,

 

I am using TIM21 in input capture mode to measure the LSI frequency (~37 kHz). However, when I set the TIM21 clock to 2 MHz or use the MSI clock, it causes a hardware fault when attempting to capture the LSI frequency.

I have reviewed the STM32CubeL0 example code for RTC_LSI, which uses an MSI clock set to 2 MHz to measure the LSI frequency successfully. However, in my case, this setup does not work, and I am not sure why.

 

Thanks! 

STM32CubeL0 example code to measure LSI:

 

/**
  ******************************************************************************
  * @file    RTC/RTC_LSI/Src/main.c
  * @author  MCD Application Team
  * @brief   This sample code shows how to use STM32L0xx RTC HAL API to 
  *          use the LSI clock source auto calibration to get a precise RTC 
  *          clock.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

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

/** @addtogroup STM32L0xx_HAL_Examples
  * @{
  */

/** @addtogroup RTC_LSI
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define WAKEUP_TIMER_ENABLE 0x32F2

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
RTC_HandleTypeDef RtcHandle;
TIM_HandleTypeDef Input_Handle;

uint16_t tmpCCTIM_CHANNEL_1[2] = {0, 0};
__IO uint32_t uwLsiFreq = 0;

__IO uint32_t uwCaptureNumber = 0;
__IO uint32_t uwPeriodValue = 0;

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void RTC_Config(void);
static uint32_t GetLSIFrequency(void);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program
  * @PAram  None
  * @retval None
  */
int main(void)
{
  GPIO_InitTypeDef  GPIO_InitStruct;

  /* STM32L0xx HAL library initialization:
       - Configure the Flash prefetch, Flash preread and Buffer caches
       - Systick timer is configured by default as source of time base, but user 
             can eventually implement his proper time base source (a general purpose 
             timer for example or other time source), keeping in mind that Time base 
             duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and 
             handled in milliseconds basis.
       - Low Level Initialization
     */
  HAL_Init();

  /* Configure the system clock to 2 MHz */
  SystemClock_Config();

  /* Configure LED3 */
  BSP_LED_Init(LED3);

  /* Configure PA.12 (Arduino D2) button */
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT; 
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    
  /* Enable GPIOA clock */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* RTC Configuration -------------------------------------------------------*/
  RTC_Config();

  /* Wait Until PA.12 (Arduino D2) is connected to GND */
  while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_SET)
  {
  }

  /* Wait Until PA.12 (Arduino D2) is released to 3.3V */
  while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_RESET)
  {
  }
  
  /* Get the LSI frequency:  TIM21 is used to measure the LSI frequency */
  uwLsiFreq = GetLSIFrequency();
  
  /* Update the Calendar Configuration with the LSI exact value */
  RtcHandle.Init.HourFormat = RTC_HOURFORMAT_24;
  RtcHandle.Init.AsynchPrediv = 0x7F;
  RtcHandle.Init.SynchPrediv = (uwLsiFreq/128) - 1;
  RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
  RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  
  if(HAL_RTC_Init(&RtcHandle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

  /* Infinite loop */
  while (1)
  {
  }
}

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow : 
  *            System Clock source            = MSI
  *            SYSCLK(Hz)                     = 2000000
  *            HCLK(Hz)                       = 2000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 1
  *            APB2 Prescaler                 = 1
  *            Flash Latency(WS)              = 0
  *            Main regulator output voltage  = Scale3 mode
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  
  /* Enable MSI Oscillator */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
  RCC_OscInitStruct.MSICalibrationValue=0x00;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
  {
    /* Initialization Error */
    while(1); 
  }
  
  /* Select MSI as system clock source and configure the HCLK, PCLK1 and PCLK2 
     clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;  
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;  
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0)!= HAL_OK)
  {
    /* Initialization Error */
    while(1); 
  }
  /* Enable Power Control clock */
  __HAL_RCC_PWR_CLK_ENABLE();
  
  /* The voltage scaling allows optimizing the power consumption when the device is 
     clocked below the maximum system frequency, to update the voltage scaling value 
     regarding system frequency refer to product datasheet.  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
  
}


/**
  * @brief  This function is executed in case of error occurrence.
  * @PAram  None
  * @retval None
  */
void Error_Handler(void)
{
  while (1)
  {
  }
}

/**
  * @brief  Configure the RTC peripheral by selecting the clock source.
  * @PAram  None
  * @retval None
  */
static void RTC_Config(void)
{
  /*##-1- Configure the RTC peripheral #######################################*/
  /* Configure RTC prescaler and RTC data registers */
  /* RTC configured as follows:
      - Hour Format    = Format 24
      - Asynch Prediv  = Value according to source clock
      - Synch Prediv   = Value according to source clock
      - OutPut         = Output Disable
      - OutPutPolarity = High Polarity
      - OutPutType     = Open Drain */
  RtcHandle.Instance = RTC; 
  RtcHandle.Init.HourFormat = RTC_HOURFORMAT_24;
  RtcHandle.Init.AsynchPrediv = RTC_ASYNCH_PREDIV; /* 0x7F */
  RtcHandle.Init.SynchPrediv = RTC_SYNCH_PREDIV; /* (39KHz / 128) - 1 = 0x130 */
  RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
  RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  
  if(HAL_RTC_Init(&RtcHandle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler(); 
  }
  
  /*##-2- Check if data stored in BackUp register1: Wakeup timer enable #######*/
  /* Read the Back Up Register 1 Data */
  if (HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR1) == WAKEUP_TIMER_ENABLE)
  {
    /* if the wakeup timer is enabled then deactivate it to disable the wakeup timer interrupt */
    if(HAL_RTCEx_DeactivateWakeUpTimer(&RtcHandle) != HAL_OK)
    {
      /* Initialization Error */
      Error_Handler(); 
    }
  }
  
  /*##-3- Configure the RTC Wakeup peripheral #################################*/
  /* Setting the Wakeup time to 1 s
       If RTC_WAKEUPCLOCK_CK_SPRE_16BITS is selected, the frequency is 1Hz, 
       this allows to get a wakeup time equal to 1 s if the counter is 0x0 */
  HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, 0x0, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
  
  /*##-4- Write 'wakeup timer enabled' tag in RTC Backup data Register 1 #######*/
  HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR1, WAKEUP_TIMER_ENABLE);

}

/**
  * @brief  Configures TIM21 to measure the LSI oscillator frequency. 
  * @PAram  None
  * @retval LSI Frequency
  */
static uint32_t GetLSIFrequency(void)
{
  TIM_IC_InitTypeDef    TIMInput_Config;

  /* Configure the TIM peripheral *********************************************/ 
  /* Set TIMx instance */  
  Input_Handle.Instance = TIM21;
  
  /* TIM21 configuration: Input Capture mode ---------------------
     The LSI oscillator is connected to TIM21 TIM_CHANNEL_1.
     The Rising edge is used as active edge.
     The TIM21 Capture/Compare register associated to TIM_CHANNEL_1 
     is used to compute the frequency value. 
  ------------------------------------------------------------ */
  Input_Handle.Init.Prescaler         = 0; 
  Input_Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;  
  Input_Handle.Init.Period            = 0xFFFF; 
  Input_Handle.Init.ClockDivision     = 0;     
  if(HAL_TIM_IC_Init(&Input_Handle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
  
  /* Connect internally the TIM21 Input Capture of TIM_CHANNEL_1 to the LSI clock output */
  HAL_TIMEx_RemapConfig(&Input_Handle, TIM21_TI1_LSI);
  
  /* Configure the Input Capture of TIM_CHANNEL_1 */
  TIMInput_Config.ICPolarity  = TIM_ICPOLARITY_RISING;
  TIMInput_Config.ICSelection = TIM_ICSELECTION_DIRECTTI;
  TIMInput_Config.ICPrescaler = TIM_ICPSC_DIV8;
  TIMInput_Config.ICFilter    = 0;
  if(HAL_TIM_IC_ConfigChannel(&Input_Handle, &TIMInput_Config, TIM_CHANNEL_1) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

  /* Start the TIM Input Capture measurement in interrupt mode */
  if(HAL_TIM_IC_Start_IT(&Input_Handle, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  /* Wait until the TIM21 gets 2 LSI edges */
  while(uwCaptureNumber != 2)
  {
  }

  /* Disable TIM21 Capture/Compare channel Interrupt Request */
  HAL_TIM_IC_Stop_IT(&Input_Handle, TIM_CHANNEL_1);
  
  /* Deinitialize the TIM21 peripheral registers to their default reset values */
  HAL_TIM_IC_DeInit(&Input_Handle);

  return uwLsiFreq;
}

/**
  * @brief  Input Capture callback in non blocking mode 
  * @PAram  htim : TIM IC handle
  * @retval None
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  /* Get the Input Capture value */
  tmpCCTIM_CHANNEL_1[uwCaptureNumber++] = HAL_TIM_ReadCapturedValue(&Input_Handle, TIM_CHANNEL_1);
  
  if (uwCaptureNumber >= 2)
  {
    if ( tmpCCTIM_CHANNEL_1[0] > tmpCCTIM_CHANNEL_1[1] )
    {
      /* Compute the period length */
      uwPeriodValue = (uint16_t)(0xFFFF - tmpCCTIM_CHANNEL_1[0] + tmpCCTIM_CHANNEL_1[1] + 1);
    }
    else
    {
      /* Compute the period length */
      uwPeriodValue = (uint16_t)(tmpCCTIM_CHANNEL_1[1] - tmpCCTIM_CHANNEL_1[0]);
    }
    /* Frequency computation */ 
    uwLsiFreq = (uint32_t) SystemCoreClock / uwPeriodValue;
    uwLsiFreq *= 8;
  }
}

/**
  * @brief  RTC wakeup timer callback 
  * @PAram  htim : TIM IC handle
  * @retval None
*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{    
  /* Toggle LED3 */
  BSP_LED_Toggle(LED3);
}


#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @PAram  file: pointer to the source file name
  * @PAram  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

This is my clock configuration:

chai2145_0-1728007215431.png

 

My timer21 configuration:

 

 

/* USER CODE END TIM21_Init 1 */

 htim21.Instance = TIM21;

 htim21.Init.Prescaler = 0;

 htim21.Init.CounterMode = TIM_COUNTERMODE_UP;

 htim21.Init.Period = 65535;

 htim21.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

 htim21.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

 if (HAL_TIM_Base_Init(&htim21) != HAL_OK)

 {

    Error_Handler();

 }

 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

 if (HAL_TIM_ConfigClockSource(&htim21, &sClockSourceConfig) != HAL_OK)

 {

    Error_Handler();

 }

 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_LSI) != HAL_OK)

 {

   Error_Handler();

 }

 

1 ACCEPTED SOLUTION

Accepted Solutions
mbrossett
Associate III

@waclawek.jan He is using an input presacler of 8 which helps some (432 cycles instead of 54), but I agree with you that this would be a good use case for the DMA if he must use a 2MHz clock.

 

@chai2145 You could try reducing the execution time of the ISR to alleviate the problem somewhat by shutting off the timer in the interrupt and moving your calculations to the GetLSIFrequency() function...

  

/**
  * @brief  Configures TIM21 to measure the LSI oscillator frequency. 
  * @PAram  None
  * @retval LSI Frequency
  */
static uint32_t GetLSIFrequency(void)
{
  TIM_IC_InitTypeDef    TIMInput_Config;

  /* Configure the TIM peripheral *********************************************/ 
  /* Set TIMx instance */  
  Input_Handle.Instance = TIM21;
  
  /* TIM21 configuration: Input Capture mode ---------------------
     The LSI oscillator is connected to TIM21 TIM_CHANNEL_1.
     The Rising edge is used as active edge.
     The TIM21 Capture/Compare register associated to TIM_CHANNEL_1 
     is used to compute the frequency value. 
  ------------------------------------------------------------ */
  Input_Handle.Init.Prescaler         = 0; 
  Input_Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;  
  Input_Handle.Init.Period            = 0xFFFF; 
  Input_Handle.Init.ClockDivision     = 0;     
  if(HAL_TIM_IC_Init(&Input_Handle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
  
  /* Connect internally the TIM21 Input Capture of TIM_CHANNEL_1 to the LSI clock output */
  HAL_TIMEx_RemapConfig(&Input_Handle, TIM21_TI1_LSI);
  
  /* Configure the Input Capture of TIM_CHANNEL_1 */
  TIMInput_Config.ICPolarity  = TIM_ICPOLARITY_RISING;
  TIMInput_Config.ICSelection = TIM_ICSELECTION_DIRECTTI;
  TIMInput_Config.ICPrescaler = TIM_ICPSC_DIV8;
  TIMInput_Config.ICFilter    = 0;
  if(HAL_TIM_IC_ConfigChannel(&Input_Handle, &TIMInput_Config, TIM_CHANNEL_1) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

  /* Reset input capture count */
  uwCaptureNumber = 0;
  
  /* Start the TIM Input Capture measurement in interrupt mode */
  if(HAL_TIM_IC_Start_IT(&Input_Handle, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  /* Wait until the TIM21 gets 2 LSI edges */
  while(uwCaptureNumber != 2)
  {
  }

  if ( tmpCCTIM_CHANNEL_1[0] > tmpCCTIM_CHANNEL_1[1] )
  {
    /* Compute the period length */
    uwPeriodValue = (uint16_t)(0xFFFF - tmpCCTIM_CHANNEL_1[0] + tmpCCTIM_CHANNEL_1[1] + 1);
  }
  else
  {
    /* Compute the period length */
    uwPeriodValue = (uint16_t)(tmpCCTIM_CHANNEL_1[1] - tmpCCTIM_CHANNEL_1[0]);
  }
  /* Frequency computation */ 
  uwLsiFreq = (uint32_t) SystemCoreClock / uwPeriodValue;
  uwLsiFreq *= 8;
  
  /* Deinitialize the TIM21 peripheral registers to their default reset values */
  HAL_TIM_IC_DeInit(&Input_Handle);

  return uwLsiFreq;
}

/**
  * @brief  Input Capture callback in non blocking mode 
  * @PAram  htim : TIM IC handle
  * @retval None
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{  
  /* Get the Input Capture value */
  tmpCCTIM_CHANNEL_1[uwCaptureNumber++] = HAL_TIM_ReadCapturedValue(&Input_Handle, TIM_CHANNEL_1);
  
  if (uwCaptureNumber == 2)
  {
    /* Disable TIM21 Capture/Compare channel Interrupt Request */
    HAL_TIM_IC_Stop_IT(&Input_Handle, TIM_CHANNEL_1);
  }	
}

 

View solution in original post

19 REPLIES 19

Timers as such don't cause hardfaults. It's your code which hardfaults, and that includes any "library" you use (Cube/HAL).

Debug as usually with hardfaults, i.e. get the stacked PC content and in disasm (best mixed with source) observe instructions before that address to find out which instruction caused the fault and why.

JW

Hello Waclawek.Jan, 

Could you teach me how to debug a hardfault using the stacked PC content and disassembly to find the instruction that caused the issue?

Thank you. 

chai2145_0-1728020539486.png

chai2145_1-1728020655070.png

 

There is no single good guideline as there are infinite ways how things can go wrong.

The IDE you are using automatically unwinded the stack, so you don't need to look at the stacked PC etc. However, the "signal handler called at 0xfffffff1" line is slightly bogus and there's no point to try to look at disasm there, the real error points to the address in first screenshot (0x0800'20b6).

At this point we would also need to look at the fault registers to find out the exact nature of the error in the fault status registers (SCB_xFSR), and also content of processor's registers - for example, the pointed instruction ldr r3, [r7, #4] (read: into r3 load a word from address given by register r7 plus a constant 4), might have failed if r7 points to invalid address, which - looking at corresponding C source - would mean that the pointer in variable htim is incorrect (so then you'd look at that variable).

But the error might've been also imprecise, which means that it happened in some previously executed instruction, for example a store to invalid address is usually imprecise, as stores are not performed immediately but they go to a buffer at the memory interface of the processor, which will may perform the actual store to the actual memory/peripheral a couple of cycles later if the bus is occupied (and processor executes other instructions meantime). This means that you would need to "walk back", in this case, you'd need to look at the last few instructions of whichever callback was used, and again, check the relevant registers to find out if their content might have lead to the error in question.

At this point, you can also take a step back, and as the callback may be suspicious, look at what happens there. For example, I see you are incrementing an index to an array which you are writing into tmpCCTIM_CHANNEL_1[uwCaptureNumber++] , and I don't see where would that index be constrained/checked against array size/zeroed, potentially thrashing other variables or even the stack.

JW

 

Yes, By right the timer interrupt will stop after get 2 LSI edge from the code below. However If I using 2Mhz clock source , it seems like it cannot stop the interrupt and the interrupt keep coming (tmpCCTIM_CHANNEL_1[uwCaptureNumber++]) also keep increasing untill hardfault occured. It works correctly if I using 4Mhz clock and above. I have no idea but the STM32CubeL0 example code for RTC_LSI, which uses an MSI clock set to 2 MHz. 

 

I find it so hard to find the reason why t.t

 

/* Start the TIM Input Capture measurement in interrupt mode */
	if(HAL_TIM_IC_Start_IT(&htim21, TIM_CHANNEL_1) != HAL_OK)
	{
		Error_Handler();
	}

	/* Wait until the TIM21 gets 2 LSI edges */
	while(uwCaptureNumber != 2)
	{
	}

	/* Disable TIM21 Capture/Compare channel Interrupt Request */
	HAL_TIM_IC_Stop_IT(&htim21, TIM_CHANNEL_1);

	/* Deinitialize the TIM21 peripheral registers to their default reset values */
	HAL_TIM_IC_DeInit(&htim21);

	return uwLsiFreq;

 

At 2MHz system frequency and 37kHz LSI=interrupt frequency there are only 54 cycles per interrupt, that's an unreasonable expectation even if it would be written optimally.

Using Cube/HAL together with no compiler optimization results in lengthy interrupt service routine (ISR), hundreds or even low thousands of cycles.

In other words, as soon as you enable the interrupt, it simply tail-chains itself forever, or, in your case, until writing to the array reaches and overwrites the stack upon which it falls to the hardfault.

With this combination of clocks, you can't use interrupts to capture every LSI edge, you have to use some other methodology, e.g. using DMA instead of interrupt together with the input capture; or using two timers, one to count LSI cycles and other to count system clock cycles, and use some mechanism to compare the two.

JW

mbrossett
Associate III

@waclawek.jan He is using an input presacler of 8 which helps some (432 cycles instead of 54), but I agree with you that this would be a good use case for the DMA if he must use a 2MHz clock.

 

@chai2145 You could try reducing the execution time of the ISR to alleviate the problem somewhat by shutting off the timer in the interrupt and moving your calculations to the GetLSIFrequency() function...

  

/**
  * @brief  Configures TIM21 to measure the LSI oscillator frequency. 
  * @PAram  None
  * @retval LSI Frequency
  */
static uint32_t GetLSIFrequency(void)
{
  TIM_IC_InitTypeDef    TIMInput_Config;

  /* Configure the TIM peripheral *********************************************/ 
  /* Set TIMx instance */  
  Input_Handle.Instance = TIM21;
  
  /* TIM21 configuration: Input Capture mode ---------------------
     The LSI oscillator is connected to TIM21 TIM_CHANNEL_1.
     The Rising edge is used as active edge.
     The TIM21 Capture/Compare register associated to TIM_CHANNEL_1 
     is used to compute the frequency value. 
  ------------------------------------------------------------ */
  Input_Handle.Init.Prescaler         = 0; 
  Input_Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;  
  Input_Handle.Init.Period            = 0xFFFF; 
  Input_Handle.Init.ClockDivision     = 0;     
  if(HAL_TIM_IC_Init(&Input_Handle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
  
  /* Connect internally the TIM21 Input Capture of TIM_CHANNEL_1 to the LSI clock output */
  HAL_TIMEx_RemapConfig(&Input_Handle, TIM21_TI1_LSI);
  
  /* Configure the Input Capture of TIM_CHANNEL_1 */
  TIMInput_Config.ICPolarity  = TIM_ICPOLARITY_RISING;
  TIMInput_Config.ICSelection = TIM_ICSELECTION_DIRECTTI;
  TIMInput_Config.ICPrescaler = TIM_ICPSC_DIV8;
  TIMInput_Config.ICFilter    = 0;
  if(HAL_TIM_IC_ConfigChannel(&Input_Handle, &TIMInput_Config, TIM_CHANNEL_1) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

  /* Reset input capture count */
  uwCaptureNumber = 0;
  
  /* Start the TIM Input Capture measurement in interrupt mode */
  if(HAL_TIM_IC_Start_IT(&Input_Handle, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  /* Wait until the TIM21 gets 2 LSI edges */
  while(uwCaptureNumber != 2)
  {
  }

  if ( tmpCCTIM_CHANNEL_1[0] > tmpCCTIM_CHANNEL_1[1] )
  {
    /* Compute the period length */
    uwPeriodValue = (uint16_t)(0xFFFF - tmpCCTIM_CHANNEL_1[0] + tmpCCTIM_CHANNEL_1[1] + 1);
  }
  else
  {
    /* Compute the period length */
    uwPeriodValue = (uint16_t)(tmpCCTIM_CHANNEL_1[1] - tmpCCTIM_CHANNEL_1[0]);
  }
  /* Frequency computation */ 
  uwLsiFreq = (uint32_t) SystemCoreClock / uwPeriodValue;
  uwLsiFreq *= 8;
  
  /* Deinitialize the TIM21 peripheral registers to their default reset values */
  HAL_TIM_IC_DeInit(&Input_Handle);

  return uwLsiFreq;
}

/**
  * @brief  Input Capture callback in non blocking mode 
  * @PAram  htim : TIM IC handle
  * @retval None
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{  
  /* Get the Input Capture value */
  tmpCCTIM_CHANNEL_1[uwCaptureNumber++] = HAL_TIM_ReadCapturedValue(&Input_Handle, TIM_CHANNEL_1);
  
  if (uwCaptureNumber == 2)
  {
    /* Disable TIM21 Capture/Compare channel Interrupt Request */
    HAL_TIM_IC_Stop_IT(&Input_Handle, TIM_CHANNEL_1);
  }	
}

 

Look at the instructions that are faulting, and the registers in the MCU

Check SP is within expected scope and properly aligned.

Have a Hard Fault Handler that prints out the details.

You don't scope the incrementing pointer into a finite array

The value for R7 probably exceeds the bounds of RAM, you'd have to look at it to know

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Hi @mbrossett , I have also tried your solution, stopping the timer interrupt after receiving two LSI clock edges. Unfortunately, the frequency obtained is not correct and is much higher than the LSI frequency. I am getting around 200 kHz. I think something is messed up between the timer clock and system clock. Only can use frequency higher than 4Mhz. I'm curious why the ST using MSI 2Mhz in their sample code is working correctly. The reason why I,m using 2Mhz is because I would like to implement a low power application, therefore I keep my system frequency low.

@Tesla DeLorean ,Most likely, the stack is overflowing because the interrupt does not stop correctly, and the array buffer keeps increasing. I'm wondering why using a 2 MHz clock cannot stop the timer interrupt correctly, whereas the 4 MHz clock can..