Interrupt Service Routines double firing on STM32

This is just a quick note to self. Remember, never try and clear the flag that caused the interrupt as the last instruction in the ISR itself. It causes the ISR to reenter immediately. Your ISR/NVIC/EXTI interrupt will retrigger, trigger twice, whatever keyword you were searching for.

void exti9_5_isr(void)
{
    state.my_counter++;
    exti_reset_request(EXTI5);  // Will cause a double interrupt
}

It’s just a matter of doing it first.

void exti9_5_isr(void)
{
    exti_reset_request(EXTI5);  // This will work
    state.my_counter++;
}

Code examples are based on libopencm3, but the same concept applies when using StdPeriphLib

  1. STM’s FAQ “Interrupt re-enters after peripheral’s flag clear” indicates it is due to buffered writes on the peripheral bus. Their suggestion of rereading the flag immediately afterwards will force the write to complete. This seems to work. However an example in libopencm3 in which a flag is written just before the ISR ends, also works, while replacing it with a function call doing the same thing, fails. So the answer doesn’t seem to be so clear cut.

  2. Exactly this is what happened to me with STM32F103.

    I had (EXTI->PR = EXTI_Line3;) in the last line of the ISR, just as suggested by many examples.

    I had EXTI interrupt fired twice and the second time EXTI-PR was ZERO.

    Moving the clear PR to the beginning helped.

  3. Stian Skjelstad

    Just saved my day! Had two different builds from the same code, with different optimize-levels. One of them doubled fired all of the time, causing my signal detection-thing to fail

  4. Christopher Head

    Just moving the clearing code earlier can be unreliable, particularly because a larger divider value between the AHB and APB frequencies means the failure window is larger (in my experiment with timer 5, at a divide of 4 it was sufficient to place 6 NOPs between the store to TIM5_SR and the interrupt return BX LR, but at a divide of 8, if I remember correctly, either 12 or 15 NOPs were needed). Far better to read the register after writing it, thus flushing the write buffer.

    The AMBA specification 2.0, available from ARM, contains an example AHB/APB bridge with a write buffer. It’s reasonable to conclude that ST’s bridge contains a write buffer as a result of this example.

    Note that the bridge write buffer appears to be disabled (thus solving this problem) if you change the memory type of the peripheral region from device to strongly ordered in the MPU. This makes sense, as the bufferable property of the write is exposed on the AHB as wire HPROT[2], so the bridge could respond differently to different types of transactions.

  5. Thanks!
    Searched a day long and wondered why I did not get a double interrupt while stepping with the debugger…

  6. Burkhard Arenfeld

    Thanks!
    The hint with the AHA/APB bridge gave me the right hint.
    I read about memory access in the datasheet (STM32F10x)
    and found the DSB assembly instruction.
    This waits until all memory access is finished and seems
    to solve my problem.

Leave a Comment

NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>