2024-10-29 01:21 AM - edited 2024-10-29 12:22 PM
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();
....
Solved! Go to Solution.
2024-10-29 03:50 PM
Thanks to waclawek.jan i found the issue:
It was a priority issue, micros() was executed, when the TIM16 interrupt was beeing executed.
2024-10-29 02:07 AM
> Here the actual point int the code where the error happend
What error?
JW
2024-10-29 10:12 AM
micros() returned a time, that had 772us less, than it should have returned.
2024-10-29 01:44 PM
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?
2024-10-29 01:54 PM
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.
2024-10-29 02:24 PM - edited 2024-10-29 02:26 PM
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
2024-10-29 02:31 PM
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.
2024-10-29 02:49 PM
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...
2024-10-29 03:33 PM
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.
2024-10-29 03:50 PM
Thanks to waclawek.jan i found the issue:
It was a priority issue, micros() was executed, when the TIM16 interrupt was beeing executed.