cancel
Showing results for 
Search instead for 
Did you mean: 

Timer IT fires directly after initialization

Tamas Novak
Associate III

STM32F030, CubeIDE generated skeleton, TIM14 is at LL.

After running MX_TIM14_Init() , TIM14_IRQHandler immediately invoked. I put a breakpoint into TIM14 IT routine, start debug, then F6 step from the beginning of main(). Please see attached video. I inserted two lines to disable timer update interrupt and clear the UPDATE flag into "USER CODE BEGIN SysInit" block while strugling...still IT fired.

And it is before reaching "LL_TIM_EnableCounter(TIM14);"

7 REPLIES 7
Tamas Novak
Associate III

I'm the reporter...Solved: watchdog kicked in, because MX_IWDG_Init(); was not commented out. Still I don't know how it could reach breakpoint in timer IT routine...it would be good to realise how debug works :o

> After running MX_TIM14_Init() , TIM14_IRQHandler immediately invoked.

Then it's in MX_TIM14_Init() where the interrupt is enabled and also probably forced by setting TIMx_EGR.UG. It then won't help if you disable the interrupt and clear the flag *before* calling MX_TIM14_Init(). My crystal cube is hazy and won't tell me what's inside your MX_TIM14_Init().

Timer interrupts are not gated by the timer's counter enable (TIMx_CR1.CEN).

JW

Tamas Novak
Associate III

@Community member​  interrupt is not enabled, but....the "stock" (MX generated) MX_TIM14_Init(} is follows:

static void MX_TIM14_Init(void) {
 
	/* USER CODE BEGIN TIM14_Init 0 */
 
	/* USER CODE END TIM14_Init 0 */
 
	LL_TIM_InitTypeDef TIM_InitStruct = { 0 };
 
	/* Peripheral clock enable */
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM14);
 
	/* TIM14 interrupt Init */
	NVIC_SetPriority(TIM14_IRQn, 0);
	NVIC_EnableIRQ(TIM14_IRQn);
 
	/* USER CODE BEGIN TIM14_Init 1 */
 
	/* USER CODE END TIM14_Init 1 */
	TIM_InitStruct.Prescaler = 125;
	TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
	TIM_InitStruct.Autoreload = 5;
	TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
	LL_TIM_Init(TIM14, &TIM_InitStruct);
	LL_TIM_DisableARRPreload(TIM14);
	/* USER CODE BEGIN TIM14_Init 2 */
 
	/* USER CODE END TIM14_Init 2 */
 
}

it calls LL_TIM_Init(), where the last line is

/* Generate an update event to reload the Prescaler
	 and the repetition counter value (if applicable) immediately */
	LL_TIM_GenerateEvent_UPDATE(TIMx);

So IT routine is called once --if I want it or not. Which is funny, as the next line is

LL_TIM_DisableARRPreload(TIM14);

where I state I don't want ARR preload, so generating update event is unneccessary...just for upload the counter values. For me a pain in the @&ss running the interrupt handler when I don't want it.

It seems I must edit the MX generated code...just not to forget about it when re-generation needed later.

By the way... Your MX_TIM14_Init() takes 10 lines of code. With a direct register programming (for a timer) exactly the same configuration takes a whopping... 5 lines!

static void MX_TIM14_Init(void) {
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM14);
 
	NVIC_SetPriority(TIM14_IRQn, 0);
	NVIC_EnableIRQ(TIM14_IRQn);
 
	TIM14->PSC = 125;
	TIM14->ARR = 5;
}

Additionally:

  • No problems with unnecessary and/or stupid code like that interrupt.
  • No big functions taking KB's of flash memory.
  • No temporary structures on stack memory.
  • You can study only reference manual, instead of reference manual, HAL/LL and CubeMX.

> LL_TIM_GenerateEvent_UPDATE()

That just sets TIMx_EGR.UG, which in turn generates the update event and, if enabled in TIMx_DIER, fires the interrupt. The main reason why Cube/MX does that is to get the prescaler from its shadow to active; otherwise the previously set prescaler (which in your case is 0) is active for the first cycle until "natural" update from counter overflow occurs.

If TIMx_DIER at the moment of setting TIMx_EGR.UG is all zero, there should be no interrupt. You may want to observe the timer registers in the debugger, while stepping through the initialization code.

JW

Is it true only for PSC or for ARR also? I kind of assumed that when counter is disabled, shadow registers are updated immediately or are loaded from preload registers when enabling counter, but indeed documentation doesn't confirm this. Thought, imho it would be more logical... Have anyone checked this nuance reliably on hardware?

In the default setting, only PSC is shadowed, but that is shadowed unconditionally. ARR and some of the output-compare registers/bitfields/bits are shadowed only if the respective bits (CR1.ARPE, CCMRx.OCyPE, CR2.CCPC in the advanced timers) are set.

Setting/clearing CR1.CEN only starts/stops the counter, it has no impact on any other feature of the timer. In the past, when it was common not to use SPL (and there was no Cube), there were quite a couple of threads dealing with the fact that PSC was not updated until the first overflow.

JW