TDM (Time Division Multiplexed)

group group_hal_tdm

High level interface for interacting with the Time Division Multiplexed controller (TDM).

The TDM 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 number of channels can all be configured.

Features

  • Transmit and receive functionality

  • Configurable data rates

  • Configurable channel count and word size

  • Configurable interrupt and callback assignment from TDM events - cyhal_tdm_event_t

Quick Start

Initialize an TDM instance using the cyhal_tdm_init and provide the transmit (tx) and/or receive (rx) pins. Call cyhal_tdm_start_tx and/or cyhal_tdm_start_rx to enable transmit and/or receive functionality as desired.

See

Snippet 1: TDM 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 * num_channels. 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, channel count, channel mask, word length, and sclk frequency will be used for both.

Code Snippets

note

Error checking is omitted for clarity

Snippet 1: TDM Initialization and Configuration

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

Initializing as TDM transmitter

    cyhal_tdm_t      tdm;
    cyhal_tdm_pins_t tx_pins = { .sck = P5_1, .ws = P5_2, .data = P5_3 };

    cyhal_tdm_config_t config =
    {
        .is_tx_slave    = false,
        .tx_ws_width    = CYHAL_TDM_WS_SINGLE,
        .is_rx_slave    = false,
        .rx_ws_width    = CYHAL_TDM_WS_SINGLE,
        .mclk_hz        = 0,
        .channel_length = 32,
        .num_channels   = 8,
        .channel_mask   = 0xFF, // All 8 channels enabled
        .word_length    = 32,
        .sample_rate_hz = 44000
    };
    cy_rslt_t result = cyhal_tdm_init(&tdm, &tx_pins, NULL, NC, &config, NULL);
    CY_ASSERT(CY_RSLT_SUCCESS == result);
Initializing as TDM receiver
    cyhal_tdm_t      tdm;
    cyhal_tdm_pins_t rx_pins = { .sck = P5_4, .ws = P5_5, .data = P5_6 };

    cyhal_tdm_config_t config =
    {
        .is_tx_slave    = false,
        .tx_ws_width    = CYHAL_TDM_WS_SINGLE,
        .is_rx_slave    = false,
        .rx_ws_width    = CYHAL_TDM_WS_SINGLE,
        .mclk_hz        = 0,
        .channel_length = 32,
        .num_channels   = 8,
        .channel_mask   = 0xFF, // All 8 channels enabled
        .word_length    = 32,
        .sample_rate_hz = 44000
    };
    cy_rslt_t result = cyhal_tdm_init(&tdm, NULL, &rx_pins, NC, &config, NULL);
    CY_ASSERT(CY_RSLT_SUCCESS == result);

Snippet 2: TDM Transmit One-shot

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

static void tdm_event_handler_transmit_one_shot(void* arg, cyhal_tdm_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the tdm object
    cyhal_tdm_t* tdm = (cyhal_tdm_t*)arg;
    if (0u != (event & CYHAL_TDM_TX_EMPTY))
    {
        cy_rslt_t result = cyhal_tdm_stop_tx(tdm);
        CY_ASSERT(CY_RSLT_SUCCESS == result);
    }
}


// Data to transmit, defined e.g. an array stored in flash
extern const uint32_t* tdm_tx_buffer;
extern const size_t    tdm_tx_buffer_len;
//--------------------------------------------------------------------------------------------------
// snippet_cyhal_tdm_async_transmit_one_shot
//--------------------------------------------------------------------------------------------------
static void snippet_cyhal_tdm_async_transmit_one_shot(void)
{
    cyhal_tdm_t tdm;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the tdm object, so that
    // we can easily reference it from the callback handler.
    cyhal_tdm_register_callback(&tdm, &tdm_event_handler_transmit_one_shot, &tdm);

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

    cy_rslt_t result = cyhal_tdm_write_async(&tdm, tdm_tx_buffer, tdm_tx_buffer_len);
    CY_ASSERT(CY_RSLT_SUCCESS == result);

    result = cyhal_tdm_start_tx(&tdm);
}

Snippet 3: TDM Transmit Streaming

This snippet shows how to transmit data using cyhal_tdm_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
static const uint32_t  tx_buffer0[BUFFER_SIZE];
static const uint32_t  tx_buffer1[BUFFER_SIZE];
static const uint32_t* active_tx_buffer;
static const uint32_t* next_tx_buffer;
//--------------------------------------------------------------------------------------------------
// tdm_event_handler_transmit_streaming
//--------------------------------------------------------------------------------------------------
static void tdm_event_handler_transmit_streaming(void* arg, cyhal_tdm_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the tdm object
    cyhal_tdm_t* tdm = (cyhal_tdm_t*)arg;
    if (0u != (event & CYHAL_TDM_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_tdm_write_async(tdm, active_tx_buffer, BUFFER_SIZE);
        CY_ASSERT(CY_RSLT_SUCCESS == result);

        // Load the next set of data into next_tx_buffer
    }
}


//--------------------------------------------------------------------------------------------------
// snippet_cyhal_tdm_async_transmit_streaming
//--------------------------------------------------------------------------------------------------
static void snippet_cyhal_tdm_async_transmit_streaming(void)
{
    cyhal_tdm_t tdm;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the tdm object, so that
    // we can easily reference it from the callback handler.
    cyhal_tdm_register_callback(&tdm, &tdm_event_handler_transmit_streaming, &tdm);

    // Subscribe to the async complete event so that we can queue up another transfer when this one
    // completes
    cyhal_tdm_enable_event(&tdm, CYHAL_TDM_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_tdm_set_async_mode(&tdm, CYHAL_ASYNC_DMA, CYHAL_DMA_PRIORITY_DEFAULT);
    CY_ASSERT(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_tdm_write_async(&tdm, active_tx_buffer, BUFFER_SIZE);
    result = cyhal_tdm_start_tx(&tdm);
}

Snippet 4: TDM Receive

This snippet shows how to receive data using cyhal_tdm_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
static uint32_t  rx_buffer0[BUFFER_SIZE];
static uint32_t  rx_buffer1[BUFFER_SIZE];
static uint32_t* active_rx_buffer;
static uint32_t* full_rx_buffer;
//--------------------------------------------------------------------------------------------------
// tdm_event_handler_receive
//--------------------------------------------------------------------------------------------------
static void tdm_event_handler_receive(void* arg, cyhal_tdm_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the tdm object
    cyhal_tdm_t* tdm = (cyhal_tdm_t*)arg;
    if (0u != (event & CYHAL_TDM_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_tdm_read_async(tdm, active_rx_buffer, BUFFER_SIZE);
        CY_ASSERT(CY_RSLT_SUCCESS == result);

        // Process the data in the full_rx_buffer
    }
}


//--------------------------------------------------------------------------------------------------
// snippet_cyhal_tdm_async_receive
//--------------------------------------------------------------------------------------------------
static void snippet_cyhal_tdm_async_receive(void)
{
    cyhal_tdm_t tdm;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the tdm object, so that
    // we can easily reference it from the callback handler.
    cyhal_tdm_register_callback(&tdm, &tdm_event_handler_receive, &tdm);

    // Subscribe to the async complete event so that we can queue up another transfer when this one
    // completes
    cyhal_tdm_enable_event(&tdm, CYHAL_TDM_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_tdm_set_async_mode(&tdm, CYHAL_ASYNC_DMA, CYHAL_DMA_PRIORITY_DEFAULT);
    CY_ASSERT(CY_RSLT_SUCCESS == result);

    active_rx_buffer = rx_buffer0;
    full_rx_buffer   = rx_buffer1;
    result           = cyhal_tdm_read_async(&tdm, active_rx_buffer, BUFFER_SIZE);
    result           = cyhal_tdm_start_rx(&tdm);
}

More Information

Code examples (Github)

Typedefs

typedef void (*cyhal_tdm_event_callback_t)(void *callback_arg, cyhal_tdm_event_t event)

Handler for TDM event callbacks.

Enums

enum cyhal_tdm_event_t

cyhal_tdm_event_t: TDM events.

Values:

enumerator CYHAL_TDM_TX_NOT_FULL

TX HW Buffer is not full.

enumerator CYHAL_TDM_TX_HALF_EMPTY

TX HW Buffer is half empty.

enumerator CYHAL_TDM_TX_EMPTY

TX HW Buffer is empty.

enumerator CYHAL_TDM_TX_OVERFLOW

Attempt to write when TX HW Buffer is full.

enumerator CYHAL_TDM_TX_UNDERFLOW

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

enumerator CYHAL_TDM_ASYNC_TX_COMPLETE

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

enumerator CYHAL_TDM_RX_NOT_EMPTY

RX HW Buffer is not Empty.

enumerator CYHAL_TDM_RX_HALF_FULL

RX HW Buffer is half full.

enumerator CYHAL_TDM_RX_FULL

RX HW Buffer is FULL.

enumerator CYHAL_TDM_RX_OVERFLOW

Attempt to write when RX HW Buffer is full.

enumerator CYHAL_TDM_RX_UNDERFLOW

Attempt to read when HW RX buffer is empty.

enumerator CYHAL_TDM_ASYNC_RX_COMPLETE

Pending async receive is complete.

enum cyhal_tdm_word_select_width_t

cyhal_tdm_word_select_width_t: Word select pulse width.

Values:

enumerator CYHAL_TDM_WS_SINGLE

Single SCK cycle.

enumerator CYHAL_TDM_WS_FULL

Full channel length.

Functions

cy_rslt_t cyhal_tdm_init(cyhal_tdm_t *obj, const cyhal_tdm_pins_t *tx_pins, const cyhal_tdm_pins_t *rx_pins, cyhal_gpio_t mclk, const cyhal_tdm_config_t *config, cyhal_clock_t *clk)

Initialize the TDM peripheral.

It sets the default parameters for TDM 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 a TDM object. The caller must allocate the memory for this object but the init function will initialize its contents.

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

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

  • [in] mclk: The master clock input pin, if an external clock should be used for the TDM 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_tdm_free(cyhal_tdm_t *obj)

Deinitialize the tdm object.

Parameters
  • [inout] obj: The tdm object

cy_rslt_t cyhal_tdm_set_sample_rate(cyhal_tdm_t *obj, uint32_t sample_rate_hz)

Set the TDM sample rate.

Return

The status of the set sample rate request

Parameters
  • [in] obj: The TDM object

  • [in] sample_rate_hz: Sample rate in Hz

cy_rslt_t cyhal_tdm_start_tx(cyhal_tdm_t *obj)

Starts transmitting data.

Transmission will continue until it is stopped by calling cyhal_tdm_stop_tx.

Return

The status of the start request.

Parameters
  • [in] obj: The TDM object

cy_rslt_t cyhal_tdm_stop_tx(cyhal_tdm_t *obj)

Stops transmitting data.

This immediately terminates data transmission.

Return

The status of the stop request.

Parameters
  • [in] obj: The TDM object

cy_rslt_t cyhal_tdm_clear_tx(cyhal_tdm_t *obj)

Clears the tx hardware buffer.

Return

The status of the clear request

Parameters
  • [in] obj: The tdm peripheral

cy_rslt_t cyhal_tdm_start_rx(cyhal_tdm_t *obj)

Starts receiving data.

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

Return

The status of the start request.

Parameters
  • [in] obj: The TDM object

cy_rslt_t cyhal_tdm_stop_rx(cyhal_tdm_t *obj)

Stops receiving data.

This immediately terminates data receipt.

Return

The status of the stop request.

Parameters
  • [in] obj: The TDM object

cy_rslt_t cyhal_tdm_clear_rx(cyhal_tdm_t *obj)

Clears the rx hardware buffer.

Return

The status of the clear request

Parameters
  • [in] obj: The tdm peripheral

cy_rslt_t cyhal_tdm_read(cyhal_tdm_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 write request

Parameters
  • [in] obj: The TDM object

  • [out] data: The buffer for receiving

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

cy_rslt_t cyhal_tdm_write(cyhal_tdm_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 TDM object

  • [in] data: The buffer for sending

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

bool cyhal_tdm_is_tx_enabled(cyhal_tdm_t *obj)

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

The transmit functionality can be enabled by calling cyhal_tdm_start_tx and disabled by calling cyhal_tdm_stop_tx

Return

Whether the TDM transmit function is enabled.

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

bool cyhal_tdm_is_rx_enabled(cyhal_tdm_t *obj)

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

The receive functionality can be enabled by calling cyhal_tdm_start_rx and disabled by calling cyhal_tdm_stop_rx

Return

Whether the TDM receive function is enabled.

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

bool cyhal_tdm_is_tx_busy(cyhal_tdm_t *obj)

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

Return

Whether the TDM is still transmitting

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

bool cyhal_tdm_is_rx_busy(cyhal_tdm_t *obj)

Checks if the specified TDM 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 TDM is still transmitting

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

cy_rslt_t cyhal_tdm_read_async(cyhal_tdm_t *obj, void *rx, size_t rx_length)

Start TDM 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_TDM_ASYNC_RX_COMPLETE event will be raised. See cyhal_tdm_register_callback and cyhal_tdm_enable_event.

cyhal_tdm_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 read_async request

Parameters

cy_rslt_t cyhal_tdm_write_async(cyhal_tdm_t *obj, const void *tx, size_t tx_length)

Start TDM 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_TDM_ASYNC_TX_COMPLETE event will be raised. See cyhal_tdm_register_callback and cyhal_tdm_enable_event.

cyhal_tdm_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 TDM object

  • [in] tx: The transmit buffer.

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

cy_rslt_t cyhal_tdm_set_async_mode(cyhal_tdm_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)

Set the mechanism that is used to perform TDM 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 TDM 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_tdm_is_read_pending(cyhal_tdm_t *obj)

Checks if the specified TDM 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_tdm_read_async) into RAM from the TDM 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 TDM peripheral to check

bool cyhal_tdm_is_write_pending(cyhal_tdm_t *obj)

Checks if the specified TDM 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_tdm_transfer_async) from RAM into the TDM 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 TDM peripheral to check

cy_rslt_t cyhal_tdm_abort_read_async(cyhal_tdm_t *obj)

Abort TDM 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 TDM object

cy_rslt_t cyhal_tdm_abort_write_async(cyhal_tdm_t *obj)

Abort TDM 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 TDM object

void cyhal_tdm_register_callback(cyhal_tdm_t *obj, cyhal_tdm_event_callback_t callback, void *callback_arg)

Register a TDM callback handler.

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

Parameters
  • [in] obj: The TDM 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_tdm_enable_event(cyhal_tdm_t *obj, cyhal_tdm_event_t event, uint8_t intr_priority, bool enable)

Configure TDM events.

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

Parameters
  • [in] obj: The TDM object

  • [in] event: The TDM 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_tdm_pins_t
#include <cyhal_tdm.h>

Pins to use for one TDM 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_tdm_config_t
#include <cyhal_tdm.h>

TDM Configuration.

Public Members

bool is_tx_slave

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

cyhal_tdm_word_select_width_t tx_ws_width

Word select pulse width for TX direction.

bool is_rx_slave

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

cyhal_tdm_word_select_width_t rx_ws_width

Word select pulse width for RX direction.

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 num_channels

Number of channels.

Must be a value between 1 and 32. Not all implementations support all channel counts; see the implementation specific documentation for details.

uint32_t channel_mask

Determines which channels are enabled.

Disabled channels still participate in the time slicing, but the contents of a disabled channel are silently discarded (for Rx) or sent as all 0’s (for Tx). This is a mask, with each bit corresponding to the enabled state of one channel. Not all implementations permit masking of arbitrary channel combinations. See the implementation specific documentation for details.

uint8_t word_length

Number of bits in each word.

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

uint32_t sample_rate_hz

Sample rate in Hz.