Skip to content

soc_esp32::gpio_matrix

The GPIO matrix: routing peripheral signals to arbitrary pads.

This is what makes the ESP32 unusual. Rather than each pad having a short fixed list of functions, a 256×40 crossbar connects almost any peripheral signal to almost any pad, in either direction. It costs a couple of clock cycles of latency compared to the IO_MUX direct path, which is why the high-speed peripherals also have “native” pads that bypass it.

Two register files, indexed differently, which is the easiest thing here to get wrong:

  • GPIO_FUNCn_IN_SEL_CFG_REG at 0x130 + 4n, where n is the signal index. Says which pad a peripheral input reads from.
  • GPIO_FUNCn_OUT_SEL_CFG_REG at 0x530 + 4n, where n is the GPIO number. Says which peripheral signal a pad drives.

The 256 input registers occupy 0x130..0x530, which is exactly where the output registers begin — a useful check that both constants are right.

Confirmed against esp-idf soc/gpio_reg.h and soc/gpio_sig_map.h.

unsafe fn connect_input(signal_idx: u32, pin: u8, invert: bool) -> hal::bus::BusResult<()>

Route a peripheral input so it reads from pin.

Writes the matrix register for signal_idx. Two peripherals cannot share an input signal index, so the caller must own the controller.

References: BusResult

unsafe fn connect_output(pin: u8, signal_idx: u32, peripheral_oe: bool, invert: bool) -> hal::bus::BusResult<()>

Route a peripheral output so it drives pin.

peripheral_oe selects who controls the pad’s output enable. For a normal output the peripheral does, and this is true. For an open-drain line driven by a peripheral – I2C – it is also the peripheral: the controller releases the line by de-asserting its own output enable, which is exactly how it generates a logical 1 and how it lets a slave stretch the clock. Forcing OEN_SEL on instead would hand output-enable to the GPIO_ENABLE register and pin the line permanently driven or permanently released.

Writes the matrix register for pin.

References: BusResult

unsafe fn set_drive(pin: u8, drive: hal::pinmux::PinDrive) -> hal::bus::BusResult<()>

Set a pad’s output driver to push-pull or open-drain.

Writes GPIO_PINn_REG for pin, read-modify-write so the interrupt configuration in the same register survives.

References: BusResult, PinDrive

fn signal_index(signal: hal::pinmux::Signal) -> Option<u32>

Peripheral signal index for a [Signal], as used by both register files.

None if this chip has no such controller. Values from esp-idf soc/gpio_sig_map.h; input and output indices coincide for every signal below, which is true on the classic ESP32 but is not a general rule.

References: Signal

const IN_CONST_ONE: u32;

IN_SEL value that feeds a peripheral input a constant 1.

const IN_CONST_ZERO: u32;

IN_SEL value that feeds a peripheral input a constant 0.

const SIG_GPIO_OUT: u32;

The OUT_SEL value meaning “this pad is driven by the GPIO_OUT register”, i.e. it is a plain software-controlled GPIO rather than a peripheral pin.