Showing results for 
Search instead for 
Did you mean: 

Problem with GPIO interrupt, MCU ignores the mode selected.

Senior III

Here's my GPIO setup:

/*Configure GPIO pins : P_H_IN_Pin PCA_INT_Pin P_L_IN_Pin */
  GPIO_InitStruct.Pin = P_H_IN_Pin|PCA_INT_Pin|P_L_IN_Pin;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOJ, &GPIO_InitStruct);

I made a little code that calculates the time in milliseconds between interrupts and output it on the screen. I tested it on timer interrupts and it counts correctly.

Then I set up the PWM generator on a timer that gives 2Hz frequency with variable duty cycle. My reading is 250ms for 50% duty cycle, indicating I get the interrupt on BOTH EDGES! When I change the duty cycle to other values I get 2 interleaved readings, that further confirms I get the interrupt on both edges, because if only falling edge would trigger the interrupt, I would get the same reading no matter what the duty cycle is.

My question is HOW IS IT POSSIBLE? I observe the test signal on oscilloscope. It's a perfect square wave, no artifacts, no noticeable noise or jitter.

The only logical conclusion is I get the interrupt on both edges of the test signal, but why? That kills my application, because I have to measure short impulses repeated over a longer period (it's negative 0-3V3 PP, duty cycle around 5%).

What can be the reason the MCU ignores the GPIO setting and triggers the interrupt also for the rising edge? What should I check and test to further investigate the problem?

My device is STM32H747I-DISCO with TouchGFX application installed. The application seems to be working correctly, because when I provide interrupts from another timer as the impulse source - it measures the correct periods very accurately. The only problem I get is from EXTI line.

I get the interrupt from the HAL_GPIO_EXTI_Callback(uint16_t pin) function. I check the pin number. I also tested it for 2 different pins. Same results. Both inputs are configured to trigger the interrupt on the falling edge, but in both cases I get the interrupts from both edges.

BTW, test signal:


I started new empty project with the issue isolated.

The new project contained only the PWM generator and the EXTI handler.

It worked. Only falling edge detected.

I downloaded my broken old project again into the same device.

Out of the blue it started to work properly.

This is the craziest thing I've seen. The device was clearly broken. After downloading different software and then again the old software it suddenly started to work.


You use layers of software (Cube) somebody else wrote, and maybe you don't understand it's usage properly. "Mysteriously changing" behaviour in this sort of "libraries" often results from init structs being defined as local and not initialized entirely.

The mcu does not work out of Cube function calls, but our of its registers. So if in doubt, always start with readingout and checking those registers.

If you want to have contro​l, write your own code.


Chief II

Have you other EXTI in your code ?

/*Configure GPIO pins : P_H_IN_Pin PCA_INT_Pin P_L_IN_Pin */
  GPIO_InitStruct.Pin = P_H_IN_Pin|PCA_INT_Pin|P_L_IN_Pin;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOJ, &GPIO_InitStruct);

If you init next EXTI on other port but same pin, config is overrided...

And you write about idea measure low duty pulses. From my info use only one edge for this havent real result.


You could have a combination of bugs facing you. Stack size, sw tools versions, always use debug mode and plant breakpoints in interrupt, stop the code and look at the hw registers.

As it would be very probable, it turned out it was a hardware bug I wouldn't expect. It turns out the software behaved correctly. There was indeed a rising slope, however, not visible on oscilloscope using the time base I used. It was a short spike introduced by a combination of a capacitor and the wiring. After removing the capacitance the device worked on the test signal from the PWM generator, however, it refused to work with the real sensor signal due to large, high-frequency spikes present in the singal. I solved the issue by replacing hardware edge detection with a software edge detection and a simple digital filter ignoring high frequency changes. It turns out the problem is low frequency counting with imperfect signal. The problem is really difficult to solve. It would take either additional input buffer and / or advanced digital filter. I'm testing adding some result averaging to provide a reasonable trade of between measuring accuracy and speed. I'm surprised how well H747 behaves sampling the signal at 50kHz.

To measure time between edges, timers are ideal. They are synchronous i.e. they sample and also incorporate digital filters on the input channels.

Contrary to timers, EXTI is asynchronous, i.e. it latches on the shortest possible pulse.


Having a clean input signal that would also be my first shot. I've seen a question similar to mine somewhere. A guy also suspected a bug in STM32 firmware, also obtained spurious rising edge detections, in his case - that happened randomly once every couple of periods. As he also admitted later - he had a spurious spike in the signal that caused additional triggering.

There are 2 ways out of it - either improving analogue input circuit to filter the spikes, noise, improve edges and so on, or - apply digital filtering. There are some filters built in STM32 HAL timers, but I couldn't find the details on them, I'm going to research it later when I'll find some time. Meanwhile I coded my own digital filter that just ignores very short pulses from being reported as edges, that is - a hard low-pass filter. It turned out it works better than expected, for floating input wire it measures exact mains frequency, while on oscilloscope it's just noise with slightly more pronounced 50Hz sine wave. On test signals it measures expected frequencies from 1 to 200Hz with better than 0.1% accuracy. As for motor RPM counter it gives an accurate reading roughly every 100ms. The time base is provided by TIM, the TIM interrupt priority is set to 1, since my counter doesn't call any system functions. As the code is called like 50k times per second, it doesn't use more than like 200 CPU cycles per interrupt. The increased CPU load is not observable. The app's GUI works as responsive as without that counter.

A reasonable point here. In harsh reality - the deadlines and time in general is the limiting factor here. For example I used much less time hacking the broken I2C HAL driver than creating the driver from scratch myself.

There's an old-school approach: people who started the adventure when the public domain software that is now available didn't exist will prefer the low-level. They probably already have a huge own code-base with handy solutions. As a new kid on the block I just needed to make things ASAP, then investigate the inner details. What I already learned is when it doesn't work as I think it should work - I just debug the drivers and test what happens there. Also re-reading the documentation one more time reveals some clues I overlooked.