How can I prevent soft interrupts stacking up until schedule queue is full? #13187
-
In an attempt to do de-bouncing without asyncio I encountered the following problem: Soft interrupts elicited by contact bounce of a switch are scrambling the schedule queue, despite the attempt to de-activate the interrupt within the ISR. Code: from machine import Pin
from micropython import schedule
def pr_imm(pin):
print(f'{pin} press. Value: {pin()}')
def isr(pin):
pin.irq(handler=None, trigger=0)
schedule(pr_imm, pin) # !!!! can give: RuntimeError: schedule queue full
p = Pin(16, Pin.IN, Pin.PULL_UP)
p.irq(handler=isr, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, hard=False)
print('started') This often gives I decided for a soft interrupt, because I thought this would be less intrusive. Now I see the opposite. |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments 5 replies
-
There are good reasons why using interrupts to debounce mechanical contacts is risky. There is no limit to the rate at which transitions can occur. Further you can get invalid logic levels for periods of time. This can trigger unexpected hardware behaviour. It is instructive to look at the signal on an oscilloscope: they can look pretty vile. That said, given that you are using |
Beta Was this translation helpful? Give feedback.
-
That line already prepares to schedule delayed calls which will run once a full Python line has been completed. Have you tried using ›hard=True‹ and disable IRQ there? |
Beta Was this translation helpful? Give feedback.
-
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# vim:fileencoding=UTF-8:ts=4
from time import sleep_ms
from machine import Pin, PWM, WDT
from micropython import schedule
def pr_imm(pin):
print(f'{pin} press. Value: {pin()}')
def isr(pin):
pin.irq(handler=None, trigger=0)
schedule(pr_imm, pin) # !!!! can give: RuntimeError: schedule queue full
p_in = Pin(16, Pin.IN, Pin.PULL_UP) # bridge pins 16 and 17
p_out = Pin(17, Pin.OUT)
pwm = PWM(p_out)
pwm.freq(12_500_000)
pwm.duty_u16(0)
wdt = WDT(timeout=1000)
p_in.irq(handler=isr, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, hard=True)
print('Start…')
pwm.duty_u16(2**15)
while True:
sleep_ms(10) Start… Only one IRQ, even @ 12.5MHz input. And that would be a truly bad switch |
Beta Was this translation helpful? Give feedback.
-
Slightly related I made a test once for the pattern of transitions when there is a slow slope at a digital input. For the test a triangle signal was applied to a pin, and a small loop would read that pin and echo the value on another pin. Done with a MIMXRT1020. The green trace is the input signal, the yellow tells what is seen by the code. The lower window is a zoom of the upper one. Since this was done by python code, the bandwidth of that test is limited. The PWM signal as trigger is surely clean with fast transitions. A switch will have more of the slow transient behavior in addition to bouncing. |
Beta Was this translation helpful? Give feedback.
-
Thank you all very much for help and ideas. I modified your @GitHubsSilverBullet (+my) example like so: from time import sleep_ms
from machine import Pin, PWM
from micropython import schedule
cnt = 0
def pr_imm(pin):
global cnt
cnt += 1
pin.irq(handler=isr, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, hard=True) # reenable IRQ
def isr(pin):
pin.irq(handler=None, trigger=0) # disable IRQ
schedule(pr_imm, pin)
p_in = Pin(16, Pin.IN, Pin.PULL_UP) # bridge pins 16 and 17
p_out = Pin(17, Pin.OUT)
pwm = PWM(p_out)
pwm.freq(50_000_000) # highest value at irq hard=False without schedule queue full: 15_000
pwm.duty_u16(0)
print('Start…')
p_in.irq(handler=isr, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, hard=True)
pwm.duty_u16(2**15)
for _ in range(100):
sleep_ms(20)
print(cnt) to learn now that even at very high rates there is no overlap of interrupts if the IRQ option is I see now that I can schedule a periodic polling of the button to be debounced with a hard ISR that deactivates itself, without fear of congesting the queues. |
Beta Was this translation helpful? Give feedback.
-
I do think this is doing things the hard way. It's much easier with |
Beta Was this translation helpful? Give feedback.
-
To add another observation: |
Beta Was this translation helpful? Give feedback.
-
Please reconsider and delete that one as well. It is of no use at all. |
Beta Was this translation helpful? Give feedback.
That line already prepares to schedule delayed calls which will run once a full Python line has been completed.
Problem is, it initially does that using hardware interrupts. So a bouncing contact quite likely triggers a whole bunch of hardware IRQs within a few µs, resulting in the schedule stack overflow.
Have you tried using ›hard=True‹ and disable IRQ there?
Not that it would be the best practice…