Hello,
I am using STM32F407ZGT6 Cortex-M4 microcontroller. This controller has same interrupt vector for 5 external interrupts i.e., NVIC (EXTI9_5) for EXTI5, EXTI6, EXTI7, EXTI8 and EXTI9 interrupts. They all have the same ISR EXTI9_5_IRQHandler interrupt service routine. So, one has to check the independent flags of each external interrupt to determine the source of the interrupt.
I am confused about how the service routine is called in case multiple interrupts arrived. Lets say, an interrupt due to EXTI5 has arrived, its interrupt handler is called and the interrupt is servicing. Meanwhile, EXTI6 interrupt is detected. Now, will the interrupt handler be called again when the EXTI5 interrupt is fully serviced or since it is already in the interrupt handler, it will not be called again?
In short, which method should I write among the following two styles:
void EXTI9_5_IRQHandler (void)
{
if EXTI5 happens
EXTI5 ISR
}
if EXTI6 happens
EXTI6 ISR
if EXTI7 happens
EXTI7 ISR
if EXTI8 happens
EXTI8 ISR
if EXTI9 happens
EXTI9 ISR
OR
else if EXTI6 happens
else if EXTI7 happens
else if EXTI8 happens
else if EXTI9 happens
Thanking in anticipation for your time and help.
Hi Jens ,
In this case, EXTI5, 6, 7, 8, 9 all sharing one input pin on the NVIC.
The STM32's External Interrupt/Event Controller (EXTI) module should be designed to general interrupt request in form of level (rather than pulse). Also, the EXTI module have its own interrupt pending status register for each of the pin. The interrupt requests are merged using an OR function before connecting to the NVIC. (Note: this is specific to STM32 design).
Each of the interrupt service (for each EXTI channel) need to clear their own pending status in the EXTI using EXTI_PR (pending register) to deassert the interrupt to NVIC. If another EXTI is pending, the clearing of the pending status in EXTI is not going to deassert the interrupt line, so the same IRQ will be triggered again and get serviced when the first ISR exited.
Both coding styles should work. But the first one works better (lower overhead) when multiple EXTI interrupts arrive at the same time because it can service multiple EXTI requests in the same ISR execution. The second coding style means it need to exit the ISR, then get back to it again and service the another EXTI request.
regards,
Joseph
Thanks a lot for your reply.
Hmm, it seems simple. But, I don't know why my code get stuck (hanged at some point) when both the enabled interrupts appear at almost same time. Both are configured as hardware external interrupts. One is connected to an accelerometer interrupt pin (rising edge triggered) and the other is to an external pin (both edges triggered) which is pulled up internally and I use a jumper to connect it to GND to activate the interrupt. I don't know whether it is due to the glitches that appear on the interrupt line when I insert or remove that jumper or something else which is causing the code to get hanged.
Many thanks for your time.
Hangs...
Do you have any loops inside your interrupt ?
I would normally blame lock-up and hanging on the software/firmware.
Just a few hints; I do not know if this is what's wrong...
Like I always do: I recommend never to use things like 'printf' from inside an interrupt.
Also try avoiding sending strings via the UART from inside the interrupt.
I normally store some numbers in volatile variables, and then read the values from task-time (eg. the main-loop) and transmit the values there, if I must use UART.
Excellent question.
This is how I understand what happens (simplified slightly):
1: A "pending bit" will be set for your interrupt.
2: When the priority level is lower than your interrupt's priority level, the priority level is set to the one that matches your interrupt; your interrupt will be invoked.
3: You interrupt reads the pending bits; eg. those bits that caused the interrupt, perhaps a new pending bit will be set just after you've read the bits.
4: Clear the pending bits that you've just read. The one that's set just after you read the bits will not be cleared.
5: Process the bits you have in your variable you've read.
6: Return. The priority level is now restored.
7: Because there's now a pending bit, your interrupt will be invoked again.
Here's an example...
void TIMER0_IRQHandler(void)
uint32_t pendingBits;
pendingBits = INTERRUPT_REGISTER;
INTERRUPT_REGISTER = pendingBits; /* this clears the bits that are set in the variable 'pendingBits'. */
if(pendingBits & (1 << 0))
/* handle interrupt for bit 0 */
if(pendingBits & (1 << 1))
/* handle interrupt for bit 1 */
/* ... */
if(pendingBits & (1 << 31))
/* handle interrupt for bit 31 */
... As I am not aware what your interrupt register is named, I've made up a name for it. It should work the same on all Cortex-M as far as I understand.
So if you follow the above mentioned model, you should never 'miss' an interrupt, not even if a pending bit is set right after you've read the value into the pendingBits, and before you're writing the bit-mask to clear.
Also, you should avoid using 'else if', so you'll process all the pending bits, otherwise you'll only process one.
jyiu - please correct me if I'm wrong.
Correction: I just learned something new, so I renamed the above interrupt routine, in order to be a more correct example; by not featuring the EXTI*_IRQHandler interrupts.
I think this method should work for other kinds of interrupts, where you get multiple pending bits per interrupt vector. That could be timer-interrupts, for instance.
Please refer to Joseph's answer, which should clarify the EXTI* interrupts.
I just have find that. may be this can resolve the probleme correctly ?
That's similar to a probleme i have had with the reading the buffer on one network card.
I was reading the manuel of the precedent model, and in fact they have changed the fonctionnement on the new version. The buffer do wasn't cleaned... That producing the mistake.
Read the link
Many thanks for your reply! The problem is solved.
Thanks for the hint @jensbauer!
In fact, I am using a UART port for debugging purposes and the external line hardware interrupt (on which I am putting jumper) is used to enable or disable that port. Due to the glitches appearing on the external line while placing jumper, the interrupt pending bit is sometimes setting again when I used to clear it (as I am clearing the interrupt line at the start of ISR) and in both routines, UART is being disabled. There is a problem in my UART disabling code that it checks some status flags in a while loop to ensure the previous transmission is completed. The flag is usually set (when the previous transmission gets completed) when the UART is enabled but it is cleared (even when the previous transmission gets completed) when UART is disabled. So, disabling the already disabled UART was creating problem as it gets stuck in that while loop. On the other hand, disabling the enabled UART does not create any problems as expected. So, I changed the code little bit and it works perfectly now.
Many thanks to all for your time and help. Sorry for disturbing you as it was a problem in my code. Any ways, I learned a lot specially how the multiple interrupts of the same vector respond.
P.S.: I am not using printf but sending strings through UART inside almost all the interrupts (I am using around 10 interrupts, most of them are software interrupt lines) and using loops as well.
I'm happy to help, even if it's not in the area where the problem is. It's great to hear that you got it working.
Creating working UART code that is free from race-conditions is a challenge.
There are many hidden trapdoors, but since you have it working, I do not need to tell you; you've already encountered them a long time ago, I believe.
Just an additional note, in case you want to find out which pin(s) caused the interrupt, you can save the old state in a variable, and then XOR it with the new state...
ISR { static uint32_t oldState = 0; uint32_t newState; uint32_t changedPins; newState = /* read_GPIO_Pins here */; changedPins = oldState ^ newState; oldState = newState; /* (clear interrupt pending bit here) */ /* now check which pin(s) caused the interrupt: */ if(changedPins & (1 << 0)) { /* pin 0 was changed */ } if(changedPins & (1 << 1)) { /* pin 1 was changed */ } /* ... etc ... */ }
You could also use the CLZ instruction from assembly language (inlined assembly), and optionally the REV instruction before that, in order to find the lowest changed pin.