Skip to content

soc_esp32::io_mux

IO_MUX: per-pad configuration.

Every ESP32 pad has one register controlling its alternate function, input enable, and pull resistors. Two things about it are traps:

  1. The register offsets are not linear in the pin number. GPIO0 is at 0x44, GPIO1 at 0x88, GPIO3 at 0x84. The table below is the only correct way to get from a pin to its register; computing pin * 4 produces plausible addresses that configure the wrong pad.
  2. The “GPIO” function number is not uniform. For most pads it is function 2, for a few it is 0. A driver that hardcodes one value silently puts some pads into a completely different peripheral’s function.

Both used to be duplicated, separately, inside esp32-uart and esp32-spi.

Offsets confirmed against esp-idf soc/io_mux_reg.h (GPIO_PIN_MUX_REG).

unsafe fn configure(pin: u8, func: u32, input_enable: bool, pull: hal::pinmux::PinPull) -> hal::bus::BusResult<()>

Configure a pad: alternate function, input enable, pull resistors.

Read-modify-write, so it does not disturb drive strength or the settings of the other fields.

Writes the IO_MUX register for pin. The caller must own that pad — two drivers configuring the same pad race, and the loser gets a peripheral wired somewhere it does not expect.

References: BusResult, PinPull

const fn gpio_function(_pin: u8) -> u32

The MCU_SEL value that puts a pad under GPIO-matrix control.

Function 2, for every pad without exception. esp-idf spells this PIN_FUNC_GPIO and writes it unconditionally in gpio_pad_select_gpio(); every FUNC_<pad>_GPIOn in soc/io_mux_reg.h is 2, including FUNC_GPIO34_GPIO34 and FUNC_GPIO39_GPIO39.

This used to return 0 for pins 34-39 on the theory that input-only pads have no alternate functions and so start at GPIO. They do have the usual function list, and function 0 on those pads is a different signal entirely – a matrix-routed input on GPIO34-39 would have been wired to the wrong thing.

fn is_input_only(pin: u8) -> bool

Whether a pad can drive an output at all.

GPIO34-39 are input-only on the classic ESP32: they have no output driver, so routing an output signal there produces a pin that reads as whatever the board pulls it to, with no error from the hardware.

fn offset(pin: u8) -> Option<u32>

IO_MUX register offset for a GPIO number.

None for GPIO 28-31, which have no IO_MUX register on this chip at all.

GPIO20 and GPIO24 do have registers and are listed here, even though the common ESP32 packages (WROOM, WROVER, PICO-D4) do not bond them to a pin. That is a package property, not a chip property — ESP32-PICO-V3 exposes GPIO20 — and this crate describes the chip. A board that names a pin its package does not carry is the board manifest’s error to make.