Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-hal-cat1 / include / cyhal_i2s.h
@Dustin Crossman Dustin Crossman on 4 Jun 2021 19 KB Fix file modes.
/***************************************************************************//**
* \file cyhal_i2s.h
*
* \brief
* Provides a high level interface for interacting with the Cypress I2S.
* This interface abstracts out the chip specific details. If any chip specific
* functionality is necessary, or performance is critical the low level functions
* can be used directly.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

/**
* \addtogroup group_hal_i2s I2S (Inter-IC Sound)
* \ingroup group_hal
* \{
* 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.
*
* \section section_i2s_features Features
*
* * Transmit and receive functionality
* * Configurable data rates
* * Configurable channel and word size
* * Configurable interrupt and callback assignment from I2S events - \ref cyhal_i2s_event_t
*
* \section section_i2s_quickstart Quick Start
* Initialize an I2S instance using the \ref cyhal_i2s_init and provide the transmit (tx) and/or receive (rx) pins. Call
* \ref cyhal_i2s_start_tx and/or \ref cyhal_i2s_start_rx to enable transmit and/or receive functionality as desired.<br>
* See \ref subsection_i2s_snippet_1 for example initialization as transmit or receive.
* \note The clock parameter (const \ref 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.
*
* \section section_i2s_snippets Code Snippets
*
* \subsection subsection_i2s_snippet_1 Snippet 1: I2S Initialization and Configuration
* This snippet initializes an I2S resource for transmit or receive and assigns the pins.
*
* Initializing as I2S transmitter
* \snippet hal_i2s.c snippet_cyhal_i2s_transmit_init
*
* Initializing as I2S receiver
* \snippet hal_i2s.c snippet_cyhal_i2s_receive_init
*
* \subsection subsection_i2s_snippet_2 Snippet 2: I2S Transmit One-shot
* This snippet shows how to transmit data using \ref cyhal_i2s_write_async when the entire sample
* is available at once. <br>
*
* \snippet hal_i2s.c snippet_cyhal_i2s_async_transmit_one_shot

* \subsection subsection_i2s_snippet_3 Snippet 3: I2S Transmit Streaming
* This snippet shows how to transmit data using \ref cyhal_i2s_write_async when sample data is
* being continuously loaded and transmitted (e.g. streaming over the network). <br>
*
* \snippet hal_i2s.c snippet_cyhal_i2s_async_transmit_streaming

* \subsection subsection_i2s_snippet_4 Snippet 4: I2S Receive
* This snippet shows how to receive data using \ref cyhal_i2s_read_async. <br>
*
* \snippet hal_i2s.c snippet_cyhal_i2s_async_receive
*
* \section subsection_i2s_moreinformation More Information
*
* <b>Code examples (Github)</b>
* * <a href="https://github.com/cypresssemiconductorco/mtb-example-psoc6-i2s" ><b>
PSoC 6 MCU: Inter-IC Sound (I2S)</b></a>
* * <a href="https://github.com/cypresssemiconductorco/mtb-example-psoc6-pdm-to-i2s" ><b>
PSoC 6 MCU: PDM to I2S</b></a>
*/

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "cy_result.h"
#include "cyhal_general_types.h"
#include "cyhal_hw_types.h"
#include "cyhal_gpio.h"

#if defined(__cplusplus)
extern "C" {
#endif

/** \addtogroup group_hal_results_i2s I2S HAL Results
 *  I2S specific return codes
 *  \ingroup group_hal_results
 *  \{ *//**
 */

/** An invalid pin location was specified */
#define CYHAL_I2S_RSLT_ERR_INVALID_PIN                  \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_I2S, 0))
/** An argument was provided */
#define CYHAL_I2S_RSLT_ERR_INVALID_ARG                  \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_I2S, 1))
/** Initialization of the I2S hardware failed*/
#define CYHAL_I2S_RSLT_ERR_INIT_FAILED                  \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_I2S, 2))
/** The requested clock frequency could not be achieved */
#define CYHAL_I2S_RSLT_ERR_CLOCK \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_I2S, 3))

/**
 * \}
 */

/** I2S events */
typedef enum {
    /** TX HW Buffer is not full */
    CYHAL_I2S_TX_NOT_FULL         = 1 << 0,
    /** TX HW Buffer is half empty */
    CYHAL_I2S_TX_HALF_EMPTY       = 1 << 1,
    /** TX HW Buffer is Empty */
    CYHAL_I2S_TX_EMPTY            = 1 << 2,
    /** Attempt to write when TX HW Buffer is full */
    CYHAL_I2S_TX_OVERFLOW         = 1 << 3,
    /** Interface ready to transfer data but HW TX buffer is empty */
    CYHAL_I2S_TX_UNDERFLOW        = 1 << 4,
    /** Pending async transmit is complete (but the HW buffer may still contain unsent data) */
    CYHAL_I2S_ASYNC_TX_COMPLETE   = 1 << 5,
    /** RX HW Buffer is not Empty */
    CYHAL_I2S_RX_NOT_EMPTY        = 1 << 6,
    /** RX HW Buffer is half full */
    CYHAL_I2S_RX_HALF_FULL        = 1 << 7,
    /** RX HW Buffer is FULL */
    CYHAL_I2S_RX_FULL             = 1 << 8,
    /** Attempt to write when RX HW Buffer is full */
    CYHAL_I2S_RX_OVERFLOW         = 1 << 9,
    /** Attempt to read when HW RX buffer is empty */
    CYHAL_I2S_RX_UNDERFLOW        = 1 << 10,
    /** Pending async receive is complete */
    CYHAL_I2S_ASYNC_RX_COMPLETE   = 1 << 11,
} cyhal_i2s_event_t;

/** Pins to use for one I2S direction */
typedef struct {
    cyhal_gpio_t sck;   //!< Clock pin
    cyhal_gpio_t ws;    //!< Word select
    cyhal_gpio_t data;  //!< Data pin (sdo or sdi)
} cyhal_i2s_pins_t;

/** I2S Configuration */
typedef struct {
    /** Configure TX to operate a slave (true) or master (false) */
    bool is_tx_slave;
    /** Configure RX to operate a slave (true) or master (false) **/
    bool is_rx_slave;
    /** 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.
     */
    uint32_t mclk_hz;
    /** Number of bits in each channel. See the implementation specific documentation for supported values. **/
    uint8_t channel_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.
     */
    uint8_t word_length;
    /** Sample rate in Hz */
    uint32_t sample_rate_hz;
} cyhal_i2s_config_t;

/** Handler for I2S event callbacks */
typedef void (*cyhal_i2s_event_callback_t)(void *callback_arg, cyhal_i2s_event_t event);

/** 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.
 *
 * @param[out] obj          Pointer to an I2S object. The caller must allocate the memory
 *                            for this object but the init function will initialize its contents.
 * @param[in]  tx_pins      Pins for I2S transmit. If NULL, transmit functionality will be disabled.
 * @param[in]  rx_pins      Pins for I2S receive. If NULL, receive functionality will be disabled.
 * @param[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.
 * @param[in]  config       Initial block configuration
 * @param[in]  clk          Clock source to use for this instance. If NULL, a dedicated clock divider will be allocated for this instance.
 * @return The status of the init request
 */
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);

/** Deinitialize the i2s object
 *
 * @param[in,out] obj The i2s object
 */
void cyhal_i2s_free(cyhal_i2s_t *obj);

/** Set the I2S sample rate
 *
 * @param[in] obj The I2S object
 * @param[in] sample_rate_hz Sample rate in Hz
 * @return The status of the set sample rate request
 */
cy_rslt_t cyhal_i2s_set_sample_rate(cyhal_i2s_t *obj, uint32_t sample_rate_hz);

/** Starts transmitting data. Transmission will continue until it is stopped by
  * calling @ref cyhal_i2s_stop_tx.
  *
  * @param[in] obj The I2S object
  * @return The status of the start request.
  */
cy_rslt_t cyhal_i2s_start_tx(cyhal_i2s_t *obj);

/** Stops transmitting data. This immediately terminates transmission.
  *
  * @param[in] obj The I2S object
  * @return The status of the stop request.
  */
cy_rslt_t cyhal_i2s_stop_tx(cyhal_i2s_t *obj);

/** Clears the tx hardware buffer
 *
 * @param[in] obj The i2s peripheral
 * @return The status of the clear request
 */
cy_rslt_t cyhal_i2s_clear_tx(cyhal_i2s_t *obj);

/** Starts receiving data. Data will continue to be received until it is
  * stopped by calling @ref cyhal_i2s_stop_rx.
  *
  * @param[in] obj The I2S object
  * @return The status of the start request.
  */
cy_rslt_t cyhal_i2s_start_rx(cyhal_i2s_t *obj);

/** Stops receiving data. This immediately terminates data receipt.
  *
  * @param[in] obj The I2S object
  * @return The status of the stop request.
  */
cy_rslt_t cyhal_i2s_stop_rx(cyhal_i2s_t *obj);

/** Clears the rx hardware buffer
 *
 * @param[in] obj The i2s peripheral
 * @return The status of the clear request
 */
cy_rslt_t cyhal_i2s_clear_rx(cyhal_i2s_t *obj);

/** 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.
 *
 * @param[in]  obj    The I2S object
 * @param[out] data   The buffer for receiving
 * @param[in,out] length Number of words to (as configured in cyhal_i2s_config_t.word_length) read, updated with the number actually read
 * @return The status of the read request
 *
 * @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
 */
cy_rslt_t cyhal_i2s_read(cyhal_i2s_t *obj, 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.
 *
 * @param[in] obj     The I2S object
 * @param[in] data    The buffer for sending
 * @param[in,out] length Number of words to write (as configured in cyhal_i2s_config_t.word_length, updated with the number actually written
 * @return The status of the write request
 *
 * @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.
 */
cy_rslt_t cyhal_i2s_write(cyhal_i2s_t *obj, const void *data, size_t *length);

/** 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 @ref cyhal_i2s_start_tx and disabled by calling
  * @ref cyhal_i2s_stop_tx
  *
  * @param[in] obj  The I2S peripheral to check
  * @return Whether the I2S transmit function is enabled.
  */
bool cyhal_i2s_is_tx_enabled(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.
  *
  * @param[in] obj  The I2S peripheral to check
  * @return Whether the I2S is still transmitting
  */
bool cyhal_i2s_is_tx_busy(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 @ref cyhal_i2s_start_rx and disabled by calling
  * @ref cyhal_i2s_stop_rx
  *
  * @param[in] obj  The I2S peripheral to check
  * @return Whether the I2S receive function is enabled.
  */
bool cyhal_i2s_is_rx_enabled(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.
  *
  * @param[in] obj  The I2S peripheral to check
  * @return Whether the I2S is still transmitting
  */
bool cyhal_i2s_is_rx_busy(cyhal_i2s_t *obj);

/** 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 @ref CYHAL_I2S_ASYNC_RX_COMPLETE event will be raised.
 * See @ref cyhal_i2s_register_callback and @ref 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.
 *
 * @param[in]     obj       The I2S object
 * @param[out]    rx        The receive buffer.
 * @param[in]     rx_length Number of words (as configured in cyhal_i2s_config_t.word_length) to read.
 * @return The status of the read_async request
 */
cy_rslt_t cyhal_i2s_read_async(cyhal_i2s_t *obj, void *rx, size_t rx_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 @ref CYHAL_I2S_ASYNC_TX_COMPLETE
 * event will be raised. See @ref cyhal_i2s_register_callback and @ref 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.
 *
 * @param[in]     obj       The I2S object
 * @param[in]     tx        The transmit buffer.
 * @param[in]     tx_length The number of words to transmit.
 * @return The status of the transfer_async request
 */
cy_rslt_t cyhal_i2s_write_async(cyhal_i2s_t *obj, const void *tx, size_t tx_length);

/** 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.
 *
 * @param[in]     obj          The I2S object
 * @param[in]     mode         The transfer mode
 * @param[in]     dma_priority The priority, if DMA is used. Valid values are the same as for @ref cyhal_dma_init.
 *                             If DMA is not selected, the only valid value is CYHAL_DMA_PRIORITY_DEFAULT, and no
                               guarantees are made about prioritization.
 * @return The status of the set mode request
 */
cy_rslt_t cyhal_i2s_set_async_mode(cyhal_i2s_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority);

/** 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.
 *
 * @param[in] obj  The I2S peripheral to check
 * @return Whether an asynchronous read operation is still in progress
 */
bool cyhal_i2s_is_read_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.
 *
 * @param[in] obj  The I2S peripheral to check
 * @return Whether an asynchronous write operation is still in progress
 */
bool cyhal_i2s_is_write_pending(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.
 *
 * @param[in] obj The I2S object
 * @return The status of the abort_async_read request
 */
cy_rslt_t cyhal_i2s_abort_read_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.
 *
 * @param[in] obj The I2S object
 * @return The status of the abort_async_write request
 */
cy_rslt_t cyhal_i2s_abort_write_async(cyhal_i2s_t *obj);

/** Register an I2S callback handler
 *
 * This function will be called when one of the events enabled by \ref cyhal_i2s_enable_event occurs.
 *
 * @param[in] obj          The I2S object
 * @param[in] callback     The callback handler which will be invoked when the interrupt fires
 * @param[in] callback_arg Generic argument that will be provided to the callback when called
 */
void cyhal_i2s_register_callback(cyhal_i2s_t *obj, cyhal_i2s_event_callback_t callback, void *callback_arg);

/** Configure I2S events.
 *
 * When an enabled event occurs, the function specified by \ref cyhal_i2s_register_callback will be called.
 *
 * @param[in] obj            The I2S object
 * @param[in] event          The I2S event type
 * @param[in] intr_priority  The priority for NVIC interrupt events
 * @param[in] enable         True to turn on specified events, False to turn off
 */
void cyhal_i2s_enable_event(cyhal_i2s_t *obj, cyhal_i2s_event_t event, uint8_t intr_priority, bool enable);

#if defined(__cplusplus)
}
#endif

#ifdef CYHAL_I2S_IMPL_HEADER
#include CYHAL_I2S_IMPL_HEADER
#endif /* CYHAL_I2S_IMPL_HEADER */

/** \} group_hal_i2s */