Search code examples
cembeddedstm32h7

EXTI interrupt fires twice on STM32H7


I'm working on a custom board including a STM32H723. From a FPGA I have a simple signal that pulses for a few 1us at roughly 50kHz. This goes to an IO pin.

I've hooked up an interrupt EXTI15_10_IRQHandler to trigger on the rising edge. Inside this routine I increment a volatile variable and toggles an IO pin too. I clear the pending bit in the EXTI-PR1 register too.

#pragma GCC push_options
#pragma GCC optimize ("O2")

volatile unsigned int intspersec = 0;

void EXTI15_10_IRQHandler(void)
{
    SCOPE_DEBUG_0;      // Toggles external IO pin
    intspersec++;       // Count em 

    // NVIC_ClearPendingIRQ(EXTI15_10_IRQn); // Does not help 
    if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) {
        LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14);
    } // endif

    // Lets waste some time 
    for (volatile unsigned int n=0;n<1;n++) {

    } // endfor
    return; 
}

#pragma GCC pop_options

The interrupt is set to falling edge and I've verified that changing it to rising edge makes the expected difference in the timing.

The problem is that the above interrupt handler is executed twice - see scope shot.

enter image description here

The top trace is the debug IO pin, while the lower (analog) is the input PE14 pin.

The absolute weird part is that if I waste a little more time in the ISR like this

    // Lets waste some time 
    for (volatile unsigned int n=0;n<2;n++) {

    } // endfor

I get a single invocation of the ISR as expected...

I've tried running the code with or without ICache/DCache etc. but it does not seem to make any consistent differences.

We've done many many designs with the STM32 family (F1,F4,G0,L0,L1..) but this is the first H7 design. Of course we can work around this issue, but I would prefer understanding why it happens, so that it does not end up biting me, in some other form, later.


Solution

  • This is normal and is caused by the interrupt clear propagating to NVIC slower than code in rest of ISR is executed.

    The standard way to cope with this is to qualify all interrupt sources even if there's only one of them. Clearing the flag causing the interrupt early on helps avoiding the "false" entry, but even if it happens it's then harmless.

    void EXTI15_10_IRQHandler(void) {
    
      // NVIC_ClearPendingIRQ(EXTI15_10_IRQn); // Does not help 
      if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) {
        LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14);
        SCOPE_DEBUG_0;      // Toggles external IO pin
        intspersec++;       // Count em 
      } // endif
    }