hal::bus
Bus abstraction traits and types.
Three-layer bus model:
- Layer 1 – PhysicalBus: raw register-level driver (e.g.
esp32_spi). - Layer 2 – Bus: protocol-level abstraction (e.g.
spi_bus::SpiBus). - Layer 3 – Logical driver: device driver using
BusHandle(e.g.bme280::Bme280).
The BusHandle struct is the bridge between layers 2 and 3: it wraps
a &dyn Bus reference and provides delegation methods.
Structs
Section titled “Structs”BusDevice
Section titled “BusDevice”struct BusDevice { pub name: &'static str, pub logical_driver: &'static str, pub bus: &'static str, pub cs_pin: Option<u8>, pub bus_speed: BusSpeed,}Logical device attached to a bus, declared in the board manifest.
References: BusSpeed
BusHandle
Section titled “BusHandle”struct BusHandle<'a> { /* private fields */ }Opaque handle returned to a logical driver.
Wraps a &dyn Bus and offers the small copy-based surface logical drivers
were written against — each method builds a [Op] and calls
[Bus::transfer]. Logical drivers never see the bus type, and never had to
change when the trait moved to a transfer list.
The borrow is 'a, not 'static: every method here takes &self and
[Bus::transfer] does too, so nothing the handle does needs the bus to
live forever. A driver can therefore be handed a bus that lives only as
long as the task’s stack frame — a board no longer has to leak its buses
into statics just to satisfy this type. Building one is usually
(&bus).into() via the [From] impl below, so a logical driver’s
constructor reads Mpu6886::new(&bus) rather than
Mpu6886::new(BusHandle::new(&bus)).
Implements: Clone, Copy, From
BusMapping
Section titled “BusMapping”struct BusMapping { pub name: &'static str, pub kind: BusKind, pub base_addr: u32, pub irq: u8, pub dma_capable: bool, pub dma_pool_bytes: u32, pub config: BusConfig,}Bus mapping entry in the board manifest.
References: BusConfig, BusKind
I2cConfig
Section titled “I2cConfig”struct I2cConfig { pub sda: u8, pub scl: u8, pub speed: BusSpeed,}Pins and clock for an I2C controller. Default is standard-mode (100 kHz)
on pins 0/0.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
References: BusSpeed
struct Op<'a> { pub tx: Option<&'a [u8]>, pub rx: Option<&'a mut [u8]>, pub word_bits: u8, pub cs: CsHold, pub delay_us: u32,}One step of a bus transfer list.
[Bus::transfer] takes a slice of these and runs them in order. Both tx
and rx set is a full-duplex exchange; tx alone is a write; rx alone is
a read. The struct is #[non_exhaustive]: build it with [Op::write],
[Op::read], or [Op::exchange] and the builder setters, so later fields
(per-op speed, and so on) stay additive.
References: CsHold
PeripheralMapping
Section titled “PeripheralMapping”struct PeripheralMapping { pub name: &'static str, pub base_addr: u32, pub irq: u8, pub dma_capable: bool, pub dma_pool_bytes: u32,}Direct (non-bus-attached) peripheral mapping.
ServiceMapping
Section titled “ServiceMapping”struct ServiceMapping { pub name: &'static str, pub always: bool,}Service task declaration in the board manifest.
SpiConfig
Section titled “SpiConfig”struct SpiConfig { pub mosi: u8, pub miso: u8, pub sck: u8, pub max_speed: BusSpeed, pub mode: SpiMode,}Pins, clock ceiling and mode for a SPI controller.
Default is mode 0 at 1 MHz on pins 0/0/0 — a placeholder to spread with
.., not a wiring anyone should run.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
UartConfig
Section titled “UartConfig”struct UartConfig { pub tx: u8, pub rx: u8, pub baud: u32, pub data_bits: UartDataBits, pub parity: UartParity, pub stop_bits: UartStopBits,}Pins, baud rate and framing for a UART. Default is 115200 8N1 on pins
0/0.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
References: UartDataBits, UartParity, UartStopBits
BusConfig
Section titled “BusConfig”enum BusConfig { Spi(SpiConfig), I2c(I2cConfig), Uart(UartConfig),}Complete configuration for initialising a peripheral bus.
One variant per bus kind, each wrapping a named config struct so a driver
or port type can hold exactly the one it needs ([SpiConfig] on a SPI
port, never a UART’s). The const fn helpers cover the common shapes
(spi_mode0, i2c, uart_8n1); anything else is built with struct
syntax and ..Default::default().
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
References: I2cConfig, SpiConfig, UartConfig
BusError
Section titled “BusError”enum BusError { Timeout, CrcMismatch, DeviceNotResponding, InvalidConfig, DmaError, Busy,}Bus error enumeration.
Implements: Clone, Copy, Debug, Display, Eq, From, PartialEq, StructuralPartialEq
BusKind
Section titled “BusKind”enum BusKind { Spi, I2c, Uart, Can, OneWire,}Kind of bus peripheral.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
BusSpeed
Section titled “BusSpeed”enum BusSpeed { KHz(u32), MHz(u32), Standard100k, Fast400k, FastPlus1M, HighSpeed3M4,}Bus clock / data-rate selection.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
CsHold
Section titled “CsHold”enum CsHold { Release, Keep,}Chip-select behaviour after an [Op] completes.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
SpiMode
Section titled “SpiMode”enum SpiMode { Mode0, Mode1, Mode2, Mode3,}SPI clock polarity / phase modes.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
UartDataBits
Section titled “UartDataBits”enum UartDataBits { Bits5, Bits6, Bits7, Bits8,}Number of data bits per UART character.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
UartParity
Section titled “UartParity”enum UartParity { None, Even, Odd,}UART parity setting.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
UartStopBits
Section titled “UartStopBits”enum UartStopBits { Stop1, Stop1_5, Stop2,}Number of stop bits.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
Traits
Section titled “Traits”trait Bus: Send + Sync { fn transfer(&self, ops: &mut [Op<'_>]) -> BusResult<()> fn max_transfer(&self) -> usize fn kind(&self) -> BusKind fn set_speed(&self, _speed: BusSpeed) -> BusResult<()>}Implemented by bus abstractions (spi_bus, i2c_bus).
UART is deliberately absent: it is a byte stream, not an addressed bus —
see [crate::stream].
These sit above the physical driver and provide a protocol-aware interface. They are linked directly into the calling task — no IPC.
The single transfer entry point is a list of [Op]s run as one logical
transaction. The old copy-based write/read/transfer(tx, rx) methods
live on [BusHandle] now, as thin wrappers that build a one- or two-element
[Op] — so logical drivers keep calling them unchanged.
PhysicalBus
Section titled “PhysicalBus”trait PhysicalBus: PhysicalTransfer { fn init(&mut self, config: &BusConfig) -> BusResult<()>}Implemented by physical driver crates (e.g. esp32_spi).
These have direct hardware register access and run at the lowest level of
the driver stack. This trait adds the one construction-time method to
[PhysicalTransfer]; once init has run, callers need only the supertrait.
PhysicalTransfer
Section titled “PhysicalTransfer”trait PhysicalTransfer: Send + Sync { fn exchange(&self, tx: &[u8], rx: &mut [u8]) -> BusResult<()> fn set_speed(&self, _speed: BusSpeed) -> BusResult<()>}The run-time half of a physical driver: everything that moves bytes on a
peripheral that is already up. Every method takes &self, so a shared
reference to a driver is itself a PhysicalTransfer (the blanket impl
below) and a bus layer can hold one without owning the driver.
Construction-time work (init, &mut self) lives on [PhysicalBus],
which extends this trait; the split is what makes the blanket impl sound.
Functions
Section titled “Functions”spin_rough_us
Section titled “spin_rough_us”fn spin_rough_us(us: u32)Busy-wait approximately us microseconds, for [Op::delay_us].
A stopgap, exactly like soc_esp32::poll: the loop bound is iterations, not
time, so its real duration drifts with the CPU clock. It exists so a per-op
delay means something today; a faithful microsecond needs the portable
cycle counter (doc/plan-arm32.md, Phase 2.4). Conservative by design — at
a lower clock it over-waits rather than under-waits.
Constants
Section titled “Constants”REG_BURST_MAX
Section titled “REG_BURST_MAX”const REG_BURST_MAX: usize;Longest burst [BusHandle::read_regs] reads over SPI. The handle stages
the exchange in a stack buffer one byte longer than this; 64 matches the
SPI controller’s data buffer, so a burst under it is one framed transfer.
Type Aliases
Section titled “Type Aliases”BusResult
Section titled “BusResult”type BusResult<T> = Result<T, BusError>;Result type for bus operations.
References: BusError