Description
While playing around with creating and destroying displays to exercise the supervisor heap, I came across some strange behavior: When a board.SPI()
object has ever been used by a FourWire
or a SharpMemoryFramebuffer
that has since been deinitialized, subsequent sessions can never reconstruct it, because its pins are still in use.
Steps to reproduce:
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 6.0.0-rc.0-61-g1a677406b-dirty on 2020-10-21; CWtest with samd51G19
>>> import board, displayio
>>> displayio.FourWire(board.SPI(), command=board.RX, chip_select=board.TX)
<FourWire>
>>> displayio.release_displays()
>>>
soft reboot
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:
Hello World!
Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 6.0.0-rc.0-61-g1a677406b-dirty on 2020-10-21; CWtest with samd51G19
>>> import board
>>> board.SPI()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: SCK in use
>>>
or
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 6.0.0-rc.0-61-g1a677406b-dirty on 2020-10-21; CWtest with samd51G19
>>> import board, displayio, sharpdisplay
>>> sharpdisplay.SharpMemoryFramebuffer(board.SPI(), board.RX, 400, 240)
<SharpMemoryFramebuffer>
>>> displayio.release_displays()
>>>
soft reboot
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:
Hello World!
Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 6.0.0-rc.0-61-g1a677406b-dirty on 2020-10-21; CWtest with samd51G19
>>> import board
>>> board.SPI()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: SCK in use
>>>
What happens is:
common_hal_displayio_fourwire_construct()
andcommon_hal_sharpdisplay_framebuffer_construct()
callcommon_hal_busio_spi_never_reset(self->bus)
.release_displays()
doesn’t do anything with the SPI bus.- When the session ends,
cleanup_after_vm()
→reset_port()
→reset_all_pins()
does not reset the pins because they were marked asnever_reset
. cleanup_after_vm()
→reset_board_busses()
setsspi_singleton = NULL;
so that the next call toboard.SPI()
needs to recreate it, but it can’t because the pins are still in use.
I’m not sure how this is supposed to work, but there seems to be a hole in the logic somewhere. I suppose someone should call common_hal_busio_spi_deinit()
at some point so that the pins are made resettable again? Maybe reset_board_busses()
before it effectively leaks the resources used by the spi_singleton
, but then reset_port()
and reset_board_busses()
come in the wrong order?
Some of the observations made by @hatchman in #3508 (comment) seem related (but probably unrelated to the actual issue of #3508, so I’m opening a new one).