Skip to content

Commit efd2c7b

Browse files
Make getCurrentMicros()/micros() ISR-safe
The original implementation had two problems: - It was not re-entrant: It cleared COUNTFLAG at startup and then used that flag to detect if an overflow happened while reading the timer values. If then another ISR would happen between the overflow (flag set) and reading the flag, and that ISR would call micros(), the flag would be cleared again and the original micros() call would return the wrong value. - It was sometimes wrong when called with interrupts disabled (e.g. inside an ISR): When COUNTFLAG was set, the code would call HAL_GetTick() again, assuming that it would return an updated value. However, when interrupts are disabled, the systick overflow interrupt cannot run and HAL_GetTick() just returns the same value again, and micros() returns the wrong value. This commit fixes both problems: - Rather than relying on HAL_GetTick() to return an updated value, just increment the value returned. This works with interrupts disabled, and even requires interrupts to be disabled to make sure that the pending bit is set (rather than running the systick ISR immediately) and also guarantee that the original value is from *before* the overflow (if any). - Becauses all interrupts are now disabled, another ISR can no longer interrupt this code and interfere with the result. Rather than disabling interrupts entirely, it could have been enough to just disable the systick irq. However, disabling the systick irq also prevents its pending bit from being set, so this would just cause overflows to be missed entirely (maybe it could be made to work by detecting overflows based on the systick value and then manually *setting* PENDSTSET to have them processed later, but that might be fragile).
1 parent 0de7157 commit efd2c7b

File tree

1 file changed

+16
-8
lines changed
  • libraries/SrcWrapper/src/stm32

1 file changed

+16
-8
lines changed

libraries/SrcWrapper/src/stm32/clock.c

+16-8
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,24 @@ extern "C" {
5151
*/
5252
uint32_t getCurrentMicros(void)
5353
{
54-
/* Ensure COUNTFLAG is reset by reading SysTick control and status register */
55-
LL_SYSTICK_IsActiveCounterFlag();
54+
uint32_t primask = __get_PRIMASK();
55+
__disable_irq();
56+
5657
uint32_t m = HAL_GetTick();
57-
const uint32_t tms = SysTick->LOAD + 1;
58-
__IO uint32_t u = tms - SysTick->VAL;
59-
if (LL_SYSTICK_IsActiveCounterFlag()) {
60-
m = HAL_GetTick();
61-
u = tms - SysTick->VAL;
58+
__IO uint32_t v = SysTick->VAL;
59+
// If an overflow happened since we disabled irqs, it cannot have been
60+
// processed yet, so increment m and reload VAL to ensure we get the
61+
// post-overflow value.
62+
if (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk) {
63+
++m;
64+
v = SysTick->VAL;
6265
}
63-
return (m * 1000 + (u * 1000) / tms);
66+
67+
// Restore irq status
68+
__set_PRIMASK(primask);
69+
70+
const uint32_t tms = SysTick->LOAD + 1;
71+
return (m * 1000 + ((tms - v) * 1000) / tms);
6472
}
6573

6674
/**

0 commit comments

Comments
 (0)