cancel
Showing results for 
Search instead for 
Did you mean: 

STM32N6 CubeMX incorrect timer clock derivation

MasterOfReality
Associate

Hello,

 

While I was developing code for STM32N6, which uses FreeRTOS that uses SysTick as tick source, I changed the HAL timebase to utilize Timer 6 (TIM6). I've noticed that the generated CubeMX code will incorrectly calculate the input clock frequency for the timer, by assuming it is APB1. According to the reference manual (RM0486 Rev 2, p.449), the kernel clock for the Timer 6 is the timer group 1 clock, which is derived from the SYS Clock, divided by the value of the prescaler TIMPRE. So it is not APB1, but it is SYSCLK/TIMPRE, if I am not misunderstanding something.

What happened to me is that I had the TIMPRE set to '/2', but there was no code generated for setting TIMPRE, and the Timer was running from SYSCLK / 1, which was 400 MHz in my case. The CubeMX generated code used the value of APB1 (which was 200 MHz in my case) for the calculations and ended up with 2x greater frequency of TIM6 (period of 0.5 ms, instead of the wanted 1 ms).

Here is the CubeMX generated code for HAL_InitTick():

HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  RCC_ClkInitTypeDef    clkconfig;
  uint32_t              uwTimclock = 0;
  uint32_t              uwPrescalerValue = 0;

  /*Configure the TIM6 IRQ priority */
  HAL_NVIC_SetPriority(TIM6_IRQn, TickPriority ,0);
  /* Enable the TIM6 global Interrupt */
  HAL_NVIC_EnableIRQ(TIM6_IRQn);

  /* Enable TIM6 clock */
  __HAL_RCC_TIM6_CLK_ENABLE();

  /* Get clock configuration */
  HAL_RCC_GetClockConfig(&clkconfig);

  /* Compute TIM6 clock */
  uwTimclock = HAL_RCC_GetPCLK1Freq();

  /* Compute the prescaler value to have TIM6 counter clock equal to 1MHz */
  uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);

  /* Initialize TIM6 */
  htim6.Instance = TIM6;

  /* Initialize TIMx peripheral as follow:
   * Period = [(TIM6CLK/1000) - 1]. to have a (1/1000) s time base.
   * Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
   * ClockDivision = 0
   * Counter direction = Up
   */
  htim6.Init.Period = (1000000U / 1000U) - 1U;
  htim6.Init.Prescaler = uwPrescalerValue;
  htim6.Init.ClockDivision = 0;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;

  if(HAL_TIM_Base_Init(&htim6) == HAL_OK)
  {
    /* Start the TIM time Base generation in interrupt mode */
    return HAL_TIM_Base_Start_IT(&htim6);
  }

  /* Return function status */
  return HAL_ERROR;
}

I've adjusted the HAL_InitTick() code as following:

HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  RCC_ClkInitTypeDef    clkconfig;
  uint32_t              uwTimclock = 0;
  uint32_t              uwPrescalerValue = 0;
  HAL_StatusTypeDef     status;

  /* Enable TIM6 clock */
  __HAL_RCC_TIM6_CLK_ENABLE();

  /* Get clock configuration */
  HAL_RCC_GetClockConfig(&clkconfig);

  /* Get Timer Group prescaler */
  RCC_PeriphCLKInitTypeDef  periphClkInit;
  HAL_RCCEx_GetPeriphCLKConfig(&periphClkInit);

  /* Compute TIM6 clock */
  uwTimclock = HAL_RCC_GetSysClockFreq();
  if(periphClkInit.TIMPresSelection == RCC_TIMPRES_DIV2)
  {
    uwTimclock /= 2U;
  }
  else if(periphClkInit.TIMPresSelection == RCC_TIMPRES_DIV4)
  {
    uwTimclock /= 4U;
  }
  else if(periphClkInit.TIMPresSelection == RCC_TIMPRES_DIV8)
  {
    uwTimclock /= 8U;
  }

  /* Compute the prescaler value to have TIM6 counter clock equal to 1MHz */
  uwPrescalerValue = (uint32_t)((uwTimclock / 1000000U) - 1U);

  /* Initialize TIM6 */
  htim6.Instance = TIM6;

  /* Initialize TIMx peripheral as follow:

  + Period = [(TIM6CLK/1000) - 1]. to have a (1/1000) s time base.
  + Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
  + ClockDivision = 0
  + Counter direction = Up
  */
  htim6.Init.Period = (1000000U / 1000U) - 1U;
  htim6.Init.Prescaler = uwPrescalerValue;
  htim6.Init.ClockDivision = 0;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;

  status = HAL_TIM_Base_Init(&htim6);
  if (status == HAL_OK)
  {
    /* Start the TIM time Base generation in interrupt mode */
    status = HAL_TIM_Base_Start_IT(&htim6);
    if (status == HAL_OK)
    {
      if (TickPriority < (1UL << __NVIC_PRIO_BITS))
      {
        /* Enable the TIM6 global Interrupt */
        HAL_NVIC_SetPriority(TIM6_IRQn, TickPriority, 0U);
        uwTickPrio = TickPriority;
      }
      else
      {
        status = HAL_ERROR;
      }
    }
  }

  /* Enable the TIM6 global Interrupt */
  HAL_NVIC_EnableIRQ(TIM6_IRQn);

  /* Return function status */
  return status;
}

Can someone confirm that this is indeed mistake with CubeMX generated code, or it is something on my side?

 

Thank you in advance.

Bojan

1 REPLY 1
TDK
Super User

Sure looks like a bug to me. Should be easy to test. Use different clocks and see if HAL_Delay(1000) pauses for a real second. Toggle an LED or something.

If you feel a post has answered your question, please click "Accept as Solution".