Skip to main content
Associate II
Oct 15, 2026 9:17 AM UTC
Question

Random Double Firing of SysTick_Handler on STM32H7

​Hello together,

my Problem ist that I can find randomly (a few times per hour) timestamps which are delayed by 1ms, after that the delays consist and kind of add up. The cause for this is probably the SysTick_Handler firing 2x in a row and increasing my Uptime_ms timestamp. But here the extended information:

 

I have a STM32H7 connected to a ADC via SAI. As I want precise time measurements my Code for capturing the time in the SAI_IRQ is the following: 

{
uint32_t primask = __get_PRIMASK();

__disable_irq();
SAI_isr_systick = SYSTICK_VALUE;
SAI_isr_uptime_ms = hfwtmr_Uptime;


__set_PRIMASK(primask); // restore PRIMASK state
}

with:

#define SYSTICK_VALUE (SysTick->VAL)
volatile int64_t hfwtmr_Uptime = 0;

volatile int64_t SAI_isr_uptime_ms = 0;

volatile uint32_t SAI_isr_systick = 0;

 

For setting up the Systick HAL_InitTick() is used. As the STM32H7 is running with 480MHz the systick gets loaded to 480000 every time. A screenshot auf the call hierarchy is added after the code segment.

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)

{

/* Check uwTickFreq for MisraC 2012 (even if uwTickFreq is a enum type that don't take the value zero)*/

if((uint32_t)uwTickFreq == 0UL)

{

return HAL_ERROR;

}


/* Configure the SysTick to have interrupt in 1ms time basis*/

if (HAL_SYSTICK_Config(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq)) > 0U)

{

return HAL_ERROR;

}


/* Configure the SysTick IRQ priority */

if (TickPriority < (1UL << __NVIC_PRIO_BITS))

{

HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);

uwTickPrio = TickPriority;

}

else

{

return HAL_ERROR;

}


/* Return function status */

return HAL_OK;

}
 

 

The Systick runs on Priority 0 and these are the following are the only other enabled Priority 0 NVICs. Furthermore  __disable_irq() is never used again in the whole project. 

 

My SysTick_Handler() is pretty short, as it contains only the following functions, which are only used 1 time in the Code. Setting Counters into SysTick_Handler and SysTick_Interrupt_Handler show that they are called the same amount.

void SysTick_Handler(void)

{

SysTick_Interrupt_Handler();

HAL_IncTick();

}
with

void SysTick_Interrupt_Handler()

{

hfwtmr_Uptime++; //increase every milli-second


for (uint16_t idx = 0; idx < HFWTMR_NUM; idx++)

{

if(g_hfwtmr[idx] != 0)

{

--g_hfwtmr[idx];

}

}

}

 

My Problem ist that I can find randomly (a few times per hour) timestamps which are delayed by 1ms.

By logging the SysTick_Handler via DWT->CYCNT I was able to figure out, that in these cases the Systick is firing 2x. In the following table you can see that there are only 874 (15227113-1252270259) ticks in the DWT->CYCCNT between the end of the first SysTick_Handler and the start of the next SysTick_Handler. The number of ticks for the error case of double firing vary between 500 and 1000. Before and after that the SysTick_Handler triggers correctly for roughly an hour. It is not time dependend, as it runs sometime for 20mins without a problem and sometimes for 2h. However it seems like the likelihood of these double firing of the SysTick_Handler is increasing with the computational load on the µC. But as the Systick is the King, no one is allowed to interrupt him (except during the time capturing of the SAI where __disableIRQ is set, so that Systick->Val and Uptime are consistent). 

DWT-Ticks

hfwtmrUptime

Event

info8

info16

 

1252269511

45863

EVENT_SysTick_Interrupt_Handler_Start

39

28581

1252270259

45864

EVENT_SysTick_Interrupt_Handler_End

39

28581

1252271133

45864

EVENT_SysTick_Interrupt_Handler_Start

39

28581

1252271419

45864

EVENT_SysTick_Interrupt_Handler_Bug2xSystick

39

28581

After the red line EVENT_SysTick_Interrupt_Handler_Bug2xSystick the Uptime is incremented again, so it jumps from 45863ms to 45865ms in a timespan of ~1000 ticks.

 

Things I have tried so far:

  • Logging the SCB->ICSR (0xE000ED04UL) at the end of every SysTick_Interrupt_Handler, but there was never a pending Systick in SCB->ICSR Bit26.
  • Adding __DSB() at the end of the SysTick_Interrupt_Handler did not make any difference.
  • Using __enableIRQ() instead of __set_PRIMASK(primask) did not make any difference

 

So my question is, how can I solve the problem that the SysTick_Handler is firing 2x inside of ~1000 ticks.

Please tell my if additional information is missing.

 

Kind regards

Heiko

​

12 replies

LCE
Principal III
Oct 15, 2026 10:00 AM UTC

> … there are only 874 (15227113-1252270259) ticks in the DWT->CYCCNT between the end of the first SysTick_Handler and the start of the next SysTick_Handler.

 

Does the SysTick interrupt have the highest priority, with no other IRQ having the same priority?

 

I check my timing in another way, by taking snapshots from the cycle counter, saving these to an array, then analysing timing later.

__DSB();
__disable_irq();

u32CycStart = CycCntGet();

Function2 Check(); // must not use IRQ

__DMB();
__DSB();
u32Cycles = CycCntGet();

__enable_irq();


/* timing calculation */
u32Cycles -= u32CycStart;
... save u32Cycles to array


 

Heiko_SMAuthor
Associate II
Oct 16, 2026 6:05 AM UTC

As mentioned in my post, SysTick has priority 0. The only other exceptions with the same or higher effective priority are:

  • non maskable interrupt
  • hardfault
  • memory management fault
  • pre-fetch fault memory access fault
  • undefined instruction or illegal state
  • system service call via SWI instructionand debug monitor

Therefore, I would not expect any of these to occur during normal operation without causing more obvious system issues.

Heiko_SMAuthor
Associate II
Oct 16, 2026 6:04 AM UTC

accidental comment

Pavel A.
Oct 19, 2026 12:56 AM UTC

Use only free-running counters (SysTick counter, DWT or other timer) for precise timestamps.

Anything based on counting interrupts can be delayed. 

The SysTick interrupts can occur in less than 1 ms period if it was pending because of __disable_irq or other priority 0 interrupts.

/* Sorry I haven’t read the whole post, could miss some details */

Note that the systick counter is 24 bits, so the 32-bit unsigned subtract trick won’t work. 

Heiko_SMAuthor
Associate II
Oct 19, 2026 6:40 AM UTC

Thank you for your response.

What would be your suggestion for getting a precies timestamp with this current setup?

The systick counting down normally, if it overruns the Uptime variable in ms gets incremented.

The timestamp is currently calculated from the systick and the Uptime. At the point where I want to capture the time, the idea was to disable the IRQ of the systick, so when the systick value gets read and the Uptime value gets read in the next code line it is not possible that the SystickIRQ interrupts in between these 2 lines and increments the Uptime. Because this would result in a Systick value belonging to the Uptime before the interrupt, but an already incremented Uptime, so Systick and Uptime are not consistent. 

TDK
Oct 19, 2026 2:31 AM UTC

EVENT_SysTick_Interrupt_Handler_Start

EVENT_SysTick_Interrupt_Handler_Bug2xSystick

Where are these events actually set? Your code has no mention of them.

Consider the possibility that interrupts are happening properly and the issue is elsewhere. Likely the interrupt is taking too long or is being delayed by some other code which is not shown.

except during the time capturing of the SAI where __disableIRQ is set

Well I know where I would look.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Heiko_SMAuthor
Associate II
Oct 19, 2026 6:46 AM UTC

Thank you for your response.

EVENT_SysTick_Interrupt_Handler_Start is set directly at the Beginning of the SystickIRQ
EVENT_SysTick_Interrupt_Handler_Bug2xSystick is set when there was a SystickIRQ directly after a Systick IRQ without one of my 64µs timer triggering in between (which should not happen as 64µs < 1ms).
The Idea behind these EventCodes was to figure out if the Systick is really triggering 2x or if the code segment where my timestamp gets calculated is just interrupted for long time. But as the DWT->CYCCNT shows that there are only ~1000 ticks between 2 SystickIRQs I was sure that the problem was the SystickIRQ triggering 2x in a row.

 

Maybe the same Question as I had to Pavel to avoid the disableIRQ:

What would be your suggestion for getting a precise timestamp with this current setup?

The systick counting down normally, if it overruns in the SystickIRQ the Uptime variable in ms gets incremented.

The timestamp is currently calculated from the systick and the Uptime. At the point where I want to capture the time, the idea was to disable the IRQ of the systick, so when the systick value gets read and the Uptime value gets read in the next code line it is not possible that the SystickIRQ interrupts in between these 2 lines and increments the Uptime. Because this would result in a Systick value belonging to the Uptime before the interrupt, but an already incremented Uptime, so Systick and Uptime are not consistent.

TDK
Oct 19, 2026 12:54 PM UTC

which should not happen as 64µs < 1ms

Unless of course interrupts were disabled for 64us and a systick happened, in which case it absolutely *will* happen.

 

What would be your suggestion for getting a precise timestamp with this current setup?

Using a free running counter like CYCCNT or any TIMx will give you the most accuracy.

"If you feel a post has answered your question, please click ""Accept as Solution""."
mbarg.1
Senior III
Oct 19, 2026 10:58 AM UTC

As a rule, I always suggest to use RTC for any exact measurement.

RTC does not suffer from any system interference.

RTC can be trimmed to good/high precision, locked to network time reference, optimized for jitter and wander ..

Other timers, rely on internal clocks derived from PLL, pll are analogue items, even while locked can be reset or altered by multiple other peripherals or sw.

Unless you have some reason not to use RTC ...