STM32F4 I2S Audio Interface: Protocol Timing and Master Clock Generation

The Inter-IC Sound (I2S) bus defines a serial link standard for streaming digital audio between integrated circuits. By routing clock and data on independent conductors, the protocol prevents timing-skew distortion, removing the need for dedicated jitter-compensation hardware in many designs.

A minimal half-duplex I2S link uses three signals. The STM32F42x extended interface adds a fourth line for simultaneous bidirectional streaming:

  • SD — Serial Data. In half-duplex mode this pin carries time-multiplexed channel data for either transmit or receive. In full-duplex mode it is restricted to transmistion, while a companion pin handles reception.
  • WS / LRC — Word Select or Left-Right Clock. This frame-sync signal indicates the active audio channel; its frequency equals the sample rate Fs.
  • CK / BCLK — Serial or Bit Clock. Each bit of the audio word is shifted on one CK period. For stereo PCM, CK = 2 × Fs × sample_bit_depth.
  • ext_SD — Extended Serial Data. Used only in full-duplex configurations to receive data while SD transmits.
  • MCK — Master Clock. An optional 256× or 384× Fs output that synchronizes external DACs or codecs when the microcontroller serves as the clock master.

Philips Standard Timing

In the I2S Philips frame format, the Word Select line transitions one clock cycle before the Most Significant Bit (MSB) of the next channel sample. WS changes on the falling edge of CK. The transmitter updates SD on CK falling edges, and the receiver captures data on CK rising edges.

A key trait is rigid MSB alignment: the first data bit (MSB) always appears on the second CK pulse after a WS edge, regardless of actual word length. This lets receivers discard trailing least-significant bits or append zeros when word sizes differ, enabling seamless interconnection of 16-bit, 24-bit, and 32-bit devices.

DMA Integration

Sustained audio throughput is handled through DMA. The STM32F4 routes I2S FIFO traffic over DMA1 or DMA2 in memory-to-peripheral or peripheral-to-memory direction, saturating the FIFO without CPU intervention during continuous playback or recording.

Clock Architecture and Frequency Planning

All I2S-derived frequencies—MCLK, BCLK, and WS—originate from a dedicated audio PLL. Assuming an 8 MHz HSE crystal and a main PLL input divider (PLLM) of 8, the PLLI2S block generates the I2S kernel clock I2SxCLK:

I2SxCLK = (HSE / PLLM) × PLLI2SN / PLLI2SR

The resulting I2SxCLK is fed into a second divider stage inside the I2S peripheral. The SPI_I2SPR register governs this stage through three fields:

  • I2SDIV (bits 7:0) — linear divider, range 2–255
  • ODD (bit 8) — odd prescaler toggle (0 or 1)
  • MCKOE (bit 9) — master-clock output enable

When MCLK is enabled (MCKOE = 1), the sampling frequency is:

Fs = I2SxCLK / [(channel_bits × 2) × ((2 × I2SDIV) + ODD) × MCLK_divider]

where the MCLK divider is 8 for 16-bit frames and 4 for 32-bit frames. With MCLK disabled, the factor collapses to:

Fs = I2SxCLK / [(channel_bits × 2) × ((2 × I2SDIV) + ODD)]

For the common case of 16-bit stereo frames with master clock output enabled, the equation simplifies to:

Fs ≈ (1000 × PLLI2SN / PLLI2SR) / [256 × (2 × I2SDIV + ODD)]

Because the discrete divider steps cannot hit every standard rate exactly from an 8 MHz reference, small deviations exist. For example, targeting 44.1 kHz with PLLI2SN = 271, PLLI2SR = 2, I2SDIV = 6, and ODD = 0 produces 44.108 kHz (0.018 % error). Ahcieving zero-error standard rates often requires an external clock source instead of a fixed crystal.

Pre-Calculated Divider Tables

Rather than computing coefficients at runtime, a lookup table maps common audio rates to their PLL and prescaler values. The following array assumes HSE = 8 MHz and PLLM = 8, giving a 1 MHz VCO input.

typedef struct {
    uint16_t freq_index; /* Target Fs in 0.1 kHz (e.g., 4410 = 44.1 kHz) */
    uint16_t pll_n;      /* PLLI2S multiplier (192–432) */
    uint16_t pll_r;      /* PLLI2S divider (2–7) */
    uint16_t div;        /* I2SDIV linear prescaler (2–255) */
    uint8_t  odd;        /* Odd prescaler bit (0 or 1) */
} I2S_AudioClock;

/*
 * 16-bit stereo, MCLK enabled, HSE = 8 MHz, PLLM = 8.
 * Fs = (1000 * pll_n / pll_r) / (256 * (2*div + odd))
 */
static const I2S_AudioClock i2s_master_clocks[] = {
    { 800,  256, 5, 12, 1},  /* 8.0 kHz    */
    {1102,  429, 4, 19, 0},  /* 11.025 kHz */
    {1600,  213, 2, 13, 0},  /* 16.0 kHz   */
    {2205,  429, 4,  9, 1},  /* 22.05 kHz  */
    {3200,  213, 2,  6, 1},  /* 32.0 kHz   */
    {4410,  271, 2,  6, 0},  /* 44.1 kHz   */
    {4800,  258, 3,  3, 1},  /* 48.0 kHz   */
    {8820,  316, 2,  3, 1},  /* 88.2 kHz   */
    {9600,  344, 2,  3, 1},  /* 96.0 kHz   */
    {17640, 361, 2,  2, 0},  /* 176.4 kHz  */
    {19200, 393, 2,  2, 0},  /* 192.0 kHz  */
};

Firmware initializes the PLLI2S via RCC_PLLI2SCofig, then programs SPI_I2SPR with the corresponding div, odd, and MCKOE values before enabling the I2S peripheral.

Tags: STM32 I2S audio Clock Configuration Embedded Systems

Posted on Fri, 12 Jun 2026 17:50:30 +0000 by shaneH