Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with multiple GPIO interrupts #3277

Open
FabrizioRomanoGenovese opened this issue Mar 18, 2025 · 5 comments
Open

Problem with multiple GPIO interrupts #3277

FabrizioRomanoGenovese opened this issue Mar 18, 2025 · 5 comments

Comments

@FabrizioRomanoGenovese
Copy link

Hello all. I have the following situation: I need to trigger different functions depending on a bunch of GPIO interrupts. I'm working with an ESP32c6. In this example I'm assuming I only have 2 GPIO interrupts set but in reality there are 4.

In short, I have a receiving_pin and a herald_pin. The way my application setting works is that

  • receiving_pin is set high;
  • I receive one or less pulses on herald_pin;
  • receiving_pin goes low.

To implement this, I proceeded as follows: First of all, I defined:

static RECEIVING_PIN: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
static HERALD_PIN: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));

Then, I defined the following interrupt handler function:

#[handler]
#[ram] //Placed in RAM for faster execution
fn handler() {
    critical_section::with(|cs| {
        let mut receiving_pin = RECEIVING_PIN.borrow_ref_mut(cs);
        let receiving_pin = receiving_pin.as_mut().unwrap();
        if receiving_pin.is_interrupt_set() {
            debug!("[ handler ] RECEIVING was the source of the interrupt");
            if receiving_pin.is_high() {
                execute_something(cs)
            } else {
                execute_something_else(receiving_sec);
            }
            receiving_pin.clear_interrupt();
        }
    });

    critical_section::with(|othercs| {
        let mut herald_pin = HERALD_PIN.borrow_ref_mut(othercs);
        let herald_pin = herald_pin.as_mut().unwrap();
        if herald_pin.is_interrupt_set() {
            debug!("[ handler ] HERALD was the source of the interrupt");
            execute_something_more(herald_sec);
            herald_pin.clear_interrupt();
        }
    });

In the main function, I defined the pins to be used:

    let receiving_pin = Input::new(peripherals.GPIO21, InputConfig::default().with_pull(Pull::Down));
    let herald_pin = Input::new(peripherals.GPIO22, InputConfig::default().with_pull(Pull::Down));
    critical_section::with(|initialize_sec| {
        RECEIVING_PIN.borrow_ref_mut(initialize_sec).replace(receiving_pin);
        HERALD_PIN.borrow_ref_mut(initialize_sec).replace(herald_pin);
    });
   receiving_pin.listen(Event::AnyEdge);
   herald_pin.listen(Event::RisingEdge);

Given my application, I would expect to see:

DEBUG [ handler ] RECEIVING was the source of the interrupt
DEBUG [ handler ] HERALD was the source of the interrupt
DEBUG [ handler ] RECEIVING was the source of the interrupt

Instead, my logs look like so:

DEBUG [ handler ] RECEIVING was the source of the interrupt
DEBUG [ handler ] HERALD was the source of the interrupt
DEBUG [ handler ] HERALD was the source of the interrupt
DEBUG [ handler ] RECEIVING was the source of the interrupt

Basically, HERALD is fired multiple times even if it just goes high once. Interestingly, this doesn't happen with RECEIVING, which works perfectly. Is there something I'm doing tragically wrong which entails this unwanted behavior?

BTW, The library I'm working on is public, and available here in case you want to look at the full source code.

@bjoernQ
Copy link
Contributor

bjoernQ commented Mar 19, 2025

Just out of curiosity (and it shouldn't cause this): What is the timing between changing the state of RECEIVING and the state of HERALD?

Does it change anything if you remove the block

            debug!("[ handler ] RECEIVING was the source of the interrupt");
            if receiving_pin.is_high() {
                execute_something(cs)
            } else {
                execute_something_else(receiving_sec);
            }

(i.e. if it's caused by generating second interrupt while the first interrupt is still being serviced)

@bugadani
Copy link
Contributor

bugadani commented Mar 19, 2025

GPIO may not be a good fit to count high frequency pulses. You may want to look at PCNT for that purpose. (Obviously if you're doing something else and not just counting, it doesn't necessarily apply).

@FabrizioRomanoGenovese
Copy link
Author

FabrizioRomanoGenovese commented Mar 19, 2025

Hello all,
It turned out that the problem wasn't really in my code, but in whatever code was being supplied on the other side of my pins 😅 I hooked my ESP32 to a simple arduino setting pins high and low in a loop and it didn't miss a beat. Sorry for having wasted your time!

As for the pulse counter: Are there any advantages in using PCNT over my "naive" approach? Would the pulse counter perform better for very short pulses, fired at very little time from each other? In this respect, what are the ESP32 capabilities when it comes to interrupts being triggered? For what I've seen 5 microsecond width pulses on different pins, fired at 5 microsecond intervals between each other are still detected fine. How low can one presumably go?

@bjoernQ
Copy link
Contributor

bjoernQ commented Mar 20, 2025

Great to hear it's working fine for you

PCNT should be able to process signals up to 40MHz but seems you are around 200kHz

Interrupt latency depends on a few factors (cache hits/misses and placing code in RAM vs FLASH, if other code is heavily using critical-sections etc.) so it's hard to tell valid numbers

@FabrizioRomanoGenovese
Copy link
Author

Hello all, sorry to come back to this. I can also open a new issue if it's better. I am basically trying to understand if using the PCNT module buys me something in terms of performance or not.

My scenario is the following: As soon as I detect a rising edge on a given pin, I have to trigger an interrupt and do some things. I can clearly use the PCNT module for this, but basically the pulse count would stop at 1 😄. So my question is: In my application, would using PCNT buy me something in terms of latency/performance instead of using the simple herald_pin.listen(Event::RisingEdge); as I did in the first post?
I guess the answer to this ultimately depends on how things are implemented at the hardware level, of which I know very little.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Todo
Development

No branches or pull requests

3 participants