AnsweredAssumed Answered

"Safe" version of HAL_GetTick()?

Question asked by Ed Maynard on Nov 9, 2017
Latest reply on Nov 13, 2017 by Ed Maynard

Using the STM32F413 Nucleo-144 and STM32F091 Nucleo-64 evaluation boards. I am using a combination of HAL_GetTick() and a read of the countdown timer to generate higher resolution time stamps for events. Using the CubeMX emitted code so no funny business there - 1ms resolution.

 

So, I have a test system that timestamps a GPS PPS signal. In general, there aren't any problems - intervals between timestamps are constant and close to 1000msec (typically within 40us). All good.

 

However, about 0.5% of the time, the value of uwTick is off by one count (equivalent to 1msec error). The next interval "catches up" with an extra millisecond to maintain the correct _average_ interval (i.e. 999msec and the next is 1001msec). Really quite stable over long periods. Anyway, unsurprisingly, this happens when the program is accessing the global uwTick while it is being changed by the ISR. This is not an unusual problem with reading timers and I have tried to implement the software equivalent of a "dirty" bit but the 1msec error stubbornly persists.

 

Specifically:

1) In SysTick_Handler declare a global value "uwDirtyTimer" that is set upon entry into the ISR. The app calling HAL_GetTimer() first clears this, reads uwTick, and then checks to see if the value changed during the read. This did not eliminate the issue.

2) Sample uwTick multiple times (quickly) and use a "voting" scheme. This helped but there appear to be corner cases that still sneak through.

3) _inline_ everything to eliminate the overhead of function calls. Would have though optimization would do this but this cut down on the numbers of lagging reads the most.

 

On a 48MHz clock, it takes very nearly 1usec to execute the ISR not including getting into or returning from the ISR and including the GPIO port set can clear (for the o-scope). It takes 500ns (GPIO2_HIGH to GPIO2_LOW) to execute the following code:

 

#pragma inline = forced
void HAL_Precision_GetTick(PrecisionSysTick_t *time)
{
GPIO2_HIGH;
bSysTickDirty = 0;
time->msec = uwTick;
time->fraction = SysTick->VAL;
if (bSysTickDirty)
{
GPIO3_HIGH;
time->msec = uwTick;
GPIO3_LOW;
}
GPIO2_LOW;
}

 

The scope triggers on GPIO2 and GPIO3 being high but the value that ends up read from uwTick is still one count off - almost as if it still hasn't finished updating - but the ISR has completed at this point.

 

To date, the best solution has been an inelegant one that involves looking that the value of the SysTick counter timer (SysTick->VAL). If this value is greater than 0x0000BB1C then I assume that a roll has occurred and add one to the value. However, no surprise here, instead of losing a tick, sometimes I gain one. I noticed this from the observation that timestamps that were 1ms off consistently reported a fractional part rounded to 0.002 (2us). Hence, "best" solution but not "fixed".

 

Has anyone out there figured out a working way to implement a "safe" read of uwTick? Basically, my approaches are band-aids for the fact that for whatever reason, I cannot ascertain when the value of uwTick is correct in RAM or at least has changed since I read it.

Outcomes