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.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.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.
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
Initializing as TDM receivercyhal_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);
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.
-
enumerator
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.
- Parameters
obj – [out] Pointer to a TDM object. The caller must allocate the memory for this object but the init function will initialize its contents.
tx_pins – [in] Pins for TDM transmit. If NULL, transmit functionality will be disabled.
rx_pins – [in] Pins for TDM receive. If NULL, receive functionality will be disabled.
mclk – [in] 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.
config – [in] Initial block configuration
clk – [in] Clock source to use for this instance. If NULL, a dedicated clock divider will be allocated for this instance.
- Returns
The status of the init request
-
void
cyhal_tdm_free
(cyhal_tdm_t *obj)¶ Deinitialize the tdm object.
- Parameters
obj – [inout] 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.
- Parameters
obj – [in] The TDM object
sample_rate_hz – [in] Sample rate in Hz
- Returns
The status of the set sample rate request
-
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.
- Parameters
obj – [in] The TDM object
- Returns
The status of the start request.
-
cy_rslt_t
cyhal_tdm_stop_tx
(cyhal_tdm_t *obj)¶ Stops transmitting data.
This immediately terminates data transmission.
- Parameters
obj – [in] The TDM object
- Returns
The status of the stop request.
-
cy_rslt_t
cyhal_tdm_clear_tx
(cyhal_tdm_t *obj)¶ Clears the tx hardware buffer.
- Parameters
obj – [in] The tdm peripheral
- Returns
The status of the clear request
-
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.
- Parameters
obj – [in] The TDM object
- Returns
The status of the start request.
-
cy_rslt_t
cyhal_tdm_stop_rx
(cyhal_tdm_t *obj)¶ Stops receiving data.
This immediately terminates data receipt.
- Parameters
obj – [in] The TDM object
- Returns
The status of the stop request.
-
cy_rslt_t
cyhal_tdm_clear_rx
(cyhal_tdm_t *obj)¶ Clears the rx hardware buffer.
- Parameters
obj – [in] The tdm peripheral
- Returns
The status of the clear request
-
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.
- Parameters
obj – [in] The TDM object
data – [out] The buffer for receiving
length – [inout] Number of words to (as configured in cyhal_tdm_config_t.word_length) read, updated with the number actually read
- Returns
The status of the read request
- Returns
The status of the write request
-
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.
- Parameters
obj – [in] The TDM object
data – [in] The buffer for sending
length – [inout] Number of words to write (as configured in cyhal_tdm_config_t.word_length, updated with the number actually written
- Returns
The status of the write request
-
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
- Parameters
obj – [in] The TDM peripheral to check
- Returns
Whether the TDM transmit function is enabled.
-
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
- Parameters
obj – [in] The TDM peripheral to check
- Returns
Whether the TDM receive function is enabled.
-
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.
- Parameters
obj – [in] The TDM peripheral to check
- Returns
Whether the TDM is still transmitting
-
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.
- Parameters
obj – [in] The TDM peripheral to check
- Returns
Whether the TDM is still transmitting
-
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.
- Parameters
obj – [in] The TDM object
rx – [out] The receive buffer.
rx_length – [in] Number of words (as configured in cyhal_tdm_config_t.word_length) to read.
- Returns
The status of the read_async request
-
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.
- Parameters
obj – [in] The TDM object
tx – [in] The transmit buffer.
tx_length – [in] The number of words to transmit.
- Returns
The status of the transfer_async request
-
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.
- Parameters
obj – [in] The TDM object
mode – [in] The transfer mode
dma_priority – [in] 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.
- Returns
The status of the set mode request
-
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.
- Parameters
obj – [in] The TDM peripheral to check
- Returns
Whether an asynchronous read operation is still in progress
-
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.
- Parameters
obj – [in] The TDM peripheral to check
- Returns
Whether an asynchronous write operation is still in progress
-
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.
- Parameters
obj – [in] The TDM object
- Returns
The status of the abort_async_read request
-
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.
- Parameters
obj – [in] The TDM object
- Returns
The status of the abort_async_write request
-
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
obj – [in] The TDM object
callback – [in] The callback handler which will be invoked when the interrupt fires
callback_arg – [in] 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
obj – [in] The TDM object
event – [in] The TDM event type
intr_priority – [in] The priority for NVIC interrupt events
enable – [in] True to turn on specified events, False to turn off
-
struct
cyhal_tdm_pins_t
¶ - #include <>
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)
-
cyhal_gpio_t
-
struct
cyhal_tdm_config_t
¶ - #include <>
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.
-
bool