I2S (Inter-IC Sound)

group group_hal_i2s

High level interface for interacting with the Inter-IC Sound (I2S).

The I2S protocol is a asynchronous serial interface protocol. This driver supports both transmit and receive modes of operation. The communication frequency, sample rate, word size, and channel size can all be configured.

Features

  • Transmit and receive functionality

  • Configurable data rates

  • Configurable channel and word size

  • Configurable interrupt and callback assignment from I2S events - cyhal_i2s_event_t

Quick Start

Initialize an I2S instance using the cyhal_i2s_init and provide the transmit (tx) and/or receive (rx) pins. Call cyhal_i2s_start_tx and/or cyhal_i2s_start_rx to enable transmit and/or receive functionality as desired.

See

Snippet 1: I2S Initialization and Configuration for example initialization as transmit or receive.

note

The clock parameter (const cyhal_clock_t *clk) is optional and can be set to NULL to generate and use an available clock resource with a default frequency.

The sclk frequency is determined as sclk = sample_rate_hz * channel_length * 2 (multiplying by 2 for 2 channels - left and right). The input clock must be a multiple of this sclk frequency; see the implementation specific documentation for the supported multipliers.

It is possible to use either only TX functionality, only RX functionality, or both RX and TX functionality at the same time. If RX and TX are both in use, the same sample rate, channel length, word length, sclk frequency will be used for both.

Code Snippets

Snippet 1: I2S Initialization and Configuration

This snippet initializes an I2S resource for transmit or receive and assigns the pins.

Initializing as I2S transmitter

    cyhal_i2s_t        i2s;
    cyhal_i2s_pins_t   tx_pins = { .sck = P5_1, .ws = P5_2, .data = P5_3 };
    cyhal_i2s_config_t config  =
    {
        .is_tx_slave    = false,
        .is_rx_slave    = false,
        .mclk_hz        = 0,
        .channel_length = 32,
        .word_length    = 32,
        .sample_rate_hz = 44000
    };
    cy_rslt_t result = cyhal_i2s_init(&i2s, &tx_pins, NULL, NC, &config, NULL);
    if (CY_RSLT_SUCCESS != result)
    {
        // Handle error
    }
Initializing as I2S receiver
    cyhal_i2s_t        i2s;
    cyhal_i2s_pins_t   rx_pins = { .sck = P5_4, .ws = P5_5, .data = P5_6 };
    cyhal_i2s_config_t config  =
    {
        .is_tx_slave    = false,
        .is_rx_slave    = true,
        .mclk_hz        = 0,
        .channel_length = 32,
        .word_length    = 32,
        .sample_rate_hz = 44000
    };
    cy_rslt_t result = cyhal_i2s_init(&i2s, NULL, &rx_pins, NC, &config, NULL);
    if (CY_RSLT_SUCCESS != result)
    {
        // Handle error
    }

Snippet 2: I2S Transmit One-shot

This snippet shows how to transmit data using cyhal_i2s_write_async when the entire sample is available at once.

static void i2s_event_handler_transmit_one_shot(void* arg, cyhal_i2s_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the i2s object
    cyhal_i2s_t* i2s = (cyhal_i2s_t*)arg;
    if (0u != (event & CYHAL_I2S_TX_EMPTY))
    {
        cyhal_i2s_stop_tx(i2s);
    }
}


// Data to transmit, defined e.g. an array stored in flash
extern const uint32_t* i2s_tx_buffer;
extern const size_t    i2s_tx_buffer_len;
cy_rslt_t snippet_cyhal_i2s_async_transmit_one_shot(void)
{
    cyhal_i2s_t i2s;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the I2S object, so that
    // we can easily reference it from the callback handler.
    cyhal_i2s_register_callback(&i2s, &i2s_event_handler_transmit_one_shot, &i2s);

    // Subscribe to the TX Empty event so that we can stop the interface when the transfer is
    // complete
    cyhal_i2s_enable_event(&i2s, CYHAL_I2S_TX_EMPTY, CYHAL_ISR_PRIORITY_DEFAULT, true);

    cy_rslt_t result = cyhal_i2s_write_async(&i2s, i2s_tx_buffer, i2s_tx_buffer_len);

    if (CY_RSLT_SUCCESS == result)
    {
        result = cyhal_i2s_start_tx(&i2s);
    }

    return result;
}

Snippet 3: I2S Transmit Streaming

This snippet shows how to transmit data using cyhal_i2s_write_async when sample data is being continuously loaded and transmitted (e.g. streaming over the network).

// We use a dual buffer system so that one buffer can be transmitting while the other is being
// filled
#define BUFFER_SIZE 128u
const uint32_t  tx_buffer0[BUFFER_SIZE];
const uint32_t  tx_buffer1[BUFFER_SIZE];
const uint32_t* active_tx_buffer;
const uint32_t* next_tx_buffer;
static void i2s_event_handler_transmit_streaming(void* arg, cyhal_i2s_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the i2s object
    cyhal_i2s_t* i2s = (cyhal_i2s_t*)arg;
    if (0u != (event & CYHAL_I2S_ASYNC_TX_COMPLETE))
    {
        // Flip the active and the next tx buffers
        const uint32_t* temp = active_tx_buffer;
        active_tx_buffer = next_tx_buffer;
        next_tx_buffer   = temp;

        // Start writing the next buffer while the just-freed one is repopulated
        cy_rslt_t result = cyhal_i2s_write_async(i2s, active_tx_buffer, BUFFER_SIZE);

        if (CY_RSLT_SUCCESS == result)
        {
            // Load the next set of data into next_tx_buffer
        }
    }
}


cy_rslt_t snippet_cyhal_i2s_async_transmit_streaming(void)
{
    cyhal_i2s_t i2s;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the I2S object, so that
    // we can easily reference it from the callback handler.
    cyhal_i2s_register_callback(&i2s, &i2s_event_handler_transmit_streaming, &i2s);

    // Subscribe to the async complete event so that we can queue up another transfer when this one
    // completes
    cyhal_i2s_enable_event(&i2s, CYHAL_I2S_ASYNC_TX_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT, true);

    // Configure asynchronous transfers to use DMA to free up the CPU during transfers
    cy_rslt_t result = cyhal_i2s_set_async_mode(&i2s, CYHAL_ASYNC_DMA, CYHAL_DMA_PRIORITY_DEFAULT);

    if (CY_RSLT_SUCCESS == result)
    {
        // Populate initial data in the two tx buffers (e.g. by streaming over the network)

        active_tx_buffer = tx_buffer0;
        next_tx_buffer   = tx_buffer1;

        result = cyhal_i2s_write_async(&i2s, active_tx_buffer, BUFFER_SIZE);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = cyhal_i2s_start_tx(&i2s);
    }

    return result;
}

Snippet 4: I2S Receive

This snippet shows how to receive data using cyhal_i2s_read_async.

// We use a dual buffer system so that one buffer can be filling while the other is being processed
#define BUFFER_SIZE 128u
uint32_t  rx_buffer0[BUFFER_SIZE];
uint32_t  rx_buffer1[BUFFER_SIZE];
uint32_t* active_rx_buffer;
uint32_t* full_rx_buffer;
static void i2s_event_handler_receive(void* arg, cyhal_i2s_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the i2s object
    cyhal_i2s_t* i2s = (cyhal_i2s_t*)arg;
    if (0u != (event & CYHAL_I2S_ASYNC_RX_COMPLETE))
    {
        // Flip the active and the next rx buffers
        uint32_t* temp = active_rx_buffer;
        active_rx_buffer = full_rx_buffer;
        full_rx_buffer   = temp;

        // Start reading into the next buffer while the just-filled one is being processed
        cy_rslt_t result = cyhal_i2s_read_async(i2s, active_rx_buffer, BUFFER_SIZE);

        if (CY_RSLT_SUCCESS == result)
        {
            // Process the data in the full_rx_buffer
        }
    }
}


cy_rslt_t snippet_cyhal_i2s_async_receive(void)
{
    cyhal_i2s_t i2s;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the I2S object, so that
    // we can easily reference it from the callback handler.
    cyhal_i2s_register_callback(&i2s, &i2s_event_handler_receive, &i2s);

    // Subscribe to the async complete event so that we can queue up another transfer when this one
    // completes
    cyhal_i2s_enable_event(&i2s, CYHAL_I2S_ASYNC_RX_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT, true);

    // Configure asynchronous transfers to use DMA to free up the CPU during transfers
    cy_rslt_t result = cyhal_i2s_set_async_mode(&i2s, CYHAL_ASYNC_DMA, CYHAL_DMA_PRIORITY_DEFAULT);

    if (CY_RSLT_SUCCESS == result)
    {
        active_rx_buffer = rx_buffer0;
        full_rx_buffer   = rx_buffer1;
        result           = cyhal_i2s_read_async(&i2s, active_rx_buffer, BUFFER_SIZE);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = cyhal_i2s_start_rx(&i2s);
    }

    return result;
}

More Information

Code examples (Github)

Typedefs

typedef void (*cyhal_i2s_event_callback_t)(void *callback_arg, cyhal_i2s_event_t event)

Handler for I2S event callbacks.

Enums

enum cyhal_i2s_event_t

cyhal_i2s_event_t: I2S events.

Values:

enumerator CYHAL_I2S_TX_NOT_FULL

TX HW Buffer is not full.

enumerator CYHAL_I2S_TX_HALF_EMPTY

TX HW Buffer is half empty.

enumerator CYHAL_I2S_TX_EMPTY

TX HW Buffer is Empty.

enumerator CYHAL_I2S_TX_OVERFLOW

Attempt to write when TX HW Buffer is full.

enumerator CYHAL_I2S_TX_UNDERFLOW

Interface ready to transfer data but HW TX buffer is empty.

enumerator CYHAL_I2S_ASYNC_TX_COMPLETE

Pending async transmit is complete (but the HW buffer may still contain unsent data)

enumerator CYHAL_I2S_RX_NOT_EMPTY

RX HW Buffer is not Empty.

enumerator CYHAL_I2S_RX_HALF_FULL

RX HW Buffer is half full.

enumerator CYHAL_I2S_RX_FULL

RX HW Buffer is FULL.

enumerator CYHAL_I2S_RX_OVERFLOW

Attempt to write when RX HW Buffer is full.

enumerator CYHAL_I2S_RX_UNDERFLOW

Attempt to read when HW RX buffer is empty.

enumerator CYHAL_I2S_ASYNC_RX_COMPLETE

Pending async receive is complete.

Functions

cy_rslt_t cyhal_i2s_init(cyhal_i2s_t *obj, const cyhal_i2s_pins_t *tx_pins, const cyhal_i2s_pins_t *rx_pins, cyhal_gpio_t mclk, const cyhal_i2s_config_t *config, cyhal_clock_t *clk)

Initialize the I2S peripheral.

It sets the default parameters for I2S peripheral, and configures its specifieds pins. If only one direction is to be used, then the pins for the other direction need not be specified (i.e. they may be set to NC). For example, if only RX is needed, tx_sck, tx_ws, and tx_sdo may all be set to NC. If one pin is specified for a direction, all pins for that direction must be specified.

Return

The status of the init request

Parameters
  • [out] obj: Pointer to an I2S object. The caller must allocate the memory for this object but the init function will initialize its contents.

  • [in] tx_pins: Pins for I2S transmit. If NULL, transmit functionality will be disabled.

  • [in] rx_pins: Pins for I2S receive. If NULL, receive functionality will be disabled.

  • [in] mclk: The master clock input pin, if an external clock should be used for the I2S block. Set to NC if an internal clock source should be used.

  • [in] config: Initial block configuration

  • [in] clk: Clock source to use for this instance. If NULL, a dedicated clock divider will be allocated for this instance.

void cyhal_i2s_free(cyhal_i2s_t *obj)

Deinitialize the i2s object.

Parameters
  • [inout] obj: The i2s object

cy_rslt_t cyhal_i2s_set_sample_rate(cyhal_i2s_t *obj, uint32_t sample_rate_hz)

Set the I2S sample rate.

Return

The status of the set sample rate request

Parameters
  • [in] obj: The I2S object

  • [in] sample_rate_hz: Sample rate in Hz

cy_rslt_t cyhal_i2s_start_tx(cyhal_i2s_t *obj)

Starts transmitting data.

Transmission will continue until it is stopped by calling cyhal_i2s_stop_tx.

Return

The status of the start request.

Parameters
  • [in] obj: The I2S object

cy_rslt_t cyhal_i2s_stop_tx(cyhal_i2s_t *obj)

Stops transmitting data.

This immediately terminates transmission.

Return

The status of the stop request.

Parameters
  • [in] obj: The I2S object

cy_rslt_t cyhal_i2s_clear_tx(cyhal_i2s_t *obj)

Clears the tx hardware buffer.

Return

The status of the clear request

Parameters
  • [in] obj: The i2s peripheral

cy_rslt_t cyhal_i2s_start_rx(cyhal_i2s_t *obj)

Starts receiving data.

Data will continue to be received until it is stopped by calling cyhal_i2s_stop_rx.

Return

The status of the start request.

Parameters
  • [in] obj: The I2S object

cy_rslt_t cyhal_i2s_stop_rx(cyhal_i2s_t *obj)

Stops receiving data.

This immediately terminates data receipt.

Return

The status of the stop request.

Parameters
  • [in] obj: The I2S object

cy_rslt_t cyhal_i2s_clear_rx(cyhal_i2s_t *obj)

Clears the rx hardware buffer.

Return

The status of the clear request

Parameters
  • [in] obj: The i2s peripheral

cy_rslt_t cyhal_i2s_read(cyhal_i2s_t *obj, void *data, size_t *length)

Read data synchronously.

This will read the number of words specified by the length parameter, or the number of words that are currently available in the receive buffer, whichever is less, then return. The value pointed to by length will be updated to reflect the number of words that were actually read.

note

Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits, each word will consume two bytes. But if the word length is 20, each word will consume 32 bytes.

Return

The status of the read request

Return

The status of the read request

Parameters
  • [in] obj: The I2S object

  • [out] data: The buffer for receiving

  • [inout] length: Number of words to (as configured in cyhal_i2s_config_t.word_length) read, updated with the number actually read

cy_rslt_t cyhal_i2s_write(cyhal_i2s_t *obj, const void *data, size_t *length)

Send data synchronously.

This will write either length words or until the write buffer is full, whichever is less, then return. The value pointed to by length will be updated to reflect the number of words that were actually written.

note

This function only queues data into the write buffer; it does not block until the data has all been sent out over the wire.

note

Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits, each word will consume two bytes. But if the word length is 20, each word will consume 32 bytes.

Return

The status of the write request

Parameters
  • [in] obj: The I2S object

  • [in] data: The buffer for sending

  • [inout] length: Number of words to write (as configured in cyhal_i2s_config_t.word_length, updated with the number actually written

bool cyhal_i2s_is_tx_enabled(cyhal_i2s_t *obj)

Checks if the transmit functionality is enabled for the specified I2S peripheral (regardless of whether data is currently queued for transmission).

The transmit functionality can be enabled by calling cyhal_i2s_start_tx and disabled by calling cyhal_i2s_stop_tx

Return

Whether the I2S transmit function is enabled.

Parameters
  • [in] obj: The I2S peripheral to check

bool cyhal_i2s_is_tx_busy(cyhal_i2s_t *obj)

Checks if the specified I2S peripheral is transmitting data, including if a pending async transfer is waiting to write more data to the transmit buffer.

Return

Whether the I2S is still transmitting

Parameters
  • [in] obj: The I2S peripheral to check

bool cyhal_i2s_is_rx_enabled(cyhal_i2s_t *obj)

Checks if the receive functionality is enabled for the specified I2S peripheral (regardless of whether any unread data has been received).

The receive functionality can be enabled by calling cyhal_i2s_start_rx and disabled by calling cyhal_i2s_stop_rx

Return

Whether the I2S receive function is enabled.

Parameters
  • [in] obj: The I2S peripheral to check

bool cyhal_i2s_is_rx_busy(cyhal_i2s_t *obj)

Checks if the specified I2S peripheral has received data that has not yet been read out of the hardware buffer.

This includes if an async read transfer is pending.

Return

Whether the I2S is still transmitting

Parameters
  • [in] obj: The I2S peripheral to check

cy_rslt_t cyhal_i2s_read_async(cyhal_i2s_t *obj, void *rx, size_t rx_length)

Start I2S asynchronous read.

This will transfer rx_length words into the buffer pointed to by rx in the background. When the requested quantity of data has been read, the CYHAL_I2S_ASYNC_RX_COMPLETE event will be raised. See cyhal_i2s_register_callback and cyhal_i2s_enable_event.

cyhal_i2s_set_async_mode can be used to control whether this uses DMA or a CPU-driven transfer.

note

Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits, each word will consume two bytes. But if the word length is 20, each word will consume 32 bytes.

Return

The status of the read_async request

Parameters

cy_rslt_t cyhal_i2s_write_async(cyhal_i2s_t *obj, const void *tx, size_t tx_length)

Start I2S asynchronous write.

This will transfer tx_length words into the tx buffer in the background. When the requested quantity of data has been queued in the transmit buffer, the CYHAL_I2S_ASYNC_TX_COMPLETE event will be raised. See cyhal_i2s_register_callback and cyhal_i2s_enable_event.

cyhal_i2s_set_async_mode can be used to control whether this uses DMA or a SW (CPU-driven) transfer.

note

Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits, each word will consume two bytes. But if the word length is 20, each word will consume 32 bytes.

Return

The status of the transfer_async request

Parameters
  • [in] obj: The I2S object

  • [in] tx: The transmit buffer.

  • [in] tx_length: The number of words to transmit.

cy_rslt_t cyhal_i2s_set_async_mode(cyhal_i2s_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)

Set the mechanism that is used to perform I2S asynchronous transfers.

The default is SW.

warning

The effect of calling this function while an async transfer is pending is undefined.

Return

The status of the set mode request

Parameters
  • [in] obj: The I2S object

  • [in] mode: The transfer mode

  • [in] dma_priority: The priority, if DMA is used. Valid values are the same as for cyhal_dma_init. If DMA is not selected, the only valid value is CYHAL_DMA_PRIORITY_DEFAULT, and no guarantees are made about prioritization.

bool cyhal_i2s_is_read_pending(cyhal_i2s_t *obj)

Checks if the specified I2S peripheral is in the process of reading data from the hardware buffer into RAM.

note

: This only checks whether there is an ongoing transfer (e.g. via cyhal_i2s_read_async) into RAM from the I2S peripheral's hardware buffer. It does not check whether unread data exists in the hardware buffer.

Return

Whether an asynchronous read operation is still in progress

Parameters
  • [in] obj: The I2S peripheral to check

bool cyhal_i2s_is_write_pending(cyhal_i2s_t *obj)

Checks if the specified I2S peripheral is in the process of writing data into the hardware buffer.

note

: This only checks whether there is an ongoing transfer (e.g. via cyhal_i2s_transfer_async) from RAM into the I2S peripheral's hardware buffer. It does not check whether unwritten data exists in the hardware buffer.

Return

Whether an asynchronous write operation is still in progress

Parameters
  • [in] obj: The I2S peripheral to check

cy_rslt_t cyhal_i2s_abort_read_async(cyhal_i2s_t *obj)

Abort I2S asynchronous read.

This function does not perform any validation before aborting the transfer. Any validation which is required is the responsibility of the application.

Return

The status of the abort_async_read request

Parameters
  • [in] obj: The I2S object

cy_rslt_t cyhal_i2s_abort_write_async(cyhal_i2s_t *obj)

Abort I2S asynchronous write.

This function does not perform any validation before aborting the transfer. Any validation which is required is the responsibility of the application.

Return

The status of the abort_async_write request

Parameters
  • [in] obj: The I2S object

void cyhal_i2s_register_callback(cyhal_i2s_t *obj, cyhal_i2s_event_callback_t callback, void *callback_arg)

Register an I2S callback handler.

This function will be called when one of the events enabled by cyhal_i2s_enable_event occurs.

Parameters
  • [in] obj: The I2S object

  • [in] callback: The callback handler which will be invoked when the interrupt fires

  • [in] callback_arg: Generic argument that will be provided to the callback when called

void cyhal_i2s_enable_event(cyhal_i2s_t *obj, cyhal_i2s_event_t event, uint8_t intr_priority, bool enable)

Configure I2S events.

When an enabled event occurs, the function specified by cyhal_i2s_register_callback will be called.

Parameters
  • [in] obj: The I2S object

  • [in] event: The I2S event type

  • [in] intr_priority: The priority for NVIC interrupt events

  • [in] enable: True to turn on specified events, False to turn off

struct cyhal_i2s_pins_t
#include <cyhal_i2s.h>

Pins to use for one I2S direction.

Public Members

cyhal_gpio_t sck

Clock pin.

cyhal_gpio_t ws

Word select.

cyhal_gpio_t data

Data pin (sdo or sdi)

struct cyhal_i2s_config_t
#include <cyhal_i2s.h>

I2S Configuration.

Public Members

bool is_tx_slave

Configure TX to operate a slave (true) or master (false)

bool is_rx_slave

Configure RX to operate a slave (true) or master (false)

uint32_t mclk_hz

Frequency, in hertz, of the master clock if it is provided by an external pin.

If the mclk pin is not NC, this must be nonzero. If the mclk pin is NC, this must be zero.

uint8_t channel_length

Number of bits in each channel.

See the implementation specific documentation for supported values.

uint8_t word_length

Number of bits in each word.

Must be less than or equal to channel_length. If word_length < channel_length, the excess bits will be padded with 0’s.

uint32_t sample_rate_hz

Sample rate in Hz.