# PIO I2S Audio Configuration in Raspberry Pi Pico WAV Player: Complete Technical Guide

> Learn PIO I2S audio configuration for Raspberry Pi Pico WAV player. Discover how state machine 0, DMA, and GPIO pins create high-quality audio output.

- Repository: [Elehobica/rpi_pico_wav_player](https://github.com/elehobica/rpi_pico_wav_player)
- Tags: technical-guide
- Published: 2026-03-01

---

**The rpi_pico_wav_player configures PIO state machine 0 with DMA channels 0 and 1, GPIO pins 18–20, and a 96 MHz USB PLL clock to drive I2S audio output via the RP2040's Programmable I/O subsystem.**

The `elehobica/rpi_pico_wav_player` project leverages the RP2040's Programmable I/O (PIO) to stream WAV files to external I2S DACs such as the PCM5102. Understanding the specific PIO I2S audio configuration is essential for customizing the player or integrating high-fidelity audio into your own Pico-based projects.

## Core PIO Configuration for I2S Output

All PIO-related parameters are encapsulated in an `audio_i2s_config_t` structure defined in [`lib/PlayAudio/i2s_audio_init.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/i2s_audio_init.cpp). This structure is passed to the Pico SDK's `audio_i2s_setup()` helper to initialize the PIO state machine, DMA channels, and GPIO pins.

```cpp
/* lib/PlayAudio/i2s_audio_init.cpp – lines 26-32 */
static audio_i2s_config_t i2s_config = {
    .data_pin        = PICO_AUDIO_I2S_DATA_PIN,
    .clock_pin_base  = PICO_AUDIO_I2S_CLOCK_PIN_BASE,
    .dma_channel0    = 0,
    .dma_channel1    = 1,
    .pio_sm          = 0
};

```

### GPIO Pin Assignment

The configuration assigns specific GPIO pins for the I2S data and clock lines:

- **`data_pin`** – Mapped to `PICO_AUDIO_I2S_DATA_PIN` (GPIO 18 by default), this pin carries the serial audio data (SD) to the DAC.
- **`clock_pin_base`** – Mapped to `PICO_AUDIO_I2S_CLOCK_PIN_BASE` (GPIO 20 by default), this defines the base GPIO for the I2S clocks. The SDK uses `clock_pin_base` for the **LRCK** (word-select) line and `clock_pin_base+1` for the **BCLK** (bit clock) line.

### DMA Channel Allocation

The player reserves two DMA channels for the PIO I2S interface:

- **`dma_channel0`** – Channel 0 moves samples from the producer buffer to the PIO TX FIFO.
- **`dma_channel1`** – Channel 1 supports optional "ping-pong" buffer handling for seamless audio streaming, though the player primarily uses channel 0 for simple playback.

### State Machine Selection

The configuration explicitly selects **PIO state machine 0** (`pio_sm = 0`) to run the I2S transmitter program. This leaves state machines 1–3 available for other peripherals, maximizing the RP2040's parallel processing capabilities.

## Clock Configuration for PIO I2S Timing

I2S audio requires precise timing, which the player achieves by configuring the RP2040's USB PLL to generate a **96 MHz** clock for the PIO domain. This is implemented in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp):

```cpp
/* src/power_manage.cpp – lines 31-44 */
void pw_set_pll_usb_96MHz()
{
    pll_init(pll_usb, 1, 1536 * MHZ, 4, 4);   // 1536 MHz / 16 = 96 MHz
    clock_configure(clk_usb, 0,
        CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
        96 * MHZ, 48 * MHZ);
    // … propagate 96 MHz to clk_sys and clk_peri …
}

```

The [`main.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/main.cpp) entry point invokes this routine before any audio initialization to ensure the PIO state machine receives the correct clock frequency:

```cpp
/* src/main.cpp – line 44 */
pw_set_pll_usb_96MHz();   // Set PLL_USB 96 MHz and use it for PIO clock for I2S

```

## Initialization Sequence

The complete PIO I2S initialization follows this sequence, orchestrated through [`lib/PlayAudio/i2s_audio_init.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/i2s_audio_init.cpp):

1. **Clock preparation** – `pw_set_pll_usb_96MHz()` establishes the 96 MHz PIO clock domain.
2. **Buffer pool creation** – `audio_new_producer_pool()` allocates a ring of audio buffers sized by `PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH` (defined in [`lib/PlayAudio/i2s_audio_init.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/i2s_audio_init.h) at line 11).
3. **PIO program loading** – `audio_i2s_setup()` programs state machine 0 with the SDK's I2S transmitter, configures DMA channels 0 and 1, and binds the GPIO pins defined in `i2s_config`.
4. **Connector establishment** – `audio_i2s_connect()` wires the producer buffer to the PIO→DMA pipeline.
5. **Enable** – `audio_i2s_set_enabled(true)` starts state machine 0, beginning audio sample transmission.

## Custom Configuration Examples

### Re-using the Default PIO Configuration

To leverage the existing PIO setup in a custom module without redefining parameters:

```cpp
#include "i2s_audio_init.h"

void my_module_init(uint32_t sample_rate, audio_buffer_pool_t *&pool)
{
    // Re-use the same PIO configuration (SM 0, DMA 0/1, GPIO pins)
    i2s_setup(sample_rate, pool);
}

```

### Overriding Default Pins for Custom Hardware

For boards with different GPIO routing, instantiate a custom `audio_i2s_config_t`:

```cpp
#include "pico/audio_i2s.h"
#include "i2s_audio_init.h"

static audio_i2s_config_t my_i2s_cfg = {
    .data_pin        = 2,   // GPIO 2 as SD
    .clock_pin_base  = 3,   // GPIO 3 = LRCK, GPIO 4 = BCLK
    .dma_channel0    = 2,
    .dma_channel1    = 3,
    .pio_sm          = 1    // use a different state machine
};

void my_i2s_init(uint32_t sr, audio_buffer_pool_t *&pool)
{
    audio_format_t fmt = {
        .sample_freq   = sr,
        .pcm_format    = AUDIO_PCM_FORMAT_S32,
        .channel_count = AUDIO_CHANNEL_STEREO
    };
    audio_i2s_setup(&fmt, &fmt, &my_i2s_cfg);
    // … follow the same steps as i2s_audio_init …
}

```

## Summary

- The rpi_pico_wav_player uses **PIO state machine 0** dedicated to I2S audio transmission, leaving SM 1–3 available for other tasks.
- **DMA channels 0 and 1** handle sample transfer from memory to the PIO TX FIFO, with channel 1 supporting optional ping-pong buffering.
- **GPIO pins 18–20** are configured by default for I2S data (SD), word-select (LRCK), and bit clock (BCLK) respectively.
- A **96 MHz USB PLL clock** is required for accurate I2S timing, initialized via `pw_set_pll_usb_96MHz()` in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) before audio setup.
- All PIO parameters are encapsulated in `audio_i2s_config_t` defined in [`lib/PlayAudio/i2s_audio_init.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/i2s_audio_init.cpp), making the configuration portable and customizable.

## Frequently Asked Questions

### Which PIO state machine does the rpi_pico_wav_player use for I2S audio?

The player exclusively uses **PIO state machine 0 (SM 0)** for I2S audio output. This is explicitly set in the `i2s_config` structure in [`lib/PlayAudio/i2s_audio_init.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/i2s_audio_init.cpp) at line 31. Using SM 0 leaves state machines 1 through 3 free for additional peripherals, maximizing the RP2040's parallel processing capabilities.

### What clock frequency is required for the PIO I2S configuration?

The PIO I2S logic requires a **96 MHz** clock source to generate accurate audio bit clocks and word-select signals. The player achieves this by reconfiguring the USB PLL to 96 MHz in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) (lines 31-44) via the `pw_set_pll_usb_96MHz()` function, which must be called before initializing the audio subsystem.

### Can I change the GPIO pins used for I2S output?

Yes, the GPIO pins are fully configurable by modifying the `audio_i2s_config_t` structure before calling `audio_i2s_setup()`. The default configuration uses GPIO 18 for data (SD) and GPIO 20 as the clock base (LRCK on 20, BCLK on 21), but you can override `data_pin` and `clock_pin_base` to use any valid GPIO pins for your specific board layout.

### How many DMA channels are allocated for I2S audio playback?

The player allocates **two DMA channels**: channel 0 for primary sample transfer from the producer buffer to the PIO TX FIFO, and channel 1 for optional ping-pong buffer management. These are defined in [`lib/PlayAudio/i2s_audio_init.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/i2s_audio_init.cpp) at lines 28-29, where `dma_channel0` is set to `0` and `dma_channel1` is set to `1`.