Skip to main content

Module spi2

Module spi2 

Source
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§

DisplayBus
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.
DisplayDriver
The initialised on-board display (plus, on Fire27, its backlight pin).
PreparedCard
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()).
PresenceCs
Chip-select wrapper that can freeze the card deasserted to force the absent-card path (see CardPresence::ForceAbsent).
Spi2Parts
The constructed bus + display pins, between Spi2Resources::into_parts and Spi2Parts::finish. bus is still exclusive here — the window for the app’s SD pre-init (see the module docs).
Spi2Resources
SPI2 pins + units (CoreS3): SCK=GPIO36, MOSI=GPIO37, MISO/DC=GPIO35, display CS=GPIO3, card CS=GPIO4, GDMA channel 0.

Enums§

CardPresence
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) after init().

Type Aliases§

CardSpiDevice
The SD-card device: CS is generic so the app can wrap it.
DisplayInitError
DisplayInterface
DisplayOnlyInitError
DisplayOnlyInterface
Display-only DC pin: a plain [Output] on both boards. On CoreS3 this is GPIO35 configured as a real output (which routes the pad) — NOT Gpio35Dc, whose register-level mux relies on with_miso() having routed the pad and would otherwise leave it unrouted (black screen). Used by Spi2Resources::into_display_only.
DisplayOnlyType
CoreS3 display-only panel: no GPIO reset (AW9523B pulses it).
DisplayType
CoreS3 panel: no GPIO reset (AW9523B pulses it; SPI SoftReset fallback).
SpiBusType
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 a SpiDmaBus is required (the CoreS3 uses GDMA for the same reason).
SpiDeviceType
A device on the shared bus with a plain GPIO chip-select (the display).