Page 1 of 2

RPi pico machine.SPI periodic dropout.

Posted: Sun Jun 06, 2021 7:05 pm
by JWF
Hello MicroPython forum.

I recently picked up a pico development board and I am really enjoying the versatility of MicroPython. In my current project I am trying interface a RPi4 with a high performance ADC, in particular the ADS131M04. However, the non-maskable interrupts are reaking havoc with communication timings. I am experimenting with placing a microcontroller as a intermediate buffer, and as a Python developer by trait the pico with MicroPython seems as the obvious choice.

The ADC requires a 8.192 MHz clock which I generate using the PIO with the following code (working splendid).

Code: Select all

class ClockGenerator:
    def __init__(self, pin: int, frequency: float) -> None:
        self.clock_out_StateMachine = rp2.StateMachine(
            0,
            self.square,
            freq=int(frequency * 2),
            set_base=Pin(pin)
        )
    
    @staticmethod
    @asm_pio(set_init=PIO.OUT_LOW)
    def square() -> None:
        wrap_target()
        set(pins, 1)
        set(pins, 0)
        wrap()
        
    def start(self) -> None:
        """Enable clock output"""
        self.clock_out_StateMachine.active(1)
        
    def stop(self) -> None:
        """Disable clock output"""
        self.clock_out_StateMachine.active(0)
The ADC uses SPI (SPI mode 1) to communicate, and a seperate active low signal to indicate available measurement data. I am using the machine.Pin to run a interrupt routine on the ADC_READY falling edge and then machine.SPI to read 18 bytes off the ADC. By the below code.

Code: Select all

def READ_ADC(*_):
    cs.value(0)
    _ = ADC.read(18)
    cs.value(1)
    
sck = Pin(2, Pin.OUT)
mosi = Pin(3, Pin.OUT)
miso = Pin(4, Pin.IN)
cs = Pin(5, Pin.OUT, value=0)
ADC_READY = Pin(14, Pin.IN)
    
ADC = SPI(0, baudrate=int(12e6), sck=sck, mosi=mosi, miso=miso, polarity=0, phase=1)
    
ADC.write(bytes([0x00]*18))
ADC_READY.irq(trigger=Pin.IRQ_FALLING, handler=READ_ADC)
Now heres my issue!

The SPI communication seems to drop at periodic intervals. In the picture below the yellow signal is the ADC_READY signal, green SPI CS and blue SPI SCK. In the beginning it is observed that following each yellow falling edge the data is read with a burst of SPI clocks, why the ready signal returns high, untill next availabe data event. However, at periodic intervals the SPI seems to choke up. The ready signal (yellow) goes low indicating new available measurement data, the IRQ rutine is entered and the SPI CS (green) is held low, but no SPI clocks (blue).

Image

The communication dropout seems to be ongoing for approximately 7.4 ms, as seen in the picture below. By the end multiple SPI SCK bursts seems to be issued faster than normal in an attempt to "catch up", before resuming normal operation. This is of course futile as the ADC have a one-deep output buffer.

Image

This phenomenon seems to be periodic happening approximately every second.

Image

I have been trying to fiddle with this for a couple of days but I am at a loss.

Hoping anyone out there can help me figure this out.

Best regards
Jakob W. Funding

Re: RPi pico machine.SPI periodic dropout.

Posted: Sun Jun 13, 2021 10:19 am
by dk2jk
Hello, I have a similar dropout effect with PWM generation.
Board: Raspberry Pico
Task: A sinusoidal signal is to be generated.
Output takes place via a PWM plus Low-pass RC. The pulse width (duty) is changed step-by-step from a sine table. There should be a continuous signal.
However, the output signal has a dropout for approx. 8 ms at irregular intervals.
What could trigger this dropout ? May be this a System-Timer ?.
This effect also occurs when the Pwm output (pwm.duty_u16) runs via a timer interrupt.

With regards
dk2jk

Code: Select all

N=20
# Sinustabelle ########
sintab =genSintab(n=N,ampl=0x8000,rand=0x4)
'''
sinustabelle
0x8000  0xa78c  0xcb3a  0xe78a  0xf9b8  0xfffc  0xf9b8  0xe78a  0xcb3a  0xa78c
0x7fff  0x5873  0x34c5  0x1875  0x0647  0x0004  0x0647  0x1875  0x34c5  0x5873 
'''

# DDS #################
class dds:
    en      = True
    i=0
def nextstep():     
    if  dds.en:
        dds.i= (dds.i+1) % N    # N is length of sin table   N=20
        pwmSet(sintab[dds.i])   # pwmSet = pwm.duty_u16
    else:
        dds.i=0
        pwmSet(sintab[0])
    pass
   
# Timer #####################
if __name__ =='__main__': 
    while True:
        nextstep()              # step throu sin table
        p17.value(dds.i > N/2)  # square wave output
Image

Re: RPi pico machine.SPI periodic dropout.

Posted: Mon Jun 14, 2021 6:20 am
by fdufnews
Garbage collection perhaps.

Re: RPi pico machine.SPI periodic dropout.

Posted: Mon Jun 14, 2021 9:18 am
by pythoncoder
Very possible, but I think that if Pyboards had this problem it would have been spotted by now.

A minimal test case might be to run PWM at a fixed frequency with a 50% duty ratio, put the result through an RC filter, and look for glitches with a scope.

Re: RPi pico machine.SPI periodic dropout.

Posted: Mon Jun 14, 2021 1:14 pm
by pythoncoder
I tried this and failed to replicate the fault. To provoke occasional GC I first ran some floating point maths. I then ran a large application which performs periodic GC. In each case the output of the filter was stable.

Assuming you guys are running recent firmware I can only conclude that this fault depends on the details code which is running concurrently with the PWM or SPI. If either of you can produce a minimal test case, I suggest raising an issue on GitHub.

Re: RPi pico machine.SPI periodic dropout.

Posted: Mon Jun 14, 2021 8:24 pm
by scruss
It might also be worth dropping something on the MicroPython section of the Raspberry Pi Forums, too. Some of the RP2040 hardware engineers hang out there.

Re: RPi pico machine.SPI periodic dropout.

Posted: Tue Jun 15, 2021 10:49 am
by pythoncoder
Yes. This is definitely worth pursuing. It seems very telling that two different candidates are producing such a similar outcome.

Re: RPi pico machine.SPI periodic dropout.

Posted: Tue Jun 15, 2021 12:34 pm
by hippy
fdufnews wrote:
Mon Jun 14, 2021 6:20 am
Garbage collection perhaps.
Seems unlikely to me. PIO and PWM are separate hardware entities so should continue running even if MicroPython had frozen solid.

In the PWM case the trace appears to show the RC output voltage rising to 3V3 which could only happen with the PWM output frozen high or with a higher duty than in the lookup table.

The only thing PIO and PWM have in common, as far as I can see, is they are both clocked; take the clock away and they will both stop.

I don't know how that could arise but that's my gut feeling.

I would add another free-running PIO square wave generator or preset PWM, see how those behave when things turn south, see if any correlations can be determined.

Re: RPi pico machine.SPI periodic dropout.

Posted: Tue Jun 15, 2021 7:29 pm
by JWF
Hello all, thanks for your interest!

Sorry for the late reply, I have been tied op lately.

Today I have tried to use another pico board with MicroPython v.1.15 (2021-04-18) installed through Thonny, sadly with identical results. However neither of my boards seems to have a problem with PWM generation, either with the PIO squarewave generator running simultaneously or not.

I have tried to check the PIO squarewave generator output when the SPI dropout occurs, and it seems to continue steadily as seen in below scope picture. Here signals are as follows: ADC_READY (yellow), PIO clockgen (purple) and SPI SCK (blue).
Image

Additionally I have tried to create a minimal (and easy replicatable) test case. Here a 50% dutycycle PWM signal is used on pin 16 to create an artificial ADC_READY signal for the pico to run interrupt routine on pin 14. All external hardware is disconnected besides a wire shorting pin 16 to pin 14. Again in the interrupt routine CS is held LOW momentarily while 18 bytes are clocked out on the SPI bus.

This code in its entirety is found below.

Code: Select all

from machine import Pin, SPI, PWM
        
def READ_ADC(*_):
    cs.value(0)
    ADC.read(18)
    cs.value(1)
    
if __name__ == "__main__":
    pin = Pin(16, Pin.OUT)
    pwm = PWM(pin)
    pwm.freq(4000)
    pwm.duty_u16(32512)

    sck = Pin(10, Pin.OUT)
    mosi = Pin(11, Pin.OUT)
    miso = Pin(12, Pin.IN)
    cs = Pin(13, Pin.OUT, value=1)
    ADC_READY = Pin(14, Pin.IN)
    
    ADC = SPI(1, baudrate=int(12e6), sck=sck, mosi=mosi, miso=miso, polarity=0, phase=1)
    ADC_READY.irq(trigger=Pin.IRQ_FALLING, handler=READ_ADC)
 
Sadly the issue consists. PWM pin 14 (yellow) and SPI SCK pin 10 (purple).

Image

As a thought I tried to lower the number of SPI read bytes from 18 to 1 with no behavioural change.

Is the ISR actually running?

Posted: Wed Jun 16, 2021 8:48 am
by pythoncoder
OK, this is clearly a different problem from that reported by @dk2jk. You have an easily reproduced test case where the Pico appears not to be responding to an IRQ. There is a doubt in my mind as to whether the ISR is not being called, or if it is being called but the SPI is not responding.

To resolve this, in the ISR set a pin high, then low and put the scope on that pin.

If the ISR is not being called you might discard all the SPI code and check if the ISR is still not being called. In this instance you would have an extremely simple test case.