cancel
Showing results for 
Search instead for 
Did you mean: 

STM32N6 CubeMX incorrect timer clock derivation

MasterOfReality
Associate II

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

6 REPLIES 6
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".
Ghofrane GSOURI
ST Employee

Hello @MasterOfReality 

I would appreciate it if you could mention the CubeMX version that you are using.

Also it would be great if you share your IOC in order to check your configuration .

THX

Ghofrane

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello @Ghofrane GSOURI 

I am using CubeMX 6.14.1.

I am attaching my ioc file. I just wanted to point out that I am using custom dev environment and only utilizing the CubeMX code for peripherals initialization, not for creating the final executable.

 

Best regards,

Bojan

Hello @MasterOfReality 

I recommend using the latest version of CubeMX (6.15.0), as it includes major fixes that may address this issue.

Meantime I am checking your IOC

I will be waiting for your feedback.

THX

Ghofrane

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello @Ghofrane GSOURI ,

I updated to the latest version of CubeMX (6.15.0), but the generated timebase code is still using APB1 as clock input to TIM6 for the calculations, instead of SYSCLK/TIMPRE.

For reference I am attaching the generated stm32n6xx_hal_timebase_tim.c file.

Ghofrane GSOURI
ST Employee

Hello @MasterOfReality 

Thanks for your contribution.

A ticket has been escalated to dev team for resolution.

Internal ticket number is 208539 

I will keep you posted with updates.

THX

Ghofrane

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.