displayio: send one area buffer while composing the next - #11468
Conversation
e1ad929 to
63e7ee7
Compare
tannewt
left a comment
There was a problem hiding this comment.
Have you looked at https://github.com/adafruit/circuitpython/pull/11040/changes#diff-ccf8c11ea898a4a001ae54eebc6f4b557c262df85c2998befbb60604b08a7ec2R23-R36 ?
I'd love to do that first and use it here.
common_hal_busio_spi_write_start() starts a write and returns; common_hal_busio_spi_write_end() waits for it without running background tasks, since the caller still holds the bus. The done flag uses the circuitpy_async_flag_t macros from adafruit#11040, so a port can later set it from an interrupt. Ports that have the write set CIRCUITPY_BUSIO_SPI_ASYNC. There is no Python API. raspberrypi: the blocking transfer and the async write share one DMA path. A blocking transfer claims its two channels per call as before; an async write keeps them until deinit. Writes shorter than 32 bytes or from flash/PSRAM stay synchronous. atmel-samd: samd51/same5x only, through shared_dma_transfer_start(). espressif: up to two queued ESP-IDF transactions (8 KB). Writes of four bytes or less, longer writes and word sizes other than 8 bits stay synchronous.
A display bus can now have send_async next to send, and flush waits for it. FourWire provides both when the SPI port has an async write. BusDisplay then uses two area buffers: it composes the next one while the previous one is sent. Background tasks still run only while the bus is released, so another device on the same SPI bus is not locked out. Full-screen repaint: PicoPad (RP2040) 76.7 -> 57.7 ms, PyBadge (SAMD51) 26.5 -> 15.6 ms, Feather ESP32-S3 TFT 29.9 -> 19.5 ms. Ports without the async write build the same code as before.
63e7ee7 to
aac8aab
Compare
|
I reworked the busio part to match the abusio interface: For now the flag is set in |
common_hal_busio_spi_write_end() becomes common_hal_busio_spi_end(). It finishes whatever a common_hal_busio_spi_*_start() call started, so an async read or full-duplex transfer added later can use it too.
|
I renamed |
py/ is kept for the core; circuitpy_async_flag.h becomes supervisor/shared/async_flag.h. No code changes.
tannewt
left a comment
There was a problem hiding this comment.
Thanks! Please follow up with a new module for async spi and move the implementation there.
BusDisplay on a FourWire bus now composes the next area buffer while the previous one is sent over SPI with DMA.
busio: internalcommon_hal_busio_spi_write_async()andcommon_hal_busio_spi_wait(), enabled byCIRCUITPY_BUSIO_SPI_ASYNC. Implemented on raspberrypi, samd51/same5x and espressif. No new Python API.displayio: the display bus gets an optionalsend_async. FourWire provides both when the SPI port has the async write. Without a DC pin, with a chip select toggled per byte, and on the other buses nothing changes.Full-screen repaint of a TileGrid with an 8-bit Bitmap, main -> this PR:
Flash: PicoPad +624 B, Fruit Jam +576 B, Feather ESP32-S3 +528 B, PyBadge +320 B.
With an async bus the second area buffer is on the stack during a refresh. A full-screen refresh at the Python recursion limit ran without a crash on both boards. On RP2 the display's SPI bus keeps one DMA channel claimed until it is deinitialized.
picogame will use this in a follow-up PR and drop its own port DMA backends.