cancel
Showing results for 
Search instead for 
Did you mean: 

My micros() function is not glitchfree?

Tobe
Senior III

Hi,

i have a micros() function, that i adapted from arduino library. It worked flawlessly so far, but i have made a discovery, that micros() did not work properly at one time. micros() returned a time, that had 772us less, than it should have returned. Seems to be a very very rare case, but i want it to be perfect, and dont have any problems at all in the future. I cant see, where there should be a problem.

These are the definitions

 

/*
 *  170 Mhz sysclock
 *  APB2 1/4
 *  x2
 *  Timer prescaler 1
 *  => 771us interval (must be beow 1ms!)
 */
#define TICS_PER_TIMER_OVERFLOW 0xFFFF // DO NOT CHANGE. Update is only generated on overflow. Capture 1 dont seem to be usable!
#define MICROS_PER_TIMER_OVERFLOW 771

// the fractional number of milliseconds per timer overflow.
#define FRACT_INC (MICROS_PER_TIMER_OVERFLOW % 1000)
#define FRACT_MAX (1000)

 

 

Timer setup:

 

void TK_init_TimeAndDelay(){
	__HAL_RCC_TIM16_CLK_ENABLE();
	TIM16->PSC = TIM16_PRESC - 1; 				// true prescaler 85 is value (85-1)
	TIM16->CR1 |= TIM_CR1_CEN | TIM_CR1_URS;					// Enable counter
	TIM16->ARR = TICS_PER_TIMER_OVERFLOW - 1;
	TIM16->DIER |= TIM_DIER_UIE;				// Comp 1 interrupt enable
}

 

 

Interrupt handler:

 

void TK_TIM1_UP_TIM16_IRQHandler(void){
	if(TIM16->SR & TIM_SR_UIF){
		TIM16->SR &= ~TIM_SR_UIF; // reset flag

		//t_millis += MILLIS_INC; is zero anyway
		millisFract += FRACT_INC;

		if (millisFract >= FRACT_MAX) {
			millisFract -= FRACT_MAX;
			t_millis++;
		}
		timer_overflow_count++;
	}
}

 

 

micros() function:

 

uint32_t micros(){
	uint32_t tovf;
	uint32_t cnt;
	NVIC_DisableIRQ(TIM1_UP_TIM16_IRQn);
	cnt = TIM16->CNT;
	tovf = timer_overflow_count;

	// If a counter overflow has happend, we need to add that extra value!
	if((TIM16->SR & TIM_SR_UIF)){
		tovf++;
		cnt = TIM16->CNT;//0xFFFF - cnt; // Might have been 0xFFF0 and is now 0, so add value (-9 -> +9)
	}

	NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
	return tovf * MICROS_PER_TIMER_OVERFLOW + cnt/85;	// divided through 85, to get to us, since 1 tic is ~11ns
}

 

 

Here the actual point int the code where micros returned the wrong time (772us less than it should):

 

void TIM1_CC_IRQHandler(){
	__disable_irq();

	// Hall external triggered
	if(TIM1->SR & TIM_SR_CC1IF){
		TIM1->SR = ~TIM_SR_CC1IF;

		//TEST_HALL_ON;
		if(mSTATE >= MOTOR_STARTED){
			TEST_HALL_ON
			__disable_irq();
			eStructs[eventIndex].timeMicros = micros();
			....

 

 

 

 

 

 

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tobe
Senior III

Thanks to waclawek.jan i found the issue:

It was a priority issue, micros() was executed, when the TIM16 interrupt was beeing executed.

View solution in original post

10 REPLIES 10

> Here the actual point int the code where the error happend

What error?

JW

micros() returned a time, that had 772us less, than it should have returned.

I don't understand the point of the overflow code

The TIM keeps chugging regardless of how much time you spend in the interupt handler(s), or off on other tasks/threads

Is there some chance you could use a 32-bit TIM? Or a 16-bit TIM clocking at 1 MHz or 10 MHz, having span of say 65.536 ms or 6.5536 ms

If you have delays longer this, should we be considering different delay strategies entirely?

Is your RTOS going to wander off the plot for several milliseconds?

What delays / precision are we hoping for here?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

I know that the counter runs in the background. The overflow is needed for longer times of course.

It works (at least 99.999%), so there is no need to question it. There is just a tiny detail wrong, which is what i want to find out.

Ok, but the TIM rolls 0 .. 65535, 0 .. 65535 ... with NO intervention on your part.

You have to be in YOUR code to count the roll-overs, which is not guaranteed.

You attempt to compute some total time, with a formula you have constructed, and the compliant reads like this is wrong/off some times.

How critical is this measurement? Why so complex?

What's the span of delays you're trying to manage here? The RTOS persumably has better things to do than dwell in this code of milli-seconds on end? At millisecond(s) yielding would seem more appropriate.

If we're talking delays of a few 100 us, the wrapping shouldn't be a concern, even clocking at 10 MHz rather than 85 or 170 MHz of the MCU/BUS, you should be able to accurately accomodate and measure multiple milli-seconds.

If you want reliablity keep it simple, don't over complicate things.

MCU cycles at 32-bits, perhaps DWT CYC_CNT if you can't free run a 32-bit TIM

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

For a Time Stamp in micro-seconds, consider a 32-bit TIM or MCU cycles DWT CYCNT

Or the SysTick 24-bit DOWN COUNT, with a span determine by your MCU or MCU/8 cycles in a milli-second.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Why is the function which appears to be the TIM16 ISR called  TK_TIM1_UP_TIM16_IRQHandler()?

What is the relationship between TIM1_CC and TIM16 interrupt priorities?

JW

 

PS.

> TIM16->SR &= ~TIM_SR_UIF;

Don't RMW the status register; perform a direct write. Interestingly, you do so for TIM1...

 

To be honest, i cant remember why i added TK_ ... i think it was because i dont use HAL too much.

TIM1 has the higher priority.

I have RMV removed.

Tobe
Senior III

Thanks to waclawek.jan i found the issue:

It was a priority issue, micros() was executed, when the TIM16 interrupt was beeing executed.