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/DOUTTips
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