← index #16912PR #16922
Likely Duplicate · high · value 0.653
QUERY · ISSUE

When miso=None is set, the default miso pin changed state.

openby kaso-0opened 2025-03-11updated 2026-03-25
bugport-rp2

Port, board and/or hardware

Raspberry Pi Pico W

MicroPython version

MicroPython v1.24.1 on 2024-11-29; Raspberry Pi Pico W with RP2040

Reproduction

RS = Pin(16, Pin.OUT)

spi = SPI(0,
baudrate=400000,
polarity=0,
phase=0,
bits=8,
firstbit=SPI.MSB,
sck=Pin(18),
mosi=Pin(19),
miso=None)

RS.value(0)
time.sleep_ms(1)
RS.value(1)

Expected behaviour

Expected to set up SPI without MISO, set the RS pin value to 0, and after 1 ms, set the RS pin value to 1.

Observed behaviour

The SPI was set up correctly, but the RS pin's value never changed.

Additional Information

If I set up the pin after initializing SPI, it works as expected, the used pin 16 is default pin for miso. I think this bug is on other types of RPIs Pico.

Code of Conduct

Yes, I agree

CANDIDATE · PULL REQUEST

ports/rp2: Optional spi_id and optional disabling of MISO pin

closedby Gadgetoidopened 2025-03-13updated 2025-03-26
port-rp2

Summary

This PR includes two changes:

  1. Bring machine.SPI() into parity with machine.I2C(), selecting the default instance where none is specified. See: https://github.com/micropython/micropython/pull/16671
  2. Make machine.SPI(mosi=None) explicitly disable MISO, for configurations which use this as a register-select or data/command pin (very common for write-only LCDs). See: https://github.com/micropython/micropython/issues/16912

I avoided making this change for all pins, since it rarely makes sense for MOSI or SCLK to be disabled.

Testing

Tested various invocations of machine.SPI() on a Pico 2 W.

Notably this change introduces a difference in behaviour that always resets MISO back to the default unless it's specified or set to None. Eg:

>>> machine.SPI()
SPI(0, baudrate=1000000, polarity=0, phase=0, bits=8, sck=18, mosi=19, miso=16)
>>> machine.SPI(miso=None)
SPI(0, baudrate=1000000, polarity=0, phase=0, bits=8, sck=18, mosi=19, miso=None)
>>> machine.SPI()
SPI(0, baudrate=1000000, polarity=0, phase=0, bits=8, sck=18, mosi=19, miso=16)

This is materially different from, for example, mosi where it's impossible to implicitly set a pin back to its default value:

>>> machine.SPI(mosi=23)
SPI(0, baudrate=1000000, polarity=0, phase=0, bits=8, sck=18, mosi=23, miso=16)
>>> machine.SPI()
SPI(0, baudrate=1000000, polarity=0, phase=0, bits=8, sck=18, mosi=23, miso=16)
>>> machine.SPI(mosi=None)
SPI(0, baudrate=1000000, polarity=0, phase=0, bits=8, sck=18, mosi=23, miso=16)

🤔 Maybe this changeset should also require that a pin is specified or that it is otherwise always set back to its default value.

Trade-offs and Alternatives

The latter change is more about explicit vs implicit. It's possibly just to set upt he MISO pin after setting up SPI and it will work as expected. A documentation change establishing this as the accepted pattern might be an alternative... though it might not be possible in all cases for the pin setup to happen before SPI.

Consider the following very crude example driver pattern where a user wishes to save a pin (normally eaten by SPI) and reuse it for register select:

class LCD:
    def __init__(self, spi=None, rs=None):
        self._spi = spi or machine.SPI()

my_led_screen = LCD(rs=machine.Pin(16))

However because the pin is constructed before it's passed into the driver class, its mux will be overwritten by machine.SPI()

With this change, a driver author could use:

class LCD:
    def __init__(self, spi=None, rs=None):
        self._spi = spi or machine.SPI(miso=None)

my_led_screen = LCD(rs=machine.Pin(16))

And SPI would no longer trample the "rs" pin config by setting it up as MISO.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied