Interrupt handler crashes

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
Tinus
Posts: 40
Joined: Sun Feb 14, 2021 4:53 pm

Interrupt handler crashes

Post by Tinus » Fri Mar 12, 2021 11:05 am

I am trying to build an ergometer and am having trouble with my rpm sensor interrupt handler crashing somehow.
It consistently happens after a fixed period of time (about 6 minutes) which makes me think it is a memory problem.
I see no exceptions and my two threads keep running normally.
I have one thread that calculates speed etc and one thread that updates the display.
Both work as expected even after the interrupts stop coming in.

I don't know how to debug this. I have been looking at gc.mem_free() and that looks fine.
I have no experience with interrupts so I might be doing something obviously wrong so any advise is welcome

Code: Select all

#mySensor
import machine
import utime
import uarray
import micropython

micropython.alloc_emergency_exception_buf(100)


class MySensor:
  sensorGPIOPin = 6
  bounce = 2000
  myRB = uarray.array('i',[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
  rb_len = 0
  w_cursor = 0
  r_cursor = 0
  
  sensor = None
  oldTime = 0
  
  tus = utime.ticks_us
  tdif = utime.ticks_diff

  def init_GPIO(self):          # initialize GPIO
    self.sensor = machine.Pin(self.sensorGPIOPin, machine.Pin.IN, machine.Pin.PULL_UP)

  def init_interrupt(self):
    self.oldTime = self.tus()
    self.sensor.irq(trigger=machine.Pin.IRQ_FALLING, handler=self.mark_time)

  def mark_time(self,channel):        # callback function
    newTime = self.tus()
    dT = self.tdif(newTime,self.oldTime)
    if dT > self.bounce:
        self.write(dT)
        self.oldTime = newTime    


  def write(self,time):
    self.myRB[self.w_cursor] = time
    self.w_cursor+=1
    if self.w_cursor >= self.rb_len:
      self.w_cursor = 0
  
  def read(self):
    if self.r_cursor != self.w_cursor:
      val = self.myRB[self.r_cursor]
      self.r_cursor += 1
      if self.r_cursor >= self.rb_len:
        self.r_cursor = 0
    else:
      val = False
    return val
    
  def __init__(self, pinNr):
    self.rb_len = len(self.myRB)
    self.sensorGPIOPin = pinNr
    self.init_GPIO()
    self.init_interrupt()
    


Tinus
Posts: 40
Joined: Sun Feb 14, 2021 4:53 pm

Re: Interrupt handler crashes

Post by Tinus » Fri Mar 12, 2021 11:34 am

I found another thing:

before the interrupt handler crashes I am able to send keyboard interrupts but after it crashes I can't.

Tinus
Posts: 40
Joined: Sun Feb 14, 2021 4:53 pm

Re: Interrupt handler crashes

Post by Tinus » Fri Mar 12, 2021 12:19 pm

I have changed my code to add disable_irq() around writing data and that seems to have fixed it although I have no idea why it would.

So not a memory problem but conflicting interrupts reading memory while it is beeing written to?

Code: Select all

  def mark_time(self,channel):        # callback function
    state = machine.disable_irq()
    newTime = self.tus()
    dT = self.tdif(newTime,self.oldTime)
    if dT > self.bounce:
        self.write(dT)
        self.oldTime = newTime
    machine.enable_irq(state)

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Possible re-entrancy

Post by pythoncoder » Sat Mar 13, 2021 4:47 pm

You may be getting re-entrancy where, owing to contact bounce, your interrupt service routine is itself being interrupted.

In general using interrupts to manage mechanical switches is not ideal: the μs-level capabilities of IRQ's is not required, given the fact that contact bounce can continue for tens of ms.

There are solutions based on uasyncio which avoid these types of problem. See this doc.
Peter Hinch
Index to my micropython libraries.

Tinus
Posts: 40
Joined: Sun Feb 14, 2021 4:53 pm

Re: Interrupt handler crashes

Post by Tinus » Sat Mar 13, 2021 4:54 pm

pythoncoder wrote:
Sat Mar 13, 2021 4:47 pm
You may be getting re-entrancy where, owing to contact bounce, your interrupt service routine is itself being interrupted.

In general using interrupts to manage mechanical switches is not ideal: the μs-level capabilities of IRQ's is not required, given the fact that contact bounce can continue for tens of ms.

There are solutions based on uasyncio which avoid these types of problem. See this doc.
But wouldn't this happen randomly not always after 6 minutes?

I am reading a rpm sensor and ms is not precise enough. (only just but still)

Also I must admit that I am a little afraid of asyncio :)

Tinus
Posts: 40
Joined: Sun Feb 14, 2021 4:53 pm

Re: Interrupt handler crashes

Post by Tinus » Sat Mar 13, 2021 4:58 pm

Tinus wrote:
Sat Mar 13, 2021 4:54 pm
pythoncoder wrote:
Sat Mar 13, 2021 4:47 pm
There are solutions based on uasyncio which avoid these types of problem. See this doc.
Also I must admit that I am a little afraid of asyncio :)
Ah, that tutorial just took a big part of my fears away. Thank you.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Interrupt handler crashes

Post by pythoncoder » Sat Mar 13, 2021 5:13 pm

I can't explain the six minutes, but realtime programming tends to spring these sorts of things. The fact that your fix works does suggest re-entrancy, and disabling interrupts in an ISR is a standard solution for this.

If you need sub-ms precision then uasyncio isn't going to deliver it. You'll need to keep using interrupts. Nevertheless I highly recommend learning uasyncio: it greatly simplifies writing applications where concurrency is required.

Incidentally you can instantiate large arrays more neatly with (e.g.):

Code: Select all

a = array.array('i', (0 for _ in range(100)))  # Zero all elements
Peter Hinch
Index to my micropython libraries.

Tinus
Posts: 40
Joined: Sun Feb 14, 2021 4:53 pm

Re: Interrupt handler crashes

Post by Tinus » Sat Mar 13, 2021 5:26 pm

Oh thank you very much for that Array tip. I was a bit ashamed of that rather silly looking array :)

I will definitely start learning asyncio. I am starting to get to the point where I have to send the results I am getting to the display and to a webserver and I think my current

Code: Select all

while True:
isn't going to cut it :)

Post Reply