Skip to main content
Graduate
October 29, 2024
Solved

My micros() function is not glitchfree?

  • October 29, 2024
  • 4 replies
  • 2224 views

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();
			....

 

 

 

 

 

 

 

 

 

    This topic has been closed for replies.
    Best answer by Tobe

    Thanks to waclawek.jan i found the issue:

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

    4 replies

    Super User
    October 29, 2024

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

    What error?

    JW

    TobeAuthor
    Graduate
    October 29, 2024

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

    Graduate II
    October 29, 2024

    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?

    Super User
    October 29, 2024

    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...

     

    TobeAuthor
    Graduate
    October 29, 2024

    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.

    TobeAuthorAnswer
    Graduate
    October 29, 2024

    Thanks to waclawek.jan i found the issue:

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

    TobeAuthor
    Graduate
    October 29, 2024

    I made this change (is it good or ugly?):

    #define CHECK_AND_DISABLE_IRQ uint32_t pMask = __get_PRIMASK(); __disable_irq();
    #define ENABLE_IRQ_IF if(!pMask) __enable_irq();
    
    void TK_TIM1_UP_TIM16_IRQHandler(void){
    	TEST_TIM16_INT_ON
    	if(TIM16->SR & TIM_SR_UIF){
    		CHECK_AND_DISABLE_IRQ
    		TIM16->SR = ~TIM_SR_UIF; // reset flag
    		millisFract += FRACT_INC;
    
    		if (millisFract >= FRACT_MAX) {
    			millisFract -= FRACT_MAX;
    			t_millis++;
    		}
    		timer_overflow_count++;
    		ENABLE_IRQ_IF
    	}
    	TEST_TIM16_INT_OFF
    }