hal::pinmux
Pin routing: connecting a peripheral signal to a physical pin.
This is the one hardware operation that has no portable implementation, only a portable contract. Every SoC family solves it differently:
- ESP32 has a GPIO matrix — nearly any signal can reach nearly any pin, via a routing table, plus a small set of IO_MUX “native” pins that bypass the matrix for lower latency.
- STM32 has alternate functions — each pad has a fixed, short list of functions and you select one by index. A signal that is not on that pad’s list cannot reach it at all.
- NXP / i.MX has IOMUXC, a third model again.
A driver that wants SDA on GPIO 21 should not have to know which of those it
is talking to, and a board manifest cannot express any of them. So the SoC
layer implements [PinMux], the driver calls [PinMux::route], and a
request the silicon cannot satisfy comes back as
BusError::InvalidConfig rather than as a peripheral quietly wired to
nothing.
Structs
Section titled “Structs”PinConfig
Section titled “PinConfig”struct PinConfig { pub drive: PinDrive, pub pull: PinPull, pub input: bool,}Everything about a pad that is not “which signal”.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
PinDrive
Section titled “PinDrive”enum PinDrive { PushPull, OpenDrain,}How the pad drives its output.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
PinPull
Section titled “PinPull”enum PinPull { None, Up, Down,}Internal pull resistor selection.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
Signal
Section titled “Signal”enum Signal { UartTx(u8), UartRx(u8), UartCts(u8), UartRts(u8), I2cSda(u8), I2cScl(u8), SpiMosi(u8), SpiMiso(u8), SpiSck(u8), SpiCs(u8), PulseOut(u8), PwmOut(u8), CanTx, CanRx, I2sTxData, I2sRxData,}A peripheral signal that can be routed to a pin.
The u8 is the controller instance, not a pin: Signal::I2cSda(0) is
I2C0’s SDA line. Which controllers exist is a property of the SoC, so
routing a signal for an instance the chip does not have is an error, not a
compile failure — a board manifest is data, and data can be wrong.
Names are the function, not a vendor’s peripheral: a pulse-train output is
PulseOut, whatever the SoC calls the block that generates it (the ESP32’s
is “RMT”). #[non_exhaustive] because the set of functions a chip family can
route is open — a second SoC will add signals this one has no block for, and
that must not be a breaking change to the contract crate.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
SignalDirection
Section titled “SignalDirection”enum SignalDirection { Input, Output, Bidirectional,}Which way a signal flows through the pad.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
Traits
Section titled “Traits”PinMux
Section titled “PinMux”trait PinMux { fn can_route(&self, signal: Signal, pin: u8) -> BusResult<()> fn route(&self, signal: Signal, pin: u8, config: PinConfig) -> BusResult<()> fn is_native(&self, signal: Signal, pin: u8) -> bool}Connects peripheral signals to pins, in whatever idiom the SoC uses.
Implemented once per SoC family (soc-esp32, soc-stm32f4,
…), never per board and never per driver.