Trivial code sometimes takes 5ms and interrupts are missed

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
ArmyrAntSEC
Posts: 7
Joined: Sat Jul 17, 2021 7:26 pm

Trivial code sometimes takes 5ms and interrupts are missed

Post by ArmyrAntSEC » Sat Jul 17, 2021 7:47 pm

Hi
So I was a huge fan of the Pico price-point and the fact that it seemed possible to program a microcontroller with Python.

But I ave found a blocker for me to use the Pico with Python in my project. The issue is that the main loop in my sample program below sometimes takes up to 5ms to run. And more specifically, it seems that the pico will miss interrupts during when that happens.

So can someone explain what it is that causes this code to sometimes take a lot longer to run? My obvious theory is that it is a garbage collector or similar, but no complex garbage collection should be needed here as only some very trivial variables are created in each loop.

The below code does not demonstrate the missed interrupts as these require some special hardware to detect, but if I can understand why the loop sometimes takes a long time, I think that might be enough.

Full disclosure: I have posted the identically same question on the Raspberry Pi Pico Micropythong forum as well.

Code: Select all

import utime

lastTime_ms = utime.ticks_ms()
nextTime_ms = utime.ticks_add( lastTime_ms, 100 )

lastTime_us = utime.ticks_us()
intervalMean = 0
intervalMeanSq = 0
callCount = 0

while ( True ):
    # This block of code is the problem. It sometimes takes more than 1ms, up to 5ms
    thisTime_us = utime.ticks_us()
    delta_us = utime.ticks_diff( thisTime_us, lastTime_us )
    intervalMean = (callCount*intervalMean + delta_us)/(callCount+1)
    intervalMeanSq = (callCount*intervalMeanSq + delta_us*delta_us)/(callCount+1)
    lastTime_us = thisTime_us
    callCount = callCount + 1
    # End of problem block

    thisTime_ms = utime.ticks_ms()

    if ( utime.ticks_diff( nextTime_ms, thisTime_ms ) < 0 ):
        timeDiff_ms = utime.ticks_diff( thisTime_ms, lastTime_ms)
        print ( "Time: ", thisTime_ms , " Time diff: ", timeDiff_ms, end='' )
        if ( timeDiff_ms != 100 ):
            print ( " Warning!" )
        else:
            print ()
        lastTime_ms = thisTime_ms
        nextTime_ms = utime.ticks_add( nextTime_ms, 100 )

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

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by Roberthh » Sat Jul 17, 2021 8:03 pm

Even a non-complex gabage collection will take some time, if many objects have to get collected. A common approach to make the timing more predictable would be to manually force garbage collection when there seems time to do some, like in the main loop. If there a only a few objects to collect, it will run much faster.

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

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by pythoncoder » Sun Jul 18, 2021 6:42 am

Yes. Though the code is simple it uses floating point maths, which implies allocation. Therefore GC will eventually occur. By periodically calling gc.collect(), collection occurs when most of the RAM is unallocated, so the GC runs much quicker - I'd expect ~1ms.

The radical approach is to rearrange the maths to use integer calculations only. With care, code can be written to be non-allocating.
Peter Hinch
Index to my micropython libraries.

ArmyrAntSEC
Posts: 7
Joined: Sat Jul 17, 2021 7:26 pm

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by ArmyrAntSEC » Sun Jul 18, 2021 8:27 pm

Hi
So the delay in the loop isn't the big problem (All algorithms I intend to use can be written to compensate for that). But the issue is that interrupt handling seems to be unrelible when these delays happen.

So my question to those with kore experience than me is: Does the garbage collector halt the interupt handlers? Are there some known details on how that works?

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by jimmo » Mon Jul 19, 2021 1:48 am

ArmyrAntSEC wrote:
Sun Jul 18, 2021 8:27 pm
So my question to those with kore experience than me is: Does the garbage collector halt the interupt handlers? Are there some known details on how that works?
It's important to clarify the two types of interrupt handlers -- hard and soft. The GC will halt soft interrupts (which are executed via the scheduler), but not hard ones (which are true hardware IRQ). This is the reason why hard IRQs are not allowed to allocate memory (as they could be interrupting the GC).

See https://docs.micropython.org/en/latest/ ... ne.Pin.irq -- the default is soft.

For more information https://docs.micropython.org/en/latest/ ... #isr-rules

ArmyrAntSEC
Posts: 7
Joined: Sat Jul 17, 2021 7:26 pm

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by ArmyrAntSEC » Sat Jul 24, 2021 5:05 pm

Interesting choice not to set the "hard" as the default on a low-level system such as this. I am not convinced that this was the right choice.

Either way, this seems to be a solution, so I will try it and hope for the best.

ArmyrAntSEC
Posts: 7
Joined: Sat Jul 17, 2021 7:26 pm

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by ArmyrAntSEC » Sat Jul 24, 2021 5:13 pm

Thanks for a clear answer!

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

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by pythoncoder » Sat Jul 24, 2021 6:18 pm

ArmyrAntSEC wrote:
Sat Jul 24, 2021 5:05 pm
Interesting choice not to set the "hard" as the default on a low-level system such as this. I am not convinced that this was the right choice...
The point of the machine module is that it is cross-platform: it should be possible to run code reliant on the module on any platform (subject to trivial changes like changing pin names). Given that some platforms don't support hard IRQ's, the default therefore has to be soft. If you write code using hard IRQ's then - inevitably - you either accept that it won't run on some platforms or figure out a solution.
Peter Hinch
Index to my micropython libraries.

ArmyrAntSEC
Posts: 7
Joined: Sat Jul 17, 2021 7:26 pm

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by ArmyrAntSEC » Sat Jul 24, 2021 8:03 pm

Looking back at the docs, I see a potential reason for the fact that I missed this:
https://docs.micropython.org/en/latest/ ... interrupts

"All pins except number 16 can be configured to trigger a hard interrupt if their input changes. You can set code (a callback function) to be executed on the trigger."

This section then has no mentione about setting a flag to make the interrups actually be "hard".

Should I submit a suggestion that the word "hard" either be removed, or that the text elaborate a bit about the fact that "hard" is not the default?

User avatar
scruss
Posts: 360
Joined: Sat Aug 12, 2017 2:27 pm
Location: Toronto, Canada
Contact:

Re: Trivial code sometimes takes 5ms and interrupts are missed

Post by scruss » Sat Jul 24, 2021 9:50 pm

note that you're linking to the ESP8266-specific instructions, so what applies to them doesn't apply to the Pico.

It's as reasonable to assume that interrupts are soft rather than hard, so to me the docs appear to be clear and adequate

Post Reply