esp32_i2s
I2S0, driven as a DMA loopback.
The classic ESP32’s I2S has no CPU-accessible FIFO — data only moves
through the peripheral’s own DMA. So even a loopback is a full DMA transfer:
a transmit chain feeds the serialiser, a receive chain drains the
deserialiser, and sig_loopback ties the transmitter’s clock and data to
the receiver internally.
One I2S block therefore tests its own whole data path — FIFO, serialiser, deserialiser, and both DMA engines — with no pins and no second peripheral: transmit a buffer, receive it back through the internal loopback, compare.
The transmitter is master (it generates the bit and word clocks); the receiver is slave and consumes those clocks over the loopback. Sixteen bits per sample, two channels; the exact sample rate is immaterial when the block only talks to itself.
One pad, for the data line
Section titled “One pad, for the data line”sig_loopback shares the word and bit clocks between transmitter and
receiver internally, but not the serial data — verified on silicon,
where the receiver otherwise clocks in nothing but zeros. So the data output
and data input are routed to one pad through the GPIO matrix: the
transmitter drives it, the receiver reads it, no external wire. That pad is
the only pin this needs.
Buffers and descriptors must live in DMA-reachable RAM and be word-aligned —
[soc_esp32::dma::build_chain] checks both. This crate reuses that tested
descriptor machinery rather than re-deriving the lldesc format.
Register facts
Section titled “Register facts”DR_REG_I2S_BASE = 0x3FF4F000, offsets from esp-idf soc/i2s_reg.h, field
positions from soc/i2s_struct.h, the sequence from hal/i2s_ll.h.
| Register | Offset | Fields used |
|---|---|---|
CONF |
0x08 |
resets [3:0], starts [5:4], slave [7:6], MSB_SHIFT [11:10], SIG_LOOPBACK 18 |
INT_RAW/INT_CLR |
0x0C/0x18 |
IN_SUC_EOF 9 |
FIFO_CONF |
0x20 |
DSCR_EN 12, TX_FIFO_MOD [15:13], RX_FIFO_MOD [18:16], data-num [11:0] |
RXEOF_NUM |
0x24 |
words to receive before EOF |
CONF_CHAN |
0x2C |
TX_CHAN_MOD [2:0], RX_CHAN_MOD [4:3] |
OUT_LINK/IN_LINK |
0x30/0x34 |
ADDR [19:0], START 29 |
LC_CONF |
0x60 |
in/out/AHBM resets [3:0] |
CLKM_CONF |
0xAC |
CLKM_DIV_NUM [7:0], _B [13:8], _A [19:14], CLKA_ENA 21 |
SAMPLE_RATE_CONF |
0xB0 |
bck-div [11:0], bits-mod [23:12] |
Structs
Section titled “Structs”I2sLoopback
Section titled “I2sLoopback”struct I2sLoopback { /* private fields */ }I2S0, configured for an internal DMA loopback.
I2sStream
Section titled “I2sStream”struct I2sStream<'a> { /* private fields */ }A running continuous DMA stream over the I2S loopback.
The descriptors form a ring the engine cycles endlessly; the CPU services one
buffer per IN_SUC_EOF. Between the EOF for a buffer and the engine lapping
back to it there are count - 1 buffers of slack — the window in which the
CPU consumes the received data and refills the transmit buffer.
I2sError
Section titled “I2sError”enum I2sError { BadBuffer, Timeout, BadGeometry,}Why a loopback failed.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq