Inter-IC Sound (I²S) Usage Scenarios#
The WiSeConnect software development kit (SDK) for SiWx917 devices provides flexible APIs for Inter-IC Sound (I²S) audio streaming. You can:
Use blocking APIs for simple, short transfers
Use non-blocking direct memory access (DMA) for continuous streaming
Run ultra-low-power (ULP) I²S in low-power states to extend battery life
This section presents common usage patterns with concise workflows and example code.
Typical Use Case 1: Blocking (Non-DMA)#
Use blocking mode when:
Transfers are short, such as tones, control frames, or small buffers
The CPU can wait for completion
Latency and throughput requirements are moderate
Workflow#
Initialize the I²S instance.
Configure sample rate, bit depth, and channels.
(Optional) Configure FIFO thresholds.
Call blocking transmit or receive functions.
Check return codes and validate data.
Deinitialize the I²S instance when finished.
I²S Primary (Controller) — Blocking Example#
Step 1: Configure and initialize#
#include "sl_si91x_i2s.h" #include "sl_i2s_instances.h" #include "rsi_debug.h" #define I2S_INSTANCE_USED SL_I2S_ULP #define I2S_BUFFER_SIZE 1024 static uint32_t write_buf[I2S_BUFFER_SIZE]; static uint32_t read_buf[I2S_BUFFER_SIZE]; static sl_i2s_config_t i2s_cfg; // Select ULP or HP config generated by UC i2s_cfg = sl_i2s_ulp_config; sl_status_t st = sl_si91x_i2s_init(I2S_INSTANCE_USED, &i2s_cfg); if (st != SL_STATUS_OK) { DEBUGOUT("I²S init failed: %u\n", st); }
Step 2: Audio Transfer (Blocking)#
// Fill DOUT pattern (left|right packed) for (uint32_t i = 0; i < I2S_BUFFER_SIZE; i++) { write_buf[i] = (i + 1u) << 16 | (i + 1u); } st = sl_si91x_i2s_transmit_data_blocking(I2S_INSTANCE_USED, write_buf, I2S_BUFFER_SIZE); if (st != SL_STATUS_OK) { DEBUGOUT("I²S blocking transmit failed: %u\n", st); } st = sl_si91x_i2s_receive_data_blocking(I2S_INSTANCE_USED, read_buf, I2S_BUFFER_SIZE); if (st != SL_STATUS_OK) { DEBUGOUT("I²S blocking receive failed: %u\n", st); }
Step 3: Validate and Clean Up#
bool match = true; for (uint32_t i = 0; i < I2S_BUFFER_SIZE; i++) { if (write_buf[i] != read_buf[i]) { match = false; break; } } DEBUGOUT("Buffer compare: %s\n", match ? "PASS" : "FAIL"); sl_si91x_i2s_deinit(I2S_INSTANCE_USED);
Tips:
Keep buffers small in blocking mode to avoid audible glitches.
Verify DIN/DOUT activity and BCLK/WS alignment with a logic analyzer.
I²S Secondary (Target) — Blocking Sketch#
sl_i2s_config_t sec_cfg = sl_i2s_ulp_config; sec_cfg.mode = SL_I2S_SLAVE; sl_status_t st = sl_si91x_i2s_init(I2S_INSTANCE_USED, &sec_cfg); st = sl_si91x_i2s_receive_data_blocking(I2S_INSTANCE_USED, read_buf, I2S_BUFFER_SIZE); st = sl_si91x_i2s_transmit_data_blocking(I2S_INSTANCE_USED, write_buf, I2S_BUFFER_SIZE); sl_si91x_i2s_deinit(I2S_INSTANCE_USED);
Typical Use Case 2: Non-Blocking (DMA)#
Use non-blocking DMA when:
Continuous, high-quality audio streaming is required
The CPU must perform other tasks concurrently
Latency and robustness are important
Workflow#
Configure DMA channels (TX and RX).
Register a callback for transfer completion and error handling.
Use non-blocking I²S transmit and receive APIs.
Handle events in the callback, such as:
Buffer completion
Underrun or overrun conditions
Error status notifications
Example — UDMA-Based I²S Transfer#
#include "sl_si91x_i2s.h" #include "sl_i2s_instances.h" #include "rsi_debug.h" #define I2S_INSTANCE_USED SL_I2S_ULP #define I2S_BUFFER_SIZE 1024 static uint32_t dma_tx_buf[I2S_BUFFER_SIZE]; static uint32_t dma_rx_buf[I2S_BUFFER_SIZE]; static sl_i2s_dma_config_t dma_cfg; static sl_i2s_config_t i2s_cfg; static void i2s_cb(sl_i2s_callback_t event, void *arg) { switch (event) { case SL_I2S_SEND_COMPLETE: DEBUGOUT("DMA transmit complete\n"); break; case SL_I2S_RECEIVE_COMPLETE: DEBUGOUT("DMA receive complete\n"); break; case SL_I2S_TX_UNDERFLOW: DEBUGOUT("I²S DOUT underrun\n"); break; case SL_I2S_RX_OVERFLOW: DEBUGOUT("I²S DIN overflow\n"); break; case SL_I2S_FRAME_ERROR: DEBUGOUT("I²S frame error\n"); break; default: break; } } // DMA channels dma_cfg.dma_tx_channel = SL_ULP_I2S_DMA_TX_CHANNEL; dma_cfg.dma_rx_channel = SL_ULP_I2S_DMA_RX_CHANNEL; i2s_cfg = sl_i2s_ulp_config; i2s_cfg.callback_event = i2s_cb; sl_status_t st = sl_si91x_i2s_init(I2S_INSTANCE_USED, &i2s_cfg); // Prepare DOUT buffer for (uint32_t i = 0; i < I2S_BUFFER_SIZE; i++) { dma_tx_buf[i] = (i + 1u) << 16 | (i + 1u); } // Start non-blocking transfers st = sl_si91x_i2s_transmit_data_non_blocking(I2S_INSTANCE_USED, dma_tx_buf, I2S_BUFFER_SIZE, &dma_cfg); st = sl_si91x_i2s_receive_data_non_blocking(I2S_INSTANCE_USED, dma_rx_buf, I2S_BUFFER_SIZE, &dma_cfg); // ... app continues work; callback signals completion ...
Tips
Size buffers to avoid underrun/overrun.
Adjust DMA and FIFO thresholds.
Prioritize I²S/DMA interrupts appropriately in your RTOS.
Secondary (Target) — Non-Blocking Sketch
sl_i2s_config_t sec_cfg = sl_i2s_ulp_config; sec_cfg.mode = SL_I2S_SLAVE; sec_cfg.callback_event = i2s_cb; sl_status_t st = sl_si91x_i2s_init(I2S_INSTANCE_USED, &sec_cfg); st = sl_si91x_i2s_receive_data_non_blocking(I2S_INSTANCE_USED, dma_rx_buf, I2S_BUFFER_SIZE, &dma_cfg); st = sl_si91x_i2s_transmit_data_non_blocking(I2S_INSTANCE_USED, dma_tx_buf, I2S_BUFFER_SIZE, &dma_cfg);
Typical Use Case 3: Ultra-Low-Power (ULP) I²S Operation#
Use ULP I²S for always-on or battery-powered audio applications where the system spends significant time in the PS2 low-power state.
Workflow#
Configure the power manager.
Enable PS2 low-power mode.
Ensure required RAM retention settings are active.
Enable ULP I²S and ULP GPIO pins in your project configuration.
Initialize I²S and verify audio streaming while in an active state (PS4 or PS3).
Transition to PS2:
Keep ULP I²S running during the low-power state.
Ensure peripheral clocks and GPIO routing match ULP requirements.
Handle wake-up operations.
Reinitialize any necessary modules.
Resume audio transfers seamlessly based on application needs.
ULP sketch
// Prepare for PS2 sl_si91x_i2s_deinit(I2S_INSTANCE_USED); sl_si91x_power_manager_add_ps_requirement(SL_SI91X_POWER_MANAGER_PS2); // ... enter PS2 ... // After wake sl_si91x_i2s_init(I2S_INSTANCE_USED, &i2s_cfg); sl_si91x_i2s_configure_power_mode(I2S_INSTANCE_USED, SL_I2S_LOW_POWER_MODE); // Resume streaming on DIN/DOUT
Tips
Place DMA buffers in ULP memory for retention.
Use ULP GPIO for I²S pins to keep BCLK, WS, DIN, and DOUT alive in PS2.
Re-register callbacks and restore thresholds after wake.
Typical Application Areas#
Battery-powered audio end nodes
Always-on voice or key-phrase detection
Wearables and IoT audio sensors with duty cycling
Long-life, low-maintenance deployments
Related Example Projects
SL Si91x – I²S Primary
SL Si91x – I²S Secondary
SL Si91x – I²S Loopback
SL Si91x – ULP I²S Primary
See WiSeConnect SDK I²S Peripheral Examples:
https://docs.silabs.com/wiseconnect/latest/wiseconnect-examples/#peripheral-examples