Expand description
Shared SPI2 bus: on-board ILI9342C display + SD-card slot.
Both boards route the display and the SD slot over the same SPI2 bus, so
their bring-up is coupled and ordering-sensitive. This module owns that
hardware knowledge; the SD-card driver stays with the application (the
sdspi crate is not yet on crates.io) and connects through the generic
chip-select + SpiDevice returned by Spi2Parts::finish.
§Bring-up sequence (application side)
The recommended path is Spi2Parts::finish_sd: the BSP owns the ≥74-clock
SD power-up idle and hands back a pre-initialised, presence-resolved
PreparedCard. The app supplies only its SD driver plus retry/degrade
policy — no board detail, no pre-init loop:
let (mut parts, card_cs) = board.spi2.into_parts(dma_rx_buf, dma_tx_buf)?;
// Display comes up UNCONDITIONALLY (works even with a dead/absent card).
// `CardPresence::ForceAbsent` reaches the SD-absent path with a card in slot.
let (display, prepared) = parts.finish_sd(card_cs, CardPresence::Detect).await?;
let mut sd = SdSpi::new(prepared.into_inner());
// bounded init retries; on failure, degrade (SD absent). Both real-absent
// and ForceAbsent fail here, on the same single degrade path.The lower-level Spi2Parts::finish primitive (no pre-init, plain generic
card_cs) remains for callers that drive the exclusive bus themselves
before sharing it.
§CoreS3: GPIO35 is shared between SPI2 MISO and display DC
The SPI peripheral owns GPIO35 as MISO input (via with_miso); the display
DC is driven on the same pad through direct GPIO register writes
(Gpio35Dc). Spi2Parts::finish calls
gpio35_disable_output
after the display init so the pad returns to high-impedance MISO input —
without it sd_card.init() reads garbage on MISO and never completes. At
runtime, every SD operation must re-assert that mux (pass the same function
as the block-device handler’s pre-op hook). This is safe only while the
display and the SD card share one task/bus mutex with no .await between a
DC write and the SPI transfer.
§Display/SD task join order differs per chip — do not unify
When the app joins the display flush loop and the SD block-device handler in one task: on fire27 (ESP32/PDMA) the SD handler must be polled FIRST so it grabs the bus mutex preferentially — the mirror order reliably wedges the SD path. cores3 (ESP32-S3/GDMA) needs the opposite order. The two chips’ interrupt/DMA timing has opposite contention characteristics; don’t change either without re-validating both targets on hardware.
Structs§
- Display
Bus - A display brought up on the shared SPI2 DMA bus with no SD-card path
(
Spi2Resources::into_display_only). For LVGL and any DMA display-only app that does not touch the SD slot. - Display
Driver - The initialised on-board display (plus, on Fire27, its backlight pin).
- Prepared
Card - A card device on the shared SPI2 bus, pre-initialised by
Spi2Parts::finish_sd(the ≥74-clock power-up idle has run) and presence-resolved. The BSP owns everything up to here with no SD-driver type in its graph; the app supplies only its SD driver:SdSpi::new(prepared.into_inner()). - Presence
Cs - Chip-select wrapper that can freeze the card deasserted to force the
absent-card path (see
CardPresence::ForceAbsent). - Spi2
Parts - The constructed bus + display pins, between
Spi2Resources::into_partsandSpi2Parts::finish.busis still exclusive here — the window for the app’s SD pre-init (see the module docs). - Spi2
Resources - SPI2 pins + units (Fire27): SCK=GPIO18, MOSI=GPIO23, MISO=GPIO19, display CS=GPIO14 / DC=GPIO27 / RST=GPIO33 / BL=GPIO32, card CS=GPIO4, PDMA SPI2 channel.
Enums§
- Card
Presence - Whether the SD slot should behave as populated or be forced to degrade.
Functions§
- display_
config - Display device config: 40 MHz Mode 0.
- sd_
init_ config - Bus base config: 400 kHz Mode 0 — the clock SD cards require during init.
The app raises the card device’s clock (via
SetConfig) afterinit().
Type Aliases§
- Card
SpiDevice - The SD-card device: CS is generic so the app can wrap it.
- Display
Init Error - Display
Interface - Display
Only Init Error - Display
Only Interface - Display-only DC pin: a plain [
Output] on both boards. On CoreS3 this is GPIO35 configured as a real output (which routes the pad) — NOTGpio35Dc, whose register-level mux relies onwith_miso()having routed the pad and would otherwise leave it unrouted (black screen). Used bySpi2Resources::into_display_only. - Display
Only Type - Fire27 display-only panel: hardware reset pin.
- Display
Type - Fire27 panel: hardware reset pin.
- SpiBus
Type - The shared bus: descriptor-backed DMA SPI. A plain
Spi::into_async()flush goes “usr-stuck” after the first frame on the ESP32 PDMA path, so aSpiDmaBusis required (the CoreS3 uses GDMA for the same reason). - SpiDevice
Type - A device on the shared bus with a plain GPIO chip-select (the display).