Author Topic: STM32: Unexpected interrupts  (Read 1340 times)

0 Members and 1 Guest are viewing this topic.

Offline AJ528Topic starter

  • Regular Contributor
  • *
  • Posts: 64
  • Country: us
STM32: Unexpected interrupts
« on: July 26, 2024, 05:22:18 pm »
I am programming on an Arm M4 microcontroller, and using a timer module and DMA to generate 6 pulses with different periods (between 0.5 and 1 second). The DMA reloads the timer every time it overflows, and raises an interrupt when there is no more data to transfer. In the DMA interrupt, I clear the timer's overflow interrupt flag, enable the NVIC timer interrupt, and expect to get an interrupt the next time it overflows. Instead, I get an interrupt immediately upon exiting the DMA interrupt. Does anyone know why this is happening?

This situation only occurs if the timer's overflow interrupt flag is enabled during setup. If the overflow interrupt flag and NVIC timer interrupt are both enabled within the DMA interrupt, there is no immediate timer interrupt. But in both situations I clear the overflow interrupt flag immediately before enabling the NVIC interrupt, so I don't understand the difference.

I am happy to provide my code and more technical information, but I wanted to start with a simple version of my question in case I'm just not understanding something about interrupts on this microcontroller.
 

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2721
  • Country: us
Re: STM32: Unexpected interrupts
« Reply #1 on: July 26, 2024, 06:04:13 pm »
This situation only occurs if the timer's overflow interrupt flag is enabled during setup. If the overflow interrupt flag and NVIC timer interrupt are both enabled within the DMA interrupt, there is no immediate timer interrupt. But in both situations I clear the overflow interrupt flag immediately before enabling the NVIC interrupt, so I don't understand the difference.

Clearing the interrupt enable bit in a peripheral doesn't clear an interrupt that is already pending for that peripheral in the NVIC.  So if the NVIC has already registered the interrupt request for the timer and set that to pending when the DMA ISR is entered, then you're getting exactly the expected behavior.  You can either enable the timer overflow interrupt in the DMA ISR as you mention, or you can clear the pending timer interrupt in the NVIC before exiting the DMA ISR.  (The latter has a possible secondary trap in that if the timer hardware is slower to respond to the OVIE bit being cleared than the NVIC is to the pending interrupt being cleared, the timer may request another interrupt after the NVIC is cleared.)
 
The following users thanked this post: AJ528

Offline AJ528Topic starter

  • Regular Contributor
  • *
  • Posts: 64
  • Country: us
Re: STM32: Unexpected interrupts
« Reply #2 on: July 26, 2024, 07:17:09 pm »
I believe I am already clearing the pending timer interrupt in the NVIC before exiting the DMA ISR. The last two lines in my DMA isr are
Code: [Select]
LL_TIM_ClearFlag_UPDATE(TIM17);
NVIC_EnableIRQ(TIM17_IRQn);
Does that not clear the pending timer interrupt in the NVIC?
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2421
  • Country: gb
Re: STM32: Unexpected interrupts
« Reply #3 on: July 26, 2024, 08:08:19 pm »
Maybe this?
Code: [Select]
LL_TIM_ClearFlag_UPDATE(TIM17);
NVIC_ClearPendingIRQ(TIM17_IRQn);
NVIC_EnableIRQ(TIM17_IRQn);
 
The following users thanked this post: AJ528

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2721
  • Country: us
Re: STM32: Unexpected interrupts
« Reply #4 on: July 26, 2024, 08:22:32 pm »
I believe I am already clearing the pending timer interrupt in the NVIC before exiting the DMA ISR. The last two lines in my DMA isr are
Code: [Select]
LL_TIM_ClearFlag_UPDATE(TIM17);
NVIC_EnableIRQ(TIM17_IRQn);
Does that not clear the pending timer interrupt in the NVIC?

If you look at the definition of `LL_TIM_ClearFlag()` you'll see it's defined as (something like) the following:

Code: [Select]
__STATIC_INLINE void LL_TIM_ClearFlag_UPDATE(TIM_TypeDef *TIMx)
{
  WRITE_REG(TIMx->SR, ~(TIM_SR_UIF));
}

So that's only clearing the flag in the timer status register, and doesn't do anything about the IRQ in the NVIC.  `NVIC_ClearPendingIRQ` is the function that would would do that, as voltsandjolts shows. 
 
The following users thanked this post: AJ528

Offline AJ528Topic starter

  • Regular Contributor
  • *
  • Posts: 64
  • Country: us
Re: STM32: Unexpected interrupts
« Reply #5 on: July 26, 2024, 08:37:49 pm »
oh, so the timer IRQ can be set as pending in the NVIC even before the NVIC timer IRQ is enabled? That is not what I was expecting. So the "NVIC_EnableIRQ" command isn't enabling the IRQ as much as it's unmasking the IRQ signal. The NVIC was listening the whole time.

What would happen if I ran "NVIC_ClearPendingIRQ(TIM17_IRQn)" in the DMA isr, but didn't run "LL_TIM_ClearFlag_UPDATE(TIM17)"? At that point the pending IRQ is cleared, but there is still an interrupt flag in TIM17's status register. Would another interrupt get triggered immediately? Or is the IRQ only set to pending on the rising edge of that interrupt flag?
 

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2721
  • Country: us
Re: STM32: Unexpected interrupts
« Reply #6 on: July 26, 2024, 09:41:38 pm »
oh, so the timer IRQ can be set as pending in the NVIC even before the NVIC timer IRQ is enabled? That is not what I was expecting. So the "NVIC_EnableIRQ" command isn't enabling the IRQ as much as it's unmasking the IRQ signal. The NVIC was listening the whole time.

It would be a bit more accurate to say that NVIC_EnableIRQ enables the interrupt to become active (ie, jump to the ISR and execute it).  This allows you to prevent entry into an ISR while still allowing the corresponding interrupt to become pending so that it can be handled later.  This also ties in with the way that preemption and priority masking are handled, which also affect when a particular interrupt is taken.

Quote
What would happen if I ran "NVIC_ClearPendingIRQ(TIM17_IRQn)" in the DMA isr, but didn't run "LL_TIM_ClearFlag_UPDATE(TIM17)"? At that point the pending IRQ is cleared, but there is still an interrupt flag in TIM17's status register. Would another interrupt get triggered immediately? Or is the IRQ only set to pending on the rising edge of that interrupt flag?

The NVIC is part of the ARM core, so if you want to get into all of the gritty details for the M4/M7, you want the ARMv7-M Architecture Reference Manual, specifically (since I happen to have that open here) sections B1.5 and B3.4.  B3.4.1 explains how the NVIC handles external interrupt lines:

Quote
ARMv7-M supports level-sensitive and pulse-sensitive interrupt behavior. This means that both level-sensitive and pulse-sensitive interrupts can be handled. Pulse interrupt sources must be held long enough to be sampled reliably by the processor clock to ensure they are latched and become pending. A subsequent pulse can add the pending state to an active interrupt, making the status of the interrupt active and pending. However, multiple pulses that occur during the active period only register as a single event for interrupt scheduling.

In summary:

• Pulses held for a clock period act like edge-sensitive interrupts. These can become pending again while the interrupt is active.

Note:
A pulse must be cleared before the assertion of AIRCR.VECTCLRACTIVE or the associated exception return, otherwise the interrupt signal behaves as a level-sensitive input and the pending bit is asserted again.

• Level-based interrupts become pending, and then make the interrupt active. The Interrupt Service Routine (ISR) then accesses the peripheral, causing it to deassert the interrupt. If the interrupt is still asserted on return from the ISR, it becomes pending again.

So, it's up to the implementer (meaning the MCU manufacturer) whether the peripheral issues a pulse on the rising edge of an interrupt flag, or simply holds the interrupt line active as long as there's an active and enabled interrupt flag.  In general, I would assume the latter, since each interrupt flag can just be ANDed with its enable bit, and then the results for all interrupt flags ORed together into the NVIC interrupt line.  So in that case, yes, if you do NVIC_ClearPendingIRQ before clearing the overflow flag, you would immediately get another IRQ pending. 
« Last Edit: July 26, 2024, 09:46:32 pm by ajb »
 
The following users thanked this post: AJ528

Offline WatchfulEye

  • Regular Contributor
  • *
  • Posts: 123
  • Country: gb
Re: STM32: Unexpected interrupts
« Reply #7 on: July 31, 2024, 04:58:21 pm »
I believe I am already clearing the pending timer interrupt in the NVIC before exiting the DMA ISR. The last two lines in my DMA isr are
Code: [Select]
LL_TIM_ClearFlag_UPDATE(TIM17);
NVIC_EnableIRQ(TIM17_IRQn);
Does that not clear the pending timer interrupt in the NVIC?
There may be a bus-architecture timing issue here. This is MCU specific and dependent on clock configuration (but for faster cores, such as M4, it is common for the peripherals to be on an APB bus which is clocked slower than the AHB system bus). Careful reading of the datasheet is required.

This issue can cause interrupt handlers to trigger twice if clearing of interrupt flag on the peripheral is left to the last instruction (the APB does not have time to complete the bus transaction before the interrupt handler completes, at which point the NVIC sees the interrupt still pending, and so restarts the handler).

A solution is to move the act of clearing the interrupt flag to earlier in the handler, or add a dummy transaction (e.g. just run the clear operation twice) on the same APB (a second APB transaction will block while the first is still pending).

If this is the problem, I would expect the following code to fix it:
Code: [Select]
LL_TIM_ClearFlag_UPDATE(TIM17);
LL_TIM_ClearFlag_UPDATE(TIM17);
NVIC_EnableIRQ(TIM17_IRQn);
 

Offline AJ528Topic starter

  • Regular Contributor
  • *
  • Posts: 64
  • Country: us
Re: STM32: Unexpected interrupts
« Reply #8 on: August 01, 2024, 07:25:45 pm »
In this situation it appears that ajb hit the nail on the head. Using voltsandjolts's suggestion of NVIC_ClearPendingIRQ(TIM17_IRQn); cleared the interrupt exactly as I was expecting. I also realized that LL_TIM_ClearFlag_UPDATE() and LL_TIM_EnableIT_UPDATE() have the same relationship as LL_TIM_ClearFlag_UPDATE() and NVIC_EnableIRQ(): even if the interrupt is disabled in the Timer Module and NVIC, the UIF flag in TIMx_SR is always watching for an interrupt condition. If UIF is set when you run LL_TIM_EnableIT_UPDATE(), an interrupt will immediately happen, so if you don't want that be sure to clear the flag before enabling.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf