I tried the very promising PIO features on new RP2350 for SPI and later for QSPI implementation.
Board: SparkFun Pro Micro - RP2350
Coding: MicroPython, with PIO
1. Examples are wrong:
I have started with:
https://github.com/raspberrypi/pico-mic ... pio_spi.py
https://datasheets.raspberrypi.com/pico ... 4wLjAuMA.., page 24ff
Actually, I want to have a smooth SCLK signal, even all is sent as bytes. There are gaps between bytes, esp. 84 micro-seconds between the 1st and 2nd byte. Afterwards, every byte has a gap of 15 micro-seconds before the next byte comes.
Why?
Is it possible to make the entire SPI transaction without any gaps on SCLK? Even it works this way, but the duration of a SPI transaction is way too long!
Here the corrected SPI examples (PIO SPI as well as regular SPI).
The intention: reuse the same SPI pins on the board as regular SPI vs. PIO SPI. Therefore, the nCS signal is not part of the sideset, instead handled as GPIO signal before and after every transaction (I want to have anyway a multi-byte SPI transaction without nCS going high all the time after every byte).
PIO SPI (corrected and usage example):The regular SPI:How to get a PIO SPI working for 40 MHz?
I need actually a QSPI, with 40 MHz. I am not convinced that PIO could do, e.g.: first byte is single lane (8bit), all other quad-lane (as 32bit words). Also changing from Big Endian on ADDR/ALT phase to Little Endian on data payload part. No idea how to do (maybe just all as single bytes). I do not want to shuffle all the bytes around before I send a transaction (vie writing to FIFO).
Board: SparkFun Pro Micro - RP2350
Coding: MicroPython, with PIO
1. Examples are wrong:
I have started with:
https://github.com/raspberrypi/pico-mic ... pio_spi.py
https://datasheets.raspberrypi.com/pico ... 4wLjAuMA.., page 24ff
- This Python code has errors: the self is missing in the class methods, you cannot use the methods of the class.
- the MISO (in_base=) uses the SCLK pin (cannot be right)
- the MISO signal (as an input) is not configured as an input: it will never receive anything
- the example creates just single-byte-transactions: after each byte is a de-assertion of the nCS signal (I want to have a burst of bytes as a single SPI transaction, works only by controlling nCS signal outside the PIO state machine. The PIO state machine de-asserts after every byte the nCS signal (not correct).
- The maximum clock speed for a PIO SPI is below 36.6MHz. Otherwise, a Python error is generated (too fast clock speed).
- The maximum clock speed which works without bit errors is: 15 MHz. Anything faster has bit error. Test case is: connect MOSI with MISO and receive what was sent.
- the regular SPI (as MicroPython SPI device) works up to below 75MHz! Faster fails also with bit errors. Remark: the SPI SCLK signal becomes not 50% duty cycle anymore above 20 MHz (not nice, but OK).
Actually, I want to have a smooth SCLK signal, even all is sent as bytes. There are gaps between bytes, esp. 84 micro-seconds between the 1st and 2nd byte. Afterwards, every byte has a gap of 15 micro-seconds before the next byte comes.
Why?
Is it possible to make the entire SPI transaction without any gaps on SCLK? Even it works this way, but the duration of a SPI transaction is way too long!
Here the corrected SPI examples (PIO SPI as well as regular SPI).
The intention: reuse the same SPI pins on the board as regular SPI vs. PIO SPI. Therefore, the nCS signal is not part of the sideset, instead handled as GPIO signal before and after every transaction (I want to have anyway a multi-byte SPI transaction without nCS going high all the time after every byte).
PIO SPI (corrected and usage example):
Code:
import rp2from machine import Pin@rp2.asm_pio(out_shiftdir=0, autopull=True, pull_thresh=8, autopush=True, push_thresh=8, sideset_init=(rp2.PIO.OUT_LOW), out_init=rp2.PIO.OUT_LOW)def spi_cpha0(): # Note X must be preinitialised by setup code before first byte, we reload after sending each byte # Would normally do this via exec() but in this case it's in the instruction memory and is only run once set(x, 6) # Actual program body follows wrap_target() pull(ifempty) .side(0x0) [1] label("bitloop") out(pins, 1) .side(0x0) [1] in_(pins, 1) .side(0x1) jmp(x_dec, "bitloop") .side(0x1) out(pins, 1) .side(0x0) # last bit (of 8) and prepare next byte shift set(x, 6) .side(0x0) # Note this could be replaced with mov x, y for programmable frame size in_(pins, 1) .side(0x1) jmp(not_osre, "bitloop") .side(0x1) # Fallthru if TXF empties #nop() .side(0x0) [1] # CSn back porch - we drive nCS as GPIO wrap()class PIOSPI: def __init__(self, sm_id, pin_mosi, pin_miso, pin_sclk, cpha=False, cpol=False, freq=1000000): assert(not(cpol or cpha)) #MISO input must be configured as input! MISO = Pin(pin_miso, Pin.IN) self._sm = rp2.StateMachine(sm_id, spi_cpha0, freq=4*freq, sideset_base=Pin(pin_sclk), out_base=Pin(pin_mosi), in_base=Pin(pin_miso)) self._sm.active(1) # Note this code will die spectacularly cause we're not draining the RX FIFO def write_blocking(self, wdata): #this creates Python error message, but hard to see! nCS.value(0) for b in wdata: self._sm.put(b << 24) nCS.value(1) def read_blocking(self, n): #how can this work without to shift out anything on MOSI? data = [] for i in range(n): data.append(self._sm.get() & 0xff) return data def write_read_blocking(self, wdata): rdata = [] nCS.value(0) for b in wdata: self._sm.put(b << 24) rdata.append(self._sm.get() & 0xff) nCS.value(1) return rdata print("PIO SPI")#ATT: sideset uses 22 as SCLK, pin 21 as nCS is driven separately as GPIO#otherwise we have nCS deasserted after each byte!nCS = Pin(21, Pin.OUT)nCS.value(1)#after 1 MHz SCLK - the clock becomes not 50% duty cycle and slower,#the max. SCLK is 37,500,000 Hz, but beyond 15.1 MHz we get bit errors on MISO!#just 1MHz seems to be OK for 50% duty cyclespi = PIOSPI(0, 23, 20, 22, freq=15000000)wdata = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]rdata = spi.write_read_blocking(wdata)print(rdata)
Code:
from machine import SPIfrom machine import Pinspi = SPI(0, 10000000, polarity=0, phase=0)nCS = Pin(21, Pin.OUT)buf = bytearray(9)nCS.value(0)spi.write_readinto('Hello SPI', buf)nCS.value(1)print(buf)
I need actually a QSPI, with 40 MHz. I am not convinced that PIO could do, e.g.: first byte is single lane (8bit), all other quad-lane (as 32bit words). Also changing from Big Endian on ADDR/ALT phase to Little Endian on data payload part. No idea how to do (maybe just all as single bytes). I do not want to shuffle all the bytes around before I send a transaction (vie writing to FIFO).
Statistics: Posted by tjakel — Wed Aug 21, 2024 9:57 pm — Replies 0 — Views 0