Skip to main content
Associate
Oct 28, 2025 12:25 PM UTC
Solved

Timer synchronization failure when using different ARR value

I'm trying to use 2 timers (TIM1, TIM8) in sync.

Everything works OK when using same value for both timers' ARR.

But drift occurs when using two different values as ARR (i.g. 3999 for TIM1 & 19999 for TIM8).

What could be causing this drift?

 

tim.c

/* TIM1 init function */
void MX_TIM1_Init(void)
{

 /* USER CODE BEGIN TIM1_Init 0 */

 /* USER CODE END TIM1_Init 0 */

 LL_TIM_InitTypeDef TIM_InitStruct = {0};
 LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
 LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};

 /* Peripheral clock enable */
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

 /* TIM1 interrupt Init */
 NVIC_SetPriority(TIM1_UP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
 NVIC_EnableIRQ(TIM1_UP_IRQn);

 /* USER CODE BEGIN TIM1_Init 1 */

 /* USER CODE END TIM1_Init 1 */
 TIM_InitStruct.Prescaler = 0;
 TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
 TIM_InitStruct.Autoreload = 3999;
 TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
 TIM_InitStruct.RepetitionCounter = 0;
 LL_TIM_Init(TIM1, &TIM_InitStruct);
 LL_TIM_DisableARRPreload(TIM1);
 TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN;
 TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
 TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
 TIM_OC_InitStruct.CompareValue = 0;
 TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
 TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
 TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
 TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
 LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
 LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1);
 LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
 LL_TIM_SetTriggerOutput2(TIM1, LL_TIM_TRGO2_RESET);
 LL_TIM_DisableMasterSlaveMode(TIM1);
 TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
 TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
 TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
 TIM_BDTRInitStruct.DeadTime = 0;
 TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;
 TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;
 TIM_BDTRInitStruct.BreakFilter = LL_TIM_BREAK_FILTER_FDIV1;
 TIM_BDTRInitStruct.BreakAFMode = LL_TIM_BREAK_AFMODE_INPUT;
 TIM_BDTRInitStruct.Break2State = LL_TIM_BREAK2_DISABLE;
 TIM_BDTRInitStruct.Break2Polarity = LL_TIM_BREAK2_POLARITY_HIGH;
 TIM_BDTRInitStruct.Break2Filter = LL_TIM_BREAK2_FILTER_FDIV1;
 TIM_BDTRInitStruct.Break2AFMode = LL_TIM_BREAK_AFMODE_INPUT;
 TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
 LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct);
 /* USER CODE BEGIN TIM1_Init 2 */

 /* USER CODE END TIM1_Init 2 */

}
/* TIM8 init function */
void MX_TIM8_Init(void)
{

 /* USER CODE BEGIN TIM8_Init 0 */

 /* USER CODE END TIM8_Init 0 */

 LL_TIM_InitTypeDef TIM_InitStruct = {0};
 LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
 LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};

 /* Peripheral clock enable */
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM8);

 /* TIM8 interrupt Init */
 NVIC_SetPriority(TIM8_UP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
 NVIC_EnableIRQ(TIM8_UP_IRQn);

 /* USER CODE BEGIN TIM8_Init 1 */

 /* USER CODE END TIM8_Init 1 */
 TIM_InitStruct.Prescaler = 0;
 TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
 TIM_InitStruct.Autoreload = 19999;
 TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
 TIM_InitStruct.RepetitionCounter = 0;
 LL_TIM_Init(TIM8, &TIM_InitStruct);
 LL_TIM_DisableARRPreload(TIM8);
 TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN;
 TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
 TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
 TIM_OC_InitStruct.CompareValue = 0;
 TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
 TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
 TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
 TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
 LL_TIM_OC_Init(TIM8, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
 LL_TIM_OC_DisableFast(TIM8, LL_TIM_CHANNEL_CH1);
 LL_TIM_SetTriggerOutput(TIM8, LL_TIM_TRGO_RESET);
 LL_TIM_SetTriggerOutput2(TIM8, LL_TIM_TRGO2_RESET);
 LL_TIM_DisableMasterSlaveMode(TIM8);
 TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
 TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
 TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
 TIM_BDTRInitStruct.DeadTime = 0;
 TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;
 TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;
 TIM_BDTRInitStruct.BreakFilter = LL_TIM_BREAK_FILTER_FDIV1;
 TIM_BDTRInitStruct.BreakAFMode = LL_TIM_BREAK_AFMODE_INPUT;
 TIM_BDTRInitStruct.Break2State = LL_TIM_BREAK2_DISABLE;
 TIM_BDTRInitStruct.Break2Polarity = LL_TIM_BREAK2_POLARITY_HIGH;
 TIM_BDTRInitStruct.Break2Filter = LL_TIM_BREAK2_FILTER_FDIV1;
 TIM_BDTRInitStruct.Break2AFMode = LL_TIM_BREAK_AFMODE_INPUT;
 TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
 LL_TIM_BDTR_Init(TIM8, &TIM_BDTRInitStruct);
 /* USER CODE BEGIN TIM8_Init 2 */

 /* USER CODE END TIM8_Init 2 */

}

stm32h5xx_it.c

/* USER CODE BEGIN PV */
volatile uint32_t TIM1Count = 0;
volatile uint32_t TIM8Count = 0;
volatile uint32_t TIMDiff = 0;
/* USER CODE END PV */

/**
 * @brief This function handles TIM1 Update interrupt.
 */
void TIM1_UP_IRQHandler(void)
{
 /* USER CODE BEGIN TIM1_UP_IRQn 0 */
	if (LL_TIM_IsActiveFlag_UPDATE(TIM1)) {
		LL_TIM_ClearFlag_UPDATE(TIM1);
		TIM1Count += 5;
	}

 /* USER CODE END TIM1_UP_IRQn 0 */
 /* USER CODE BEGIN TIM1_UP_IRQn 1 */

 /* USER CODE END TIM1_UP_IRQn 1 */
}

/**
 * @brief This function handles TIM8 Update interrupt.
 */
void TIM8_UP_IRQHandler(void)
{
 /* USER CODE BEGIN TIM8_UP_IRQn 0 */
	if (LL_TIM_IsActiveFlag_UPDATE(TIM8)) {
		LL_TIM_ClearFlag_UPDATE(TIM8);
		TIM8Count++;
		TIMDiff = TIM1Count - TIM8Count; // <-- Diff increases
	}

 /* USER CODE END TIM8_UP_IRQn 0 */
 /* USER CODE BEGIN TIM8_UP_IRQn 1 */

 /* USER CODE END TIM8_UP_IRQn 1 */
}

 

 

 

This topic has been closed for replies.
jhsonBest answer
Associate
Oct 29, 2025 12:54 AM UTC

There was a mistake when editing code snippet for upload.

Correct one would look like below as you suggested.

/* USER CODE BEGIN PV */
volatile uint32_t TIM1Count = 0;
volatile uint32_t TIM8Count = 0;
volatile uint32_t TIMDiff = 0;
/* USER CODE END PV */

/**
 * @brief This function handles TIM1 Update interrupt.
 */
void TIM1_UP_IRQHandler(void)
{
 /* USER CODE BEGIN TIM1_UP_IRQn 0 */
	if (LL_TIM_IsActiveFlag_UPDATE(TIM1)) {
		LL_TIM_ClearFlag_UPDATE(TIM1);
		TIM1Count++;
	}

 /* USER CODE END TIM1_UP_IRQn 0 */
 /* USER CODE BEGIN TIM1_UP_IRQn 1 */

 /* USER CODE END TIM1_UP_IRQn 1 */
}

/**
 * @brief This function handles TIM8 Update interrupt.
 */
void TIM8_UP_IRQHandler(void)
{
 /* USER CODE BEGIN TIM8_UP_IRQn 0 */
	if (LL_TIM_IsActiveFlag_UPDATE(TIM8)) {
		LL_TIM_ClearFlag_UPDATE(TIM8);
		TIM8Count += 5;
		TIMDiff = TIM1Count - TIM8Count; // <-- Diff increases
	}

 /* USER CODE END TIM8_UP_IRQn 0 */
 /* USER CODE BEGIN TIM8_UP_IRQn 1 */

 /* USER CODE END TIM8_UP_IRQn 1 */
}

 

And the problem was that I was using TIM1 as center aligned mode 1 in the original code.

So using (Desired ARR - 1) as ARR for TIM1 was a critical mistake causing the drift.

Using (Desired ARR) as ARR for TIM1 and using (Desired ARR * 5 - 1) as ARR for TIM8 solved my problem.

 

Thank you!

4 replies

TDK
Oct 28, 2025 1:16 PM UTC

Should work unless interrupts are getting blocked.

What does your main.c look like? What other interrupts are active in the system and what are their priorities?

"If you feel a post has answered your question, please click ""Accept as Solution""."
waclawek.jan
Super User
Oct 28, 2025 3:14 PM UTC

You set TIM1's period to be 4000 clock periods and TIM8's period to be 20000 clock periods, so there are 5 TIM1 cycles in one TIM8 cycle; thus for one increment of TIM8Count, you increment TIM1Count by 5 * 5 = 25.

JW

jhsonAuthorBest answer
Associate
Oct 29, 2025 12:54 AM UTC

There was a mistake when editing code snippet for upload.

Correct one would look like below as you suggested.

/* USER CODE BEGIN PV */
volatile uint32_t TIM1Count = 0;
volatile uint32_t TIM8Count = 0;
volatile uint32_t TIMDiff = 0;
/* USER CODE END PV */

/**
 * @brief This function handles TIM1 Update interrupt.
 */
void TIM1_UP_IRQHandler(void)
{
 /* USER CODE BEGIN TIM1_UP_IRQn 0 */
	if (LL_TIM_IsActiveFlag_UPDATE(TIM1)) {
		LL_TIM_ClearFlag_UPDATE(TIM1);
		TIM1Count++;
	}

 /* USER CODE END TIM1_UP_IRQn 0 */
 /* USER CODE BEGIN TIM1_UP_IRQn 1 */

 /* USER CODE END TIM1_UP_IRQn 1 */
}

/**
 * @brief This function handles TIM8 Update interrupt.
 */
void TIM8_UP_IRQHandler(void)
{
 /* USER CODE BEGIN TIM8_UP_IRQn 0 */
	if (LL_TIM_IsActiveFlag_UPDATE(TIM8)) {
		LL_TIM_ClearFlag_UPDATE(TIM8);
		TIM8Count += 5;
		TIMDiff = TIM1Count - TIM8Count; // <-- Diff increases
	}

 /* USER CODE END TIM8_UP_IRQn 0 */
 /* USER CODE BEGIN TIM8_UP_IRQn 1 */

 /* USER CODE END TIM8_UP_IRQn 1 */
}

 

And the problem was that I was using TIM1 as center aligned mode 1 in the original code.

So using (Desired ARR - 1) as ARR for TIM1 was a critical mistake causing the drift.

Using (Desired ARR) as ARR for TIM1 and using (Desired ARR * 5 - 1) as ARR for TIM8 solved my problem.

 

Thank you!

waclawek.jan
Super User
Oct 29, 2025 8:25 AM UTC

Thanks for explanation, but

> editing code snippet for upload

this is why it's better to copy/paste code unedited. Or, even better, post a minimal but complete compilable code exhibiting the problem.

Glad you got it working.

JW