IRQ interrupt callback runs multiple times

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
Post Reply
lvx_xvl
Posts: 5
Joined: Sun Jul 26, 2020 11:45 am

IRQ interrupt callback runs multiple times

Post by lvx_xvl » Sun May 23, 2021 11:35 am

Hi all,

I was debugging my inductive proximity sensor code because the counter registers more revolutions than expected.
My device is a Wemos D1 mini with the latest Micropython on it: "esp8266-20210418-v1.15", and the sensor is the DFRobot FIT0658, connected via a 4N35 optocoupler.

I read that it is very wise to disable the irq at entry of the callback function saving its state and enabling it after finishing with the saved state.
But somehow it still registers multiple callbacks with just one pass over the sensor.

Below my code (please don't mind all the 'uart' stuff, that is inthere for debugging and will be removed afterwards):

Code: Select all

class ProxSensor():
    pin = Pin(13, Pin.IN)  # D7 = GPIO13 on Wemos D1 mini
    counter = 0
    state = 0
    uart = None

    def __init__(self, uart):
        """Initialize the class."""
        self.uart = uart
        self.uart.write('\n\ninit\n')

        self.pin.irq(trigger=Pin.IRQ_FALLING, handler=self.callback)

    def callback(self, pin):
        """The IRQ trigger callback function for input 'pin'."""
        self.state = machine.disable_irq()
        self.uart.write('IRQ callback\n')
        self.uart.write('state: %s\n' % self.state)
        
        # debounce wait
        time.sleep_ms(50)
        
        # wait for the pin to rise again
        while True:
            debounce = 0
            for i in range(20):
                pin_state = self.pin.value()
                self.uart.write(str(pin_state)+'\n')
                debounce = debounce + pin_state
                time.sleep_ms(10)
            self.uart.write('D: ' + str(debounce)+'\n\n')
            if debounce > 15:
                    self.uart.write('-!-\n\n')
                    break
 
        # increment the counter
        self.counter += 1

        machine.enable_irq(self.state)
Below the output of the 'uart' when I pass once over the sensor.

Code: Select all

IRQ callback
state: 32
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
D: 0

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
D: 4

1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
D: 20

-!-

IRQ callback
state: 32
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
D: 20

-!-
You can see the callback function is called twice (sometimes even three times), and therefore the counter isn't displaying the correct amount of revolutions.

Why is the callback function called multiple times, even when the 'disbale_irq' is the first line in the callback function?

I tried enabling the pull-up resistor on the pin but didn't show a difference. I also tried passing a 0, 1, or nothing into the 'enable_irq()'. But most of the time this causes a reset of the device.

Best regards,
lvx

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: IRQ interrupt callback runs multiple times

Post by Roberthh » Sun May 23, 2021 12:01 pm

The ESP8266 (and the ESP32) have the habit of detecting multiple 0/1 transitions when signals with slow ramp are applied. That will cause multiple interrupts to be fired. You have to add something like a schmitt-trigger gate between your 4N35 and the ESP8266. E.g. a 74HC14 gate. If you use two inverters, the signal polarity is maintained.

lvx_xvl
Posts: 5
Joined: Sun Jul 26, 2020 11:45 am

Re: IRQ interrupt callback runs multiple times

Post by lvx_xvl » Mon May 24, 2021 7:42 am

Hi Roberthh,

Thanks for the swift response, and for pointing to a solution. I wasn't aware that the espressif chips had this problem, too bad.

Just one more question, is the 4N35 opto causing this slow/uncontrolled edge of the signal, or is the sensor already causing this? I tried looking via a multimeter, but these are not fast enough.

BR,
lvx

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: IRQ interrupt callback runs multiple times

Post by Roberthh » Mon May 24, 2021 8:11 am

It could be a mix of both. The 4N35 is not really fast, especially when switching off. That's common to optocouplers. The data sheet talks of 10 µs. You could speed that up by a resistor between Base and Emittter (pins 4 and 6), which in turn also reduces sensitivity. And the way the optocoupler is driven may also cause a slow ramp. Attached is a picture which shows the effect for a triangle pulse of 10µs width. When I had that on a ESP8266, I could not add an additional gate, so I cleared the IRQ flag in the ISR when handling the first IRQ. That worked out. The code for that is pretty simple:

Code: Select all

#
# Clear Interrupt status
#
@micropython.viper
def clear_isr(mask: int):
    ptr32(0x60000324)[0] = mask
And it is called with:

Code: Select all

clear_isr(0x20)  # clear interrupt mask of Pin(5)
You may have to adapt that value 0x20 for the Pin number you are using.
triangle_resp_10.jpg
triangle_resp_10.jpg (310.6 KiB) Viewed 2911 times

Post Reply