kopia lustrzana https://github.com/peterhinch/micropython-samples
RP2: Bidirectional SPI release.
rodzic
c5dc32b9a2
commit
f22acb644b
30
rp2/RP2.md
30
rp2/RP2.md
|
@ -81,10 +81,19 @@ acquired (MISO):
|
||||||
* `ibuf` A `bytearray` for MISO data. If the quantity of data exceeds the length
|
* `ibuf` A `bytearray` for MISO data. If the quantity of data exceeds the length
|
||||||
of the buffer it will be truncated.
|
of the buffer it will be truncated.
|
||||||
|
|
||||||
|
Using MISO affects the maximum achievable baudrate. This is because the incoming
|
||||||
|
signal is delayed by two system clock periods by the input synchronisers (RP2040
|
||||||
|
manual 3.5.6.3), plus up to one additional system clock period because the slave
|
||||||
|
and master are mutually asynchronous. The latency becomes even worse when using
|
||||||
|
the asynchronous slave because the slave's `sck` is further delayed relative to
|
||||||
|
the master's by the slave's input synchronisers. In testing even 10MHz was too
|
||||||
|
high a rate for reliable reception.
|
||||||
|
|
||||||
## 1.3 Methods
|
## 1.3 Methods
|
||||||
|
|
||||||
* `write(data : bytes)` arg a `bytes` or `bytearray` of data to transmit. Return
|
* `write(data : bytes)` arg a `bytes` or `bytearray` of data to transmit. Return
|
||||||
is rapid with transmission running in the background. Returns `None`.
|
is rapid, returns `None`. The data is queued for transmission. The oldest object
|
||||||
|
is transmitted when the master next initiates a transfer.
|
||||||
* `deinit()` Disables the DMA and the SM. Returns `None`.
|
* `deinit()` Disables the DMA and the SM. Returns `None`.
|
||||||
|
|
||||||
## 1.4 CS/
|
## 1.4 CS/
|
||||||
|
@ -97,12 +106,14 @@ between power-up and application start-up. A value of a few KΩ is suggested.
|
||||||
## 1.5 Callback
|
## 1.5 Callback
|
||||||
|
|
||||||
This runs when the DMA is complete. It takes no args and runs in a hard IRQ
|
This runs when the DMA is complete. It takes no args and runs in a hard IRQ
|
||||||
context. Typical use is to set a `ThreadSafeFlag`, allowing a pending task to
|
context. It should deassert `CS/`. Typical use is to set a `ThreadSafeFlag`,
|
||||||
resume. Typically this will deassert `CS/` and initiate processing of received
|
allowing a pending task to resume, to initiate processing of received data.
|
||||||
data. Note that the DMA completes before transmission ends due to bytes stored
|
|
||||||
in the SM FIFO. This is unlikely to have practical consequences because of
|
Users of low baudrates (<1MHz) should note that the DMA completes before
|
||||||
MicroPython latency: the master executes several MP instructions before the
|
transmission ends due to three bytes stored in the SM FIFO. This is unlikely to
|
||||||
callback runs, and the response to a `ThreadSafeFlag` typically takes >200μs.
|
have practical consequences because of MicroPython latency: where the callback
|
||||||
|
deasserts `CS/` the time between the last edge of `clk` and the trailing edge
|
||||||
|
of `CS/` was 93μs (RP2040 @125MHz).
|
||||||
|
|
||||||
# 2. Nonblocking SPI slave
|
# 2. Nonblocking SPI slave
|
||||||
|
|
||||||
|
@ -252,6 +263,11 @@ The slave will ignore all interface activity until CS/ is driven low. It then
|
||||||
receives data with the end of message identified by a low to high transition on
|
receives data with the end of message identified by a low to high transition on
|
||||||
CS/.
|
CS/.
|
||||||
|
|
||||||
|
The input pins are very sensitive to electrical noise: wiring to the master must
|
||||||
|
be kept very short. This is because of the very high speed of the RP2 internal
|
||||||
|
logic (clock rates >=125MHz). Pulses of a few ns duration can cause the state
|
||||||
|
machine to respond, or can cause an IRQ to be raised.
|
||||||
|
|
||||||
# 3. Pulse Measurement
|
# 3. Pulse Measurement
|
||||||
|
|
||||||
The file `measure_pulse.py` is a simple demo of using the PIO to measure a pulse
|
The file `measure_pulse.py` is a simple demo of using the PIO to measure a pulse
|
||||||
|
|
|
@ -3,15 +3,12 @@
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2025 Peter Hinch
|
# Copyright (c) 2025 Peter Hinch
|
||||||
|
|
||||||
# Performs SPI output: check on scope or LA.
|
# Performs SPI I/O.
|
||||||
# TODO:
|
|
||||||
# Fails if baudrate >= 9MHz
|
# Bidirectional test fails if baudrate >= 9MHz: see RP2.md for reason.
|
||||||
|
|
||||||
# Problem with pin nos?
|
# Problem with pin nos?
|
||||||
# 11 Sept slave works bidirectionally with tx.py, but with this code slave receives
|
|
||||||
# nothing, but responds with correct data. Waveforms look identical. MOSI data looks correct.
|
|
||||||
# Slave gets correct data with unidirectional setup (no ibuf)
|
|
||||||
# 45us after last clock edge, clk emits a 250ns pulse. CS/ goes high 724us after last clock
|
|
||||||
# I think spurious clk is screwing RX SM.
|
|
||||||
from machine import Pin
|
from machine import Pin
|
||||||
import asyncio
|
import asyncio
|
||||||
from .spi_master import SpiMaster
|
from .spi_master import SpiMaster
|
||||||
|
@ -25,12 +22,15 @@ tsf = asyncio.ThreadSafeFlag()
|
||||||
|
|
||||||
|
|
||||||
def callback(): # Hard ISR
|
def callback(): # Hard ISR
|
||||||
pin_cs(1) # Decrease deassert time from 724us to 93us but still fails
|
pin_cs(1) # Decrease deassert time from 724us to 93us
|
||||||
tsf.set() # Flag user code that transfer is complete
|
tsf.set() # Flag user code that transfer is complete
|
||||||
|
|
||||||
|
|
||||||
buf = bytearray(100)
|
buf = bytearray(100)
|
||||||
|
# Bidirectional test:
|
||||||
spi = SpiMaster(6, 1_000_000, pin_sck, pin_mosi, callback, miso=pin_miso, ibuf=buf)
|
spi = SpiMaster(6, 1_000_000, pin_sck, pin_mosi, callback, miso=pin_miso, ibuf=buf)
|
||||||
|
# For unidirectional test issue:
|
||||||
|
# spi = SpiMaster(6, 1_000_000, pin_sck, pin_mosi, callback))
|
||||||
|
|
||||||
|
|
||||||
async def send(data):
|
async def send(data):
|
||||||
|
@ -42,7 +42,7 @@ async def send(data):
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
src_data = bytearray(b"\xFF\x55\xAA\x00the quick brown fox jumps over the lazy dog")
|
src_data = bytearray(b"\x00 The quick brown fox jumps over the lazy dog")
|
||||||
n = 0
|
n = 0
|
||||||
while True:
|
while True:
|
||||||
await send(src_data)
|
await send(src_data)
|
||||||
|
|
|
@ -16,7 +16,7 @@ alloc_emergency_exception_buf(100)
|
||||||
# 2 CS\
|
# 2 CS\
|
||||||
|
|
||||||
|
|
||||||
@rp2.asm_pio(autopull=True)
|
@rp2.asm_pio(autopush=True, autopull=True)
|
||||||
def spi_in():
|
def spi_in():
|
||||||
label("escape") # Just started, transfer ended or overrun attempt.
|
label("escape") # Just started, transfer ended or overrun attempt.
|
||||||
out(y, 32) # Get maximum byte count (blocking wait)
|
out(y, 32) # Get maximum byte count (blocking wait)
|
||||||
|
@ -37,14 +37,13 @@ def spi_in():
|
||||||
jmp("overrun")
|
jmp("overrun")
|
||||||
label("continue")
|
label("continue")
|
||||||
jmp(x_dec, "bit") # Post decrement
|
jmp(x_dec, "bit") # Post decrement
|
||||||
push()
|
# push()
|
||||||
wrap() # Next byte
|
wrap() # Next byte
|
||||||
label("done") # ISR has sent data
|
label("done") # ISR has sent data
|
||||||
out(x, 32) # Discard it
|
out(x, 32) # Discard it
|
||||||
in_(y, 30) # Return amount of unfilled buffer truncated to 30 bits
|
in_(y, 30) # Return amount of unfilled buffer truncated to 30 bits
|
||||||
push()
|
|
||||||
# TODO Is this valid given that push_thresh==8?
|
|
||||||
# Truncation ensures that overrun returns a short int
|
# Truncation ensures that overrun returns a short int
|
||||||
|
# push()
|
||||||
jmp("escape")
|
jmp("escape")
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +94,7 @@ class SpiSlave:
|
||||||
sm_num,
|
sm_num,
|
||||||
spi_in,
|
spi_in,
|
||||||
in_shiftdir=rp2.PIO.SHIFT_LEFT,
|
in_shiftdir=rp2.PIO.SHIFT_LEFT,
|
||||||
# push_thresh=8,
|
push_thresh=8,
|
||||||
in_base=mosi,
|
in_base=mosi,
|
||||||
jmp_pin=sck,
|
jmp_pin=sck,
|
||||||
)
|
)
|
||||||
|
@ -187,7 +186,7 @@ class SpiSlave:
|
||||||
return # ISR runs on trailing edge but SM is not running. Nothing to do.
|
return # ISR runs on trailing edge but SM is not running. Nothing to do.
|
||||||
# See above comment re memfails on next line
|
# See above comment re memfails on next line
|
||||||
sp = self._sm.get() >> 3 # Bits->bytes: space left in buffer or 7ffffff on overflow
|
sp = self._sm.get() >> 3 # Bits->bytes: space left in buffer or 7ffffff on overflow
|
||||||
self._nbytes = self._buflen - sp if sp != 0x7FFFFFF else self._buflen
|
self._nbytes = self._buflen - sp if sp != 0x07FF_FFFF else self._buflen
|
||||||
self._dma.active(0)
|
self._dma.active(0)
|
||||||
self._sm.active(0)
|
self._sm.active(0)
|
||||||
self._tsf.set()
|
self._tsf.set()
|
||||||
|
|
Ładowanie…
Reference in New Issue