RPi pico machine.SPI periodic dropout.

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
JWF
Posts: 5
Joined: Sun Jun 06, 2021 5:39 pm

Re: Is the ISR actually running?

Post by JWF » Wed Jun 16, 2021 9:14 am

pythoncoder wrote:
Wed Jun 16, 2021 8:48 am
To resolve this, in the ISR set a pin high, then low and put the scope on that pin.
This is seen in the first picture of the original post. I am at the moment unable to supply a measurement of this with the minimal test case - will do tonight.

In this picture it is seen in green how the SPI CS is held low but never returned high, indicating the pico is stuck in the SPI.read function.

Edit.
I have added a measurement of the CS signal in the minimal test case. CS (blue) is held low while inside the ISR. From the first picture I gather that the ISR is indeed entered and CS put low. However it seems to be stuck in SPI.read().

I am unsure of what is happening in the second picture? does this indicate that the problem might not be in machine.SPI but rather a timing thing?

Image

Image

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

Re: RPi pico machine.SPI periodic dropout.

Post by pythoncoder » Wed Jun 16, 2021 7:23 pm

A possibility is the fact that SPI.read() allocates, which is not allowed in an ISR. This will occur even though you aren't assigning the bytes object to a variable. On most ports allocating in an ISR leads to a traceback, but perhaps the Pico fails silently. If it behaves in this way I would regard it as a bug.

To avoid allocation use .readinto with a pre-allocated buffer. Alternatively use micropython.schedule to run a soft ISR.
Peter Hinch

JWF
Posts: 5
Joined: Sun Jun 06, 2021 5:39 pm

Re: RPi pico machine.SPI periodic dropout.

Post by JWF » Wed Jun 16, 2021 8:22 pm

pythoncoder wrote:
Wed Jun 16, 2021 7:23 pm
A possibility is the fact that SPI.read() allocates
Sharp observation. However, even without the SPI in the below even more minimal case, the issue seems to persist.

Code: Select all

from machine import Pin, PWM

def READ_ADC(*_):
    cs.value(1)
    cs.value(0)
    
if __name__ == "__main__":
    pin = Pin(16, Pin.OUT)
    pwm = PWM(pin)
    pwm.freq(4000)
    pwm.duty_u16(32512)
    cs = Pin(13, Pin.OUT, value=1)
    ADC_READY = Pin(14, Pin.IN)
    ADC_READY.irq(trigger=Pin.IRQ_FALLING, handler=READ_ADC)
Here I have omittet the SPI and is just doing an IRQ on the generated PWM falling edge signal.

Image

hippy
Posts: 46
Joined: Sat Feb 20, 2021 2:46 pm
Location: UK

Re: RPi pico machine.SPI periodic dropout.

Post by hippy » Thu Jun 17, 2021 12:59 pm

JWF wrote:
Wed Jun 16, 2021 8:22 pm
even without the SPI in the below even more minimal case, the issue seems to persist.
That could be a result of garbage collection. If garbage collection is happening within the callback or when called, the callback time will be extended and callbacks may be dropped or deferred - I admit I don't know what is meant to happen.

That seems to be the case with my test code which doesn't need any hardware connections or links; the 'inp' monitors the same 'pwm' pin ...

Code: Select all

from   machine import Pin, PWM
import gc
import time

count = 0
def Irq(arg):
    global count
    count += 1
    
inp = Pin(16, Pin.IN)
inp.irq(trigger=Pin.IRQ_FALLING, handler=Irq)

pwm = PWM(Pin(16, Pin.OUT))
pwm.freq(4000)
pwm.duty_u16(0x8000)

while True:
    # gc.disable()
    count = 0
    time.sleep_ms(100)
    counted = count
    # gc.enable()
    print(counted)
That consistently reports 400 or 401 as expected.

Now change "def Irq(arg):" to "def Irq(*_):" which reflects how you are doing things.

That also reports 400 as expected, but occasionally drops lower, also goes higher -

Code: Select all

400
400
386
407
400
401
Something is preventing the callback from being called, preventing count being incremented.

With my original code "def Irq(arg):" and uncommenting the two 'gc.disable()' and 'gc.enable() calls and everything is fine, same as before, 400 or 401.

But with "def Irq(*_):' we soon hit "MemoryError" which suggests to me that more garbage collection is going on in that case because disabling that causes a major problem.

I am not entirely convinced it is just the use of "*_" which is the problem. Given the issues with pin callbacks, timer callbacks, threading, I suspect there are some systemic issues playing their part.


And just for a laugh - Back to my original code; replace "time.sleep_ms(100)" with the functional equivalent of "time.sleep_us(100_000)" and, as described elsewhere, count is always zero.

BTW : None of this explains the PWM case.

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

Re: RPi pico machine.SPI periodic dropout.

Post by pythoncoder » Thu Jun 17, 2021 3:39 pm

JWF wrote:
Wed Jun 16, 2021 8:22 pm
... However, even without the SPI in the below even more minimal case, the issue seems to persist...
This strikes me as a clear smoking gun: it is surely a bug. There should be no GC taking place in this minimal example.

I suggest you report this on GitHub. If you prefer I will do so,
Peter Hinch

JWF
Posts: 5
Joined: Sun Jun 06, 2021 5:39 pm

Re: RPi pico machine.SPI periodic dropout.

Post by JWF » Fri Jun 18, 2021 5:19 pm

pythoncoder wrote:
Thu Jun 17, 2021 3:39 pm
I suggest you report this on GitHub. If you prefer I will do so,
If it is not too much of a inconvenience for you, I would feel more comfortable if you would. Thanks. I am not even sure what Github you speak of.

dk2jk
Posts: 2
Joined: Sun Jun 13, 2021 9:09 am
Location: JO41dl

Re: RPi pico machine.SPI periodic dropout.

Post by dk2jk » Sat Jun 19, 2021 8:22 am

ref: viewtopic.php?f=21&t=10642#p58809

When I change the statement for the square wave output from

Code: Select all

 p17.value (dds.i> N / 2) # square wave output
to

Code: Select all

p17.value (dds.i> N >> 1) # square wave output
the PWM problems described are gone.
It seems, that no problem occur, when I use strictly only integer variables an only simple bit operations.

It doesn't matter whether this output occurs in a timer interrupt or in the main loop ; In the interrupt, of course, the square wave output is more precisely matched to the sine output.
However, my target program is a DDS (direct digital synthesis), where the sine table has a fixed number of points and the output frequency is changed via phase steps (phase wheel, https://www.digikey.com/en/articles/th ... izers-ddss).
Here is the final script:

Code: Select all

from machine import Pin, PWM,Timer
from time import sleep
from sintab import genSintab
import gc
import micropython
micropython.alloc_emergency_exception_buf(100)

p17= Pin(17,Pin.OUT )

#PWM #################
pwm=PWM( Pin(16) )
pwm.freq(16000)
pwm.duty_u16(0x8000)

# Sinustabelle ########
N=256
sintab =genSintab(n=N,ampl=0x8000,rand=0x100)
'''
sinustabelle
0x8000  0x8323  ... 0xfff0  ... 0x7fff  ... 0x0010  ... 0x07cdc  
'''

# DDS #################
MAX = const(0x1_00_00_00)
step    = 0xc280   # MAX/8 => 25 Hz
en      = True
class dds:
    acc     = MAX-1    # 0..1   
    index   = 0
    
def nextstep(t):  
    if  en:      
        dds.acc = (dds.acc + step)
        if dds.acc >= MAX:    
            dds.acc = dds.acc - MAX  # 0x1_00_12_34 =>  0x12_34
        dds.index = dds.acc >>16     # 0x0_ff_12_34 =>  0xff     
        pwm.duty_u16(sintab[dds.index] )
        p17.value(dds.index>>7)      # mostleft bit ; 0xff =>  0x01 , 0x7f =>  0x00
    else:
        dds.acc = 00;
        pwm.duty_u16(sintab[0])
    pass
    
# Timer #####################
t1= Timer()
t1.init(freq =8000, mode = Timer.PERIODIC, callback =nextstep)


while True:
    try:    
        pass
    except Exception as e:
         pwm.duty_u16(sintab[0])
         print(e)
         break



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

Re: RPi pico machine.SPI periodic dropout.

Post by Roberthh » Sat Jun 19, 2021 8:33 am

The floating point operation allocated RAM, the pure integer operation does not. It should also work with // instead of /.

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

Re: RPi pico machine.SPI periodic dropout.

Post by pythoncoder » Sat Jun 19, 2021 2:48 pm

@JWF Now done, please see here.

MicroPython sourcecode is hosted on GitHub. Raising an issue is the way to report firmware bugs, so hopefully this will be addressed by the maintainers in due course.
Peter Hinch

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

Re: RPi pico machine.SPI periodic dropout.

Post by pythoncoder » Wed Jun 23, 2021 6:45 am

The upshot of the GitHub discussion is that interrupts defined with machine.Pin.irq() default to soft ISR's (a fact which is documented and I'd failed to spot). For fast, deterministic handling you need to set hard=True. This means that you can't allocate in the handler, which rules out any floating point code. Also, declarations like

Code: Select all

def READ_ADC(*_):
    cs.value(1)
    cs.value(0)
are invalid in a hard ISR because a 1-tuple is created and assigned to a variable _. Instantiating a tuple causes allocation. When you define the ISR as hard, this (correctly) raises a memory error. So the code should read:

Code: Select all

def READ_ADC(_):
    cs.value(1)
    cs.value(0)
My take on this is that there is probably no bug. Perhaps you might like to try these changes, first on your simple example, then on your actual code, and report back.
Peter Hinch

Post Reply