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

ESP32 INTERRUPT ON BOTH FALLING AND RISING EDGE #1111

Open
usmanshahid001 opened this issue Feb 14, 2018 · 41 comments
Open

ESP32 INTERRUPT ON BOTH FALLING AND RISING EDGE #1111

usmanshahid001 opened this issue Feb 14, 2018 · 41 comments
Labels
Status: Needs investigation We need to do some research before taking next steps on this issue

Comments

@usmanshahid001
Copy link

Hiii i am using proximity sensor with esp32 and it is calling ISR routine on both Falling and Rising Edge
i have verified it using nodemcu which is working properly i have tried all things like Falling Rising Change HIGH but its showing the same behaviour kindly help me in this regard...

@dazjd02
Copy link

dazjd02 commented Feb 16, 2018

I am using an interrupt on io34 and having the same issue; I have a 10K resistor pulling it high to try to stabilized it. It seems to show the same behavior whether FALLING or RISING is used. Sometimes interrupt occurs on the falling edge and other times it occurs on the rising edge and switches back every few seconds.

@usmanshahid001
Copy link
Author

actually its working on change no matter which mode is selected...

@usmanshahid001
Copy link
Author

i am having interrupts both on falling and rising edge .........how to resolve that problem related to gpio

@yashaelfaith
Copy link

i also have the same problem. I am using an interrupt on pin 27, i tried using FALLING and RISING but it seems that the ISR got trigger on CHANGE. are there any solution to this problem?

@usmanshahid001
Copy link
Author

i have check using esp-idf gpio code that is working fine for positive edge trigger....but not working in arduino............

@usmanshahid001
Copy link
Author

#include "driver/gpio.h"

#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0 4
#define GPIO_INPUT_IO_1 5
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0
extern "C" {
#include "soc/gpio_struct.h"
}

static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
Serial.println("yes");

}

void setup()
{

Serial.begin(115200);
gpio_config_t pGPIOConfig
{
GPIO_INPUT_PIN_SEL,
GPIO_MODE_INPUT,
GPIO_PULLUP_ENABLE,
GPIO_PULLDOWN_DISABLE,
GPIO_INTR_POSEDGE,
};
pGPIOConfig.intr_type=GPIO_INTR_POSEDGE;
gpio_config(&pGPIOConfig);
gpio_set_intr_type(GPIO_NUM_5, GPIO_INTR_POSEDGE);
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_5, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);
 gpio_isr_handler_remove(GPIO_NUM_4);
//hook isr handler for specific gpio pin again
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

int cnt = 0;

}
void loop()
{

}

@usmanshahid001
Copy link
Author

working in this way

@musskopf
Copy link

musskopf commented Jul 5, 2018

I can confirm the issue is happening to me as well. My test code looks like:

// User Buttons
#define USER_BT1      25

/*
 * Interrupt Definitions
 */
volatile uint8_t bt1Pressed = 0;

void bt1IntHandler()
{
  bt1Pressed++;
}


void setup() {
  Serial.begin(115200);
  Serial.println("Starting Up");
  
  Serial.println(" - User Buttons");
  pinMode(USER_BT1, INPUT_PULLUP);

  // Some Delay for things to calm down
  delay(30);

  // Add Interrupts
  attachInterrupt(digitalPinToInterrupt(USER_BT1), bt1IntHandler, FALLING);
}

void loop() {
  // Print the number of interrupts every 3 second
  Serial.print("Number of Interrupts: ");
  Serial.println(bt1Pressed);
  delay(3000);
}

I also capture the button press on my scope, no noise in the signal.
ds1104z_ds1za180300224_2018-07-05_15 23 06

The output on the Serial Print always increment by "2" every time I press the button. If I press and hold, it increments 1, but it'll increment 1 again once I release it.

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:956
load:0x40078000,len:0
load:0x40078000,len:13256
entry 0x40078a90
Starting Up
 - User Buttons
Number of Interrupts: 0
Number of Interrupts: 2 <- Quick Press and Release
Number of Interrupts: 4 <- Another Press and Release
Number of Interrupts: 5 <- Press and Hold
Number of Interrupts: 5 <- Still holding
Number of Interrupts: 6 <- Released
Number of Interrupts: 6

@usmanshahid001
Copy link
Author

usmanshahid001 commented Jul 5, 2018 via email

@usmanshahid001
Copy link
Author

try this code

#include "driver/gpio.h"

#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0 4
#define GPIO_INPUT_IO_1 5
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0
extern "C" {
#include "soc/gpio_struct.h"
}

static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
Serial.println("yes");

}

void setup()
{

Serial.begin(115200);
gpio_config_t pGPIOConfig
{
GPIO_INPUT_PIN_SEL,
GPIO_MODE_INPUT,
GPIO_PULLUP_ENABLE,
GPIO_PULLDOWN_DISABLE,
GPIO_INTR_POSEDGE,
};
pGPIOConfig.intr_type=GPIO_INTR_POSEDGE;
gpio_config(&pGPIOConfig);
gpio_set_intr_type(GPIO_NUM_5, GPIO_INTR_POSEDGE);
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_5, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);
gpio_isr_handler_remove(GPIO_NUM_4);
//hook isr handler for specific gpio pin again
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

int cnt = 0;
}
void loop()
{

}

@usmanshahid001
Copy link
Author

attach interrupt function is not working as expected

@musskopf
Copy link

musskopf commented Jul 5, 2018

Yes, I saw it, many thanks!

In any case, I was highlighting that this still a problem with the Arduino "attachInterrupt" function.

@dazjd02
Copy link

dazjd02 commented Jul 5, 2018 via email

@william-ferguson-au
Copy link
Contributor

william-ferguson-au commented Jul 8, 2018

Just to be clear this happens with the ESPIF libraries too. Not just the Arduino attachInterrupt() function.

Using the same board that @musskopf provided images for above and the following:

volatile uint8_t triggerPressed = 0;

static void IRAM_ATTR triggerIntHandler(void *args) {
    triggerPressed = 1;
}

void setup_hardware() {
    log("Initialising Pins");

    // input pins
    gpio_config_t gpioInputConfig;
    gpioInputConfig.pin_bit_mask = 1ULL << TRIGGER_BT;
    gpioInputConfig.mode = GPIO_MODE_INPUT;
    gpioInputConfig.pull_up_en = GPIO_PULLUP_ENABLE;
    gpioInputConfig.pull_down_en = GPIO_PULLDOWN_DISABLE;
    gpioInputConfig.intr_type = GPIO_INTR_POSEDGE;
    ESP_ERROR_CHECK(gpio_config(&gpioInputConfig));

    ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_IRAM));
    gpio_isr_handler_add(TRIGGER_BT, triggerIntHandler, NULL);
}

void loop() {
    while (1) {
        if (triggerPressed) {
            triggerPressed = 0;
            Serial.println("Responding to trigger pressed");
        }

        ...
    }
}

The trigger fires when it is depressed and then once again when it is released.
But it should fire only for GPIO_INTR_POSEDGE.

As you can see from the screenshots from @musskopf the signal is clean.
So what needs to be done to get it to fire on a single edge instead of both edges?

@stickbreaker
Copy link
Contributor

@william-ferguson-au If the interrupt is allocated to a shared interrupt it only act as a level interrupt. This design choice is required because because of the interrupt latency of sharing an interrupt. The ISR has to do the filtering. Currently pin interrupt routine assumes the hardware will filter base on selection (FALLING, RISING, LOW, HIGH, CHANGE).

I assume(ass u me) that the code has been tested in a single interrupt(non Shared) environment. On a non shared interrupt it will correctly respond.

I ran into the same problem when I was building the I2C ISR.
I cheated, I allocated the interrupt on edge and specified the interrupt status register for my peripheral, and a mask. This allows FreeRTOS to only call my ISR when an Edge happens and the Interrupt status bit is high in the StatusRegister.

    if(!i2c->intr_handle) { // create ISR for either peripheral
        // log_i("create ISR %d",i2c->num);
        uint32_t ret = 0;
        uint32_t flags = ESP_INTR_FLAG_EDGE |  //< Edge-triggered interrupt
          ESP_INTR_FLAG_IRAM |  //< ISR can be called if cache is disabled
          ESP_INTR_FLAG_LOWMED;   //< Low and medium prio interrupts. These can be handled in C.

        if(i2c->num) {
            ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x1FFF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
        } else {
            ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x1FFF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
        }

i2c->dev=>int_status.val is the status register, 0x1fff is the mask to apply.

I think the Pin interrupt ISR need to be rewritten. I have not needed it yet.

Chuck.

@william-ferguson-au
Copy link
Contributor

Note also, that it also fires twice if I use GPIO_INT_NEGEDGE

@william-ferguson-au
Copy link
Contributor

@stickbreaker when you say "shared" interrupt, what do you mean?

I do want to have interrupts for several pins, but I am getting this behaviour when I have only configured an interrupt for a single pin.

@william-ferguson-au
Copy link
Contributor

@stickbreaker I read http://esp-idf.readthedocs.io/en/latest/api-reference/system/intr_alloc.html and am now confused.

By using:

gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
gpio_isr_handler_add(BUTTON_1, myHandler, NULL);

am I somehow using a shared interrupt?
For source ETS_GPIO_INTR_SOURCE?

If so, how do I define interrupts for 3 buttons that only fire only the falling edge?

@stickbreaker
Copy link
Contributor

stickbreaker commented Jul 8, 2018

@william-ferguson-au I'm not able to answer you questions. I have not studied interrupts exhaustively. I just learned enough to solve my problems.
If you read that link you can see that the results you are seeing are mirroring a Shared Level interrupt.

To solve your problem you are going to have to delve deeper into the hardware and the ESP interrupt allocation code. I believe that issues revolve around interrupt source assignment and shared interrupt filtering. Through a quick(non authoritarian review) I surmise that all pin interrupt sources use one interrupt(shared) and either the pin source configuration is wrong, or my understanding is bogus.

This link describes the hardware GPIO interface:
gpio_struct.h

This is the part of the GPIO structure that I think relates to interrupt configuration.
pin interrupt configuration

I think the current GPIO interrupt functions are not correct, but I have not had the need to fix them. Have a Go.

Chuck.

@PepeTheFroggie
Copy link

Same problem here.

@bradley-douglas
Copy link

same problem using ESP32 Wroom 32u only falling edge works for me , if i select rising edge it triggers on both rising and falling , no noise on input as i have 100nf there.

@bradley-douglas
Copy link

On further testing what i found is weird but it solves this double trigger issue with ESP32
There must be no debounce capacitor on gpio line it seems that the slew rate of the edge of falling side of logic if slow creates a 2nd trigger. Also the logic must be the following, you must pull the gpio to gnd and have a pullup resistor. if you follow this principal the interrupt works fine for RISING and FALLING perfectly.

@rafaelalvesferreira
Copy link

On further testing what i found is weird but it solves this double trigger issue with ESP32
There must be no debounce capacitor on gpio line it seems that the slew rate of the edge of falling side of logic if slow creates a 2nd trigger. Also the logic must be the following, you must pull the gpio to gnd and have a pullup resistor. if you follow this principal the interrupt works fine for RISING and FALLING perfectly.

The info is perfect. You can use the internal pullup resistor using:

  • pinMode(Pin_number, INPUT_PULLUP);
    Be aware that GPIOs 34-39 are input-only and do not have internal pull-up or pull-down circuitry.

@stale
Copy link

stale bot commented Nov 11, 2019

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: Stale Issue is stale stage (outdated/stuck) label Nov 11, 2019
@stale
Copy link

stale bot commented Nov 25, 2019

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

@stale stale bot closed this as completed Nov 25, 2019
@MacLeod-D
Copy link

My experience:
Pressing a button gives a lot of noise - pulses in down to some ns.
For that mostly you have to debounce a button press.
Try:

  • Use an output pin to produce pulses
  • Connect this pin with the interrupt pin

I had never a problem with an interrupt using the ESP32.

Maybe you want to look at:
https://github.com/MacLeod-D/ESp32-Fast-external-IRQs

There you can find the measurements for external interrupts.
And a polling solution which is VERY fast.

@DougArmstrong
Copy link

Have there been any updates on this issue? I have studied this issue closely and it appears that when in rising or falling mode the ISRs trigger randomly in the other mode. (This is not a key bounce issue as have been discussed above. This is a test square wave fed directly to the MCU)

In the image below, the yellow square wave is fed into PIN 17 on the ESP32. Each time the ISR fires, it toggles the output pin shown in purple. To the left you can see that the ISR fired on the rising edge of the input, however in the following two pulses it fired correctly.

The ISR setup and code follows:

#define RPM_PIN 17
pinMode(Pin, INPUT_PULLUP); // Pull Up the pin and make it an input
pinMode(Light, OUTPUT);

// Be sure the ISR is fully initialized by this point.
attachInterrupt(digitalPinToInterrupt(Pin), RPM_ISR, FALLING); 

void IRAM_ATTR RPM_ISR()
{
static uint64_t StartValue = 0; // First interrupt value
BaseType_t xHigherPriorityTaskWoken;
uint32_t PeriodCount;
uint64_t TempVal;

xHigherPriorityTaskWoken = pdFALSE;

TempVal = timerRead(RPM_Timer);			// value of timer at interrupt
PeriodCount = (TempVal - StartValue);		// period count between rising edges in 

// ESP 32 appears to have a bug where sometimes different edges fire regardless of rising/falling
// Some test code to see the timing on the scope
digitalWrite(Light, !digitalRead(Light));

xQueueSendToBackFromISR(RPM_Queue, &PeriodCount, &xHigherPriorityTaskWoken);
									// puts latest reading as start for next calculation
    StartValue = TempVal;

} // RPM_ISR

image

@JimDrewGH
Copy link

JimDrewGH commented Jul 3, 2020

I have seen this same comment from several different people, and the only thing in common that everyone has had that I noticed was that they are all using INPUT_PULLUP. I wonder if the ESP32 disconnects the pin internally during some part of the interrupt process and reconnecting it sometimes retriggers the interrupt. Unlike traditional microcontrollers where you have to manually clear an interrupt flag, that doesn't seem to be the case with the ESP32. Is that true?

@DougArmstrong
Copy link

ESP32 does not require clearing the interrupt pending flag (or at least if it does this is done in background). I will try an external pull up to see if that helps. Some posts are about key-bounce which is clearly not what is happening here. It does seem like a noise problem. A previous post suggested that using the IDF instead of Arduino FW creates code that works correctly. I try to stick with Arduino since I write for so many different CPUs. Thanks for the feedback Jim, I'll run some tests and let you know what I find out.

@BjarneBitscrambler
Copy link

@DougArmstrong - have you made any discoveries regarding internal vs external pullups?

And, in your scope shot - is the yellow trace being probed right at the ESP32 input pin, or is there some additional wire length or passive components between the scope probe and the GPIO pin? The scope shot certainly does look like a clean and fast-edged signal.

@gehowi
Copy link

gehowi commented Jul 16, 2020

Random interrupts happen on ‘input only’ pins 34-39 that are pulled up or stay high. It has nothing to do with EMI.
If the GPIO pin is normally low, a positive pulse will generate a normal interrupt in any mode.
If the pin stays high, interrupts will fire continuously, no matter which mode is used.
Use Input/Output pins for interrupts. Test with GPIO 4.

@zfields
Copy link

zfields commented Dec 12, 2020

Similar to the OP, I had a clean signal, but experienced CHANGE behavior when I had set the interrupt for FALLING.

My interrupts were originally configured as follows:

  pinMode(21, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(21), isr_1, FALLING);
  pinMode(14, INPUT);
  attachInterrupt(digitalPinToInterrupt(14), isr_2, RISING);

After reading @stickbreaker 's comment...

"all pin interrupt sources use one interrupt(shared)"

I configured the interrupts to fire on the same edge, and I was able to resolve my issue.

  pinMode(21, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(21), isr_1, RISING);
  pinMode(14, INPUT);
  attachInterrupt(digitalPinToInterrupt(14), isr_2, RISING);

Minimally, it appears to be a bug in the Arduino Board Package implementation.

Board: Adafruit Huzzah32 ESP32 Feather
Arduino IDE: v1.8.13
Additional Boards Manager URL: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Boards Package: esp32 (Espressif Systems) v1.0.4

@ALEBORTOT
Copy link

ESP32 is really a "fake cpu".
Please see

3.14. The ESP32 GPIO peripheral may not trigger interrupts correctly at
https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf

I use a PIC16C54 at 1997 and the interrupts works fine.

Now, at 2021, It's unbelievable that a CPU has serious problems interrupting a GPIO. Its the fantastic ESP32.

And there is no easy workaround .

@LMBernardo
Copy link

LMBernardo commented Sep 5, 2021

This issue is still occurring for me, specifically on GPIO 35 (which is input only and has no pull up / pull down). I'm setting the config for GPIO 0 and GPIO 35 at the same time, so they are identical, but GPIO 0 behaves as expected and reacts on falling edge only while GPIO 35 will randomly interrupt on rising edges as well. Not sure if this is a hardware thing or a software thing or what. I did consider adding a pull up resistor to the pin, but it seems that GPIO_INTR_ANYEDGE works as expected on both GPIO 0 and GPIO 35.

This being the case, I was able to work around the issue by just triggering on all edges and implementing the falling edge detector in software. Just keep track of the previous input state - when an edge event happens, check the previous state for that input. If the previous state was high and the new state is low, then you have a falling edge.

In theory, you could just check inside your ISR whether or not the pin is now low or now high as suggested by @n6il in the linked issue, but this wasn't working for me for some reason.

static void IRAM_ATTR gpio_isr_handler( void* arg ){
    uint32_t gpio_num = (uint32_t) arg;
    if (gpio_get_level(io_num)) return;
    ...
}

@zfields
Copy link

zfields commented Sep 7, 2021

@LMBernardo I also had difficulties when trying to use RISING and FALLING edges simultaneously. Once I set my logic to use only RISING or FALLING, then the interrupts behaved as expected.

See: #1111 (comment)

@wanggaoteng
Copy link

wanggaoteng commented Apr 30, 2022

I can confirm the issue is happening to me as well. My test code looks like:

// User Buttons
#define USER_BT1      25

/*
 * Interrupt Definitions
 */
volatile uint8_t bt1Pressed = 0;

void bt1IntHandler()
{
  bt1Pressed++;
}


void setup() {
  Serial.begin(115200);
  Serial.println("Starting Up");
  
  Serial.println(" - User Buttons");
  pinMode(USER_BT1, INPUT_PULLUP);

  // Some Delay for things to calm down
  delay(30);

  // Add Interrupts
  attachInterrupt(digitalPinToInterrupt(USER_BT1), bt1IntHandler, FALLING);
}

void loop() {
  // Print the number of interrupts every 3 second
  Serial.print("Number of Interrupts: ");
  Serial.println(bt1Pressed);
  delay(3000);
}

I also capture the button press on my scope, no noise in the signal. ds1104z_ds1za180300224_2018-07-05_15 23 06

The output on the Serial Print always increment by "2" every time I press the button. If I press and hold, it increments 1, but it'll increment 1 again once I release it.

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:956
load:0x40078000,len:0
load:0x40078000,len:13256
entry 0x40078a90
Starting Up
 - User Buttons
Number of Interrupts: 0
Number of Interrupts: 2 <- Quick Press and Release
Number of Interrupts: 4 <- Another Press and Release
Number of Interrupts: 5 <- Press and Hold
Number of Interrupts: 5 <- Still holding
Number of Interrupts: 6 <- Released
Number of Interrupts: 6

Hi, today is 2022.04.30, my test code is very similar to yours, and the problem of esp32-arduino interrupt is still exists.

@VojtechBartoska VojtechBartoska removed the Status: Stale Issue is stale stage (outdated/stuck) label May 3, 2022
@VojtechBartoska VojtechBartoska added the Status: Needs investigation We need to do some research before taking next steps on this issue label May 3, 2022
@florentbr
Copy link

I faced the same issue with a signal from an optoisolator. The unexpected interrupts happened due to a significant rise/fall time. The ESP32 does not work well with curved edges. It needs straight edges to avoid the unexpected interrupts.

There's multiple ways to handle/avoid this issue:

  • de-bounce with a timer in the interrupt
  • read the input in the interrupt and skip if the level is not right
  • reduce the rise/fall time with a transistor on the input to quickly jump into saturation.
  • clean the input with a trigger buffer

@matei-miron
Copy link

matei-miron commented Oct 19, 2023

Hi, I am experience the same problem.
Checked the input with the scope. It is really clean. Had extra caps and resistors added to debounce the signal "just in case".
Using 5.1.1

#include "driver/gpio.h"
#include "esp_log.h"
#include "soc/gpio_reg.h"           // For GPIO Registers. GPIO_OUT_REG (31..0) . GPIO_OUT1_REG (32..39) . REG_READ(GPIO_IN1_REG)
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

#define crankPin 32
#define wheelPin 33
#define bothPins ((1ULL << crankPin) | (1ULL << wheelPin))

uint32_t wheelRevs = 0;
uint16_t wheelTime = 0;
uint16_t crankRevs = 0;
uint16_t crankTime = 0;
uint32_t wheelRevsOld = 0;
uint16_t wheelTimeOld = 0;
uint16_t crankRevsOld = 0;
uint16_t crankTimeOld = 0;

uint32_t interruptCrankOldTime = 0;
uint32_t interruptWheelOldTime = 0;

static const char *INTTAG = "Register Output:";

static void IRAM_ATTR gpio_isr_handler(void* arg) {
    uint32_t gpioInValue = REG_READ(GPIO_IN1_REG); // Read the whole GPIO input data register
    uint32_t timestamp = xTaskGetTickCount();

    uint32_t data[2];
    data[0] = gpioInValue;
    data[1] = timestamp;

    // Pass the GPIO data and timestamp to the queue
    xQueueSendFromISR(gpio_evt_queue, data, NULL);
}

void updateRecords(void* arg) {
    uint32_t receivedData[2];

    for (;;) {
        if (xQueueReceive(gpio_evt_queue, receivedData, portMAX_DELAY)) {
            //(((receivedData[0] >> 1) & 1) << 1) | (receivedData[0] >> 0) & 1
            uint32_t interruptTime = receivedData[1] * 10;
            uint32_t deltaTimeCrank = interruptTime - interruptCrankOldTime;
            uint32_t deltaTimeWheel = interruptTime - interruptWheelOldTime;
            
            if ((receivedData[0] >> (crankPin % 32)) & 1) {
                // 250 ms are 4 Hz or 240 rpm.
                if (deltaTimeCrank >= 250) {
                    crankRevs++;
                    ESP_LOGI(INTTAG, "Crank rotations: %u, %lu. Delta: %lu", crankRevs, interruptTime, deltaTimeCrank);
                }
                interruptCrankOldTime = interruptTime;
            }

            if ((receivedData[0] >> (wheelPin % 32)) & 1) {
                // 100 ms are 10 Hz or 600 rpm. About 72 km/h for 29 inch (700C) wheels 
                if (deltaTimeWheel >= 100) {
                    wheelRevs++;
                    ESP_LOGI(INTTAG, "Wheel rotations: %lu, %lu. Delta: %lu", wheelRevs, interruptTime, deltaTimeWheel);
                }
                interruptWheelOldTime = interruptTime;
            }
        }
    }
}

void app_main() {
    esp_log_level_set("*", ESP_LOG_INFO);

    gpio_config_t pinConfig = {};
    pinConfig.intr_type = GPIO_INTR_POSEDGE;
    pinConfig.mode = GPIO_MODE_INPUT;
    pinConfig.pin_bit_mask = bothPins;
    pinConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
    pinConfig.pull_up_en = GPIO_PULLUP_DISABLE;

    gpio_config(&pinConfig);

    // Create a queue to handle GPIO events from ISR
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t[2]));

    // Start the GPIO task
    xTaskCreate(updateRecords, "update_records", 2048, NULL, 10, NULL);

    // Add the ISR
    gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
    gpio_isr_handler_add(crankPin, gpio_isr_handler, (void*) NULL);
    gpio_isr_handler_add(wheelPin, gpio_isr_handler, (void*) NULL);

    while (1) {
        // Add a suitable delay to control the timing of your application
        vTaskDelay(pdMS_TO_TICKS(1000)); // Example: 1-second delay
    }
}

This produces an interrupt as expected if the signal is applied for a short time.
But it produces an interrupt on the raising edge and on the falling edge if the signal is high for a "long enough time". Let's say 1 second.
It also panics the core if I set pinConfig.intr_type = GPIO_INTR_HIGH_LEVEL;

@MarcinSzymanek
Copy link

Hi, I am experience the same problem. Checked the input with the scope. It is really clean. Had extra caps and resistors added to debounce the signal "just in case". Using 5.1.1

#include "driver/gpio.h"
#include "esp_log.h"
#include "soc/gpio_reg.h"           // For GPIO Registers. GPIO_OUT_REG (31..0) . GPIO_OUT1_REG (32..39) . REG_READ(GPIO_IN1_REG)
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

#define crankPin 32
#define wheelPin 33
#define bothPins ((1ULL << crankPin) | (1ULL << wheelPin))

uint32_t wheelRevs = 0;
uint16_t wheelTime = 0;
uint16_t crankRevs = 0;
uint16_t crankTime = 0;
uint32_t wheelRevsOld = 0;
uint16_t wheelTimeOld = 0;
uint16_t crankRevsOld = 0;
uint16_t crankTimeOld = 0;

uint32_t interruptCrankOldTime = 0;
uint32_t interruptWheelOldTime = 0;

static const char *INTTAG = "Register Output:";

static void IRAM_ATTR gpio_isr_handler(void* arg) {
    uint32_t gpioInValue = REG_READ(GPIO_IN1_REG); // Read the whole GPIO input data register
    uint32_t timestamp = xTaskGetTickCount();

    uint32_t data[2];
    data[0] = gpioInValue;
    data[1] = timestamp;

    // Pass the GPIO data and timestamp to the queue
    xQueueSendFromISR(gpio_evt_queue, data, NULL);
}

void updateRecords(void* arg) {
    uint32_t receivedData[2];

    for (;;) {
        if (xQueueReceive(gpio_evt_queue, receivedData, portMAX_DELAY)) {
            //(((receivedData[0] >> 1) & 1) << 1) | (receivedData[0] >> 0) & 1
            uint32_t interruptTime = receivedData[1] * 10;
            uint32_t deltaTimeCrank = interruptTime - interruptCrankOldTime;
            uint32_t deltaTimeWheel = interruptTime - interruptWheelOldTime;
            
            if ((receivedData[0] >> (crankPin % 32)) & 1) {
                // 250 ms are 4 Hz or 240 rpm.
                if (deltaTimeCrank >= 250) {
                    crankRevs++;
                    ESP_LOGI(INTTAG, "Crank rotations: %u, %lu. Delta: %lu", crankRevs, interruptTime, deltaTimeCrank);
                }
                interruptCrankOldTime = interruptTime;
            }

            if ((receivedData[0] >> (wheelPin % 32)) & 1) {
                // 100 ms are 10 Hz or 600 rpm. About 72 km/h for 29 inch (700C) wheels 
                if (deltaTimeWheel >= 100) {
                    wheelRevs++;
                    ESP_LOGI(INTTAG, "Wheel rotations: %lu, %lu. Delta: %lu", wheelRevs, interruptTime, deltaTimeWheel);
                }
                interruptWheelOldTime = interruptTime;
            }
        }
    }
}

void app_main() {
    esp_log_level_set("*", ESP_LOG_INFO);

    gpio_config_t pinConfig = {};
    pinConfig.intr_type = GPIO_INTR_POSEDGE;
    pinConfig.mode = GPIO_MODE_INPUT;
    pinConfig.pin_bit_mask = bothPins;
    pinConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
    pinConfig.pull_up_en = GPIO_PULLUP_DISABLE;

    gpio_config(&pinConfig);

    // Create a queue to handle GPIO events from ISR
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t[2]));

    // Start the GPIO task
    xTaskCreate(updateRecords, "update_records", 2048, NULL, 10, NULL);

    // Add the ISR
    gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
    gpio_isr_handler_add(crankPin, gpio_isr_handler, (void*) NULL);
    gpio_isr_handler_add(wheelPin, gpio_isr_handler, (void*) NULL);

    while (1) {
        // Add a suitable delay to control the timing of your application
        vTaskDelay(pdMS_TO_TICKS(1000)); // Example: 1-second delay
    }
}

This produces an interrupt as expected if the signal is applied for a short time. But it produces an interrupt on the raising edge and on the falling edge if the signal is high for a "long enough time". Let's say 1 second. It also panics the core if I set pinConfig.intr_type = GPIO_INTR_HIGH_LEVEL;

The core panicking can happen because while the signal stays high, the interrupt keeps triggering?

@MikeJohnsEng
Copy link

MikeJohnsEng commented Jun 22, 2024

There is no Schmitt triggering on ESP32 inputs, interrupts on this MPU need a quick clean transition.
I found with slow rise time transitions that interrupts are very unreliable, and even multiple interrupts can be triggered from one event .

It's best to use an external Schmitt trigger buffer.

The STM32 series of MPU's have Schmitt on all inputs which make them a lot more robust, so I'll often interface less than ideal inputs with a Black-Pill and then send the data on to the esp32, works out cheaper than using external Schmitt buffers !

I faced the same issue with a signal from an optoisolator. The unexpected interrupts happened due to a significant rise/fall time. The ESP32 does not work well with curved edges. It needs straight edges to avoid the unexpected interrupts.

There's multiple ways to handle/avoid this issue:
......

@uygurrr
Copy link

uygurrr commented Jul 6, 2024

falling edge and rising edge ramp should be vertical. there should be no capacitor. solution code for FALLING edge:
static void kesmeIR()
{
if (digitalRead(ir_pin) == LOW) {
say++;
peryot = micros() - per ;
per = micros();
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Needs investigation We need to do some research before taking next steps on this issue
Projects
None yet
Development

No branches or pull requests