cancel
Showing results for 
Search instead for 
Did you mean: 

Timer3 disable/enable operation

CharlieMangold
Associate III

We are using Timer3 to increment a uint64_t counter for serial port operation and need to be able to grab the counter value in the main loop.

 

	//----------------------------------------------------------------
	// -------------- Timer 3 ----------------------------------------
	//----------------------------------------------------------------
	// Setup for timer 3, set defaults
uint32_t tim_prescaler	= 0;
uint32_t tim_period		= 0;
uint32_t TimOutClock	= 1;

	// Timer 3 configuration. We want a timer 3 frequency of 2Khz(a interrupt
	// every .5msec
	tim_prescaler	= 0;
	TimOutClock		= SystemCoreClock/1;
	tim_period		= __LL_TIM_CALC_ARR(TimOutClock, tim_prescaler, 2000);

	TIM_TimeBaseStructure.Prescaler			= tim_prescaler;
	TIM_TimeBaseStructure.CounterMode		= LL_TIM_COUNTERMODE_UP;
	TIM_TimeBaseStructure.Autoreload		= tim_period;
	TIM_TimeBaseStructure.ClockDivision		= LL_TIM_CLOCKDIVISION_DIV1;
	TIM_TimeBaseStructure.RepetitionCounter	= 0;
	LL_TIM_Init(TIM3, &TIM_TimeBaseStructure);

 

Because the counter can not be retrieved in a atomic operation we initially disabled the interrupt and turned it back on.

 

	LL_TIM_DisableIT_UPDATE(TIM3);
	rc = timer3Tick;
	LL_TIM_EnableIT_UPDATE(TIM3);

 

The issue with this is that if the timer rolled over with the interrupt off, we would lose that tick count as the timer never stopped. We want to use the timer disable/enable to stop the timer, grab the counter and restart the timer exactly where we stopped it. Is there a document that can verify that when we restart the timer it does not reset the counter register Timer3->CNT?

 

uint64_t timer3_get_count(void)
{
uint64_t rc;

	// Turn off the timer 3 counter
	LL_TIM_DisableCounter(TIM3);
	rc = timer3Tick;
	// Turn on the timer 3 counter
	LL_TIM_EnableCounter(TIM3);
	return(rc);
}

 

 Capture.JPG

1 ACCEPTED SOLUTION

Accepted Solutions

Which STM32?

> if the timer rolled over with the interrupt off...

... nothing wrong happens. TIMx_SR.UIF gets set regardless, and after reenablin interrupts, it fires immediately.

You can easily test it - disable interrupts for a long time, toggle a pin and reenable - in the ISR also toggle a pin. You'll see both toggles on a scope/LA just after each other.

That is, if those functions or macros do nothing else but clear/set TIM_DIER.UIE.

OTOH, clearing  TIM_DIER.UIE won't *guarantee* atomicity of what comes after it. The story is long and I am tired, it's late here (the essence of it is same as of this). Just use _disable_irq()/_enable_irq() instead. Don't argue, just do it.

JW

 

View solution in original post

4 REPLIES 4
Bob S
Principal

> We want to use the timer disable/enable to stop the timer

Oh heavens no, don't do that.  That is totally unnecessary and you will skew the timer period each time you read the 64-bit value.

With a 2KHz interrupt rate you can use something like this code.  Note that this code presumes that the 3 reads of timer3Tick all happen in less than 2 Timer3 interrupt periods.  So it requires no absurdly long interrupt functions or higher priority RTOS tasks that take that long.

uint64_t timer3_get_count(void)
{
   uint64_t rc1, rc2, rc3;

   // THIS CODE PRESUMES timer3Tick is declared "volatile"
   rc1 = timer3Tick;
   rc2 = timer3Tick;
   rc3 = timer3Tick;

   // If the first 2 readings are the same, then no interrupt happened and
   // either reading is value.  Return either one of them.
   if ( rc1 == rc2 ) {
      return( rc1 );
   }
   // Otherwise, an interrupt happened between rc1 and rc2, which means it
   // SHOULD NOT happen again between rc2 and rc3, so both rc2 and rc3 are
   // valid.  Return either one of them.
   return( rc3 );
}

 

I like the code you wrote but you are doing three 64bit reads (at least two clock cycles each) and a 64 bit wide compare. A single register write(disable), 64bit read and single register write(enable) is faster. I can't say how many timer3 clock cycles I would lose as I'm not proficient in ARM assembly, but it's somewhere in the 32-48nsec range for each read of the timer value if the disable/enable does not reset the timer3 count. If the timer were critical I would use your method or maybe go with a 32bit value which are single cycle reads and deal with roll overs, but losing a few nsec in our use case has no bearing.

Which STM32?

> if the timer rolled over with the interrupt off...

... nothing wrong happens. TIMx_SR.UIF gets set regardless, and after reenablin interrupts, it fires immediately.

You can easily test it - disable interrupts for a long time, toggle a pin and reenable - in the ISR also toggle a pin. You'll see both toggles on a scope/LA just after each other.

That is, if those functions or macros do nothing else but clear/set TIM_DIER.UIE.

OTOH, clearing  TIM_DIER.UIE won't *guarantee* atomicity of what comes after it. The story is long and I am tired, it's late here (the essence of it is same as of this). Just use _disable_irq()/_enable_irq() instead. Don't argue, just do it.

JW

 


@waclawek.jan wrote:

The story is long and I am tired, it's late here. Just use _disable_irq()/_enable_irq() instead. Don't argue, just do it.

JW

 


Thanks. Will do.