rt-thread-official/bsp/essemi/es32vf2264/libraries/ALD/ES32VF2264/Source/ald_i2s.c

1043 lines
32 KiB
C

/**
*********************************************************************************
*
* @file ald_I2S.c
* @brief I2S module driver.
* This file provides firmware functions to manage the following
* functionalities of I2S peripheral:
* + Initialization functions
* + IO operation functions
* + Peripheral Control functions
* + Peripheral State functions
*
* @version V1.0
* @date 09 Mar. 2023
* @author AE Team
* @note
* Change Logs:
* Date Author Notes
* 09 Mar. 2023 Lisq The first version
*
* Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
*
* 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
*
* 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.
**********************************************************************************
@verbatim
==============================================================================
##### How to use this driver #####
==============================================================================
[..]
The I2S driver can be used as follows:
(#) Declare a i2s_handle_t structure, for example:
i2s_handle_t hperh;
(#) Initialize the I2S low level resources:
(##) Enable the I2Sx interface clock
(##) I2S pins configuration
(+++) Enable the clock for the I2S GPIOs
(+++) Configure these I2S pins as push-pull
(##) NVIC configuration if you need to use interrupt process
by implementing the ald_mcu_irq_config() API.
Invoked ald_i2s_irq_handler() function in I2S-IRQ function
(##) DMA Configuration if you need to use DMA process
(+++) Define ALD_DMA in ald_conf.h
(+++) Enable the DMAx clock
(#) Program the Channel length, Data length, Polarity, Standard, Pcm frame,
external clock and Main clock output, Odd factor and Divide clock in the i2s_init_t structure.
(#) Initialize the I2S module by invoking the ald_i2s_init() API.
[..]
Circular mode restriction:
(#) When the I2S DMA Pause/Stop features are used, we must use the following APIs
the ald_i2s_dma_pause()/ ald_i2s_dma_stop().
* @endverbatim
*/
#include "ald_cmu.h"
#include "ald_i2s.h"
/** @addtogroup ES32VF2264_ALD
* @{
*/
/** @defgroup I2S I2S
* @brief I2S module driver
* @{
*/
/** @addtogroup I2S_Private_Functions I2S Private Functions
* @{
*/
static ald_status_t i2s_wait_status(ald_i2s_handle_t *hperh, ald_i2s_status_t state, flag_status_t status, uint32_t timeout);
static void __i2s_send_by_it(ald_i2s_handle_t *hperh);
static void __i2s_recv_by_it(ald_i2s_handle_t *hperh);
static void __i2s_tx_recv_by_it(ald_i2s_handle_t *hperh);
static void i2s_dma_send_cplt(void *arg);
static void i2s_dma_recv_cplt(void *arg);
/**
* @}
*/
/** @defgroup I2S_Public_Functions I2S Public Functions
* @{
*/
/** @defgroup I2S_Public_Functions_Group1 Initialization functions
* @brief Initialization and Configuration functions
*
* @verbatim
===============================================================================
##### Initialization and de-initialization functions #####
===============================================================================
[..] This subsection provides a set of functions allowing to initialize and
reset the I2Sx peripheral:
(+) User must configure all related peripherals resources
(CLOCK, GPIO, DMA, NVIC).
(+) Call the function ald_i2s_init() to configure the selected device with
the selected configuration:
(++) Channel length
(++) Data length
(++) Polarity
(++) Standard
(++) Pcm frame
(++) External clock
(++) Main clock output function
(++) Odd factor
(++) Divide clock
(+) Call the function ald_i2s_reset() to reset the selected I2Sx periperal.
@endverbatim
* @{
*/
/**
* @brief Reset the I2S peripheral.
* @param hperh: Pointer to a i2s_handle_t structure that contains
* the configuration information for the specified I2S module.
* @retval None
*/
void ald_i2s_reset(ald_i2s_handle_t *hperh)
{
hperh->perh->I2SCFG = 0x0;
hperh->perh->I2SPR = 0x0;
ALD_I2S_RESET_HANDLE_STATE(hperh);
__UNLOCK(hperh);
return;
}
/**
* @brief Initializes the I2S mode according to the specified parameters in
* the i2s_init_t and create the associated handle.
* @param hperh: Pointer to a i2s_handle_t structure that contains
* the configuration information for the specified SPI module.
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_init(ald_i2s_handle_t *hperh)
{
uint32_t tmp = 0, clk, _div;
if (hperh == NULL)
return ALD_ERROR;
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_CH_LEN(hperh->init.ch_len));
assert_param(IS_I2S_DATE_LEN(hperh->init.data_len));
assert_param(IS_I2S_CPOL(hperh->init.polarity));
assert_param(IS_I2S_STANDARD(hperh->init.standard));
assert_param(IS_FUNC_STATE(hperh->init.ext_clk_en));
assert_param(IS_FUNC_STATE(hperh->init.mck_en));
assert_param(IS_I2S_PCMS(hperh->init.pcm_frame));
ald_i2s_reset(hperh);
tmp |= (hperh->init.ext_clk_en << SPI_I2SPR_EXTCKEN_POS);
/* Get I2S clock */
if (hperh->init.ext_clk_en)
clk = hperh->init.ext_clk;
else
clk = ald_cmu_get_pclk_clock();
if (hperh->init.mck_en) {
_div = ((clk / hperh->init.sampling) >> 8);
}
else {
if (hperh->init.ch_len == ALD_I2S_WIDE_16)
_div = ((clk / hperh->init.sampling) >> 5);
else
_div = ((clk / hperh->init.sampling) >> 6);
}
if (_div & 0x1) {
SET_BIT(tmp, SPI_I2SPR_ODD_MSK);
--_div;
}
else {
CLEAR_BIT(tmp, SPI_I2SPR_ODD_MSK);
}
if (hperh->init.standard != ALD_I2S_STD_PCM)
MODIFY_REG(tmp, SPI_I2SPR_I2SDIV_MSK, (_div >> 1) << SPI_I2SPR_I2SDIV_POSS);
else
MODIFY_REG(tmp, SPI_I2SPR_I2SDIV_MSK, _div << SPI_I2SPR_I2SDIV_POSS);
hperh->perh->I2SPR = tmp;
tmp = hperh->perh->I2SCFG;
tmp |= ((hperh->init.ch_len << SPI_I2SCFG_CHLEN_POS) | (hperh->init.data_len << SPI_I2SCFG_DATLEN_POSS) |
(hperh->init.polarity << SPI_I2SCFG_CKPOL_POS) | (hperh->init.standard << SPI_I2SCFG_I2SSTD_POSS) |
(1 << SPI_I2SCFG_I2SMOD_POS));
hperh->perh->I2SCFG = tmp;
if (hperh->init.standard == ALD_I2S_STD_PCM)
hperh->perh->I2SCFG |= (hperh->init.pcm_frame << SPI_I2SCFG_PCMSYNC_POS);
hperh->err_code = ALD_I2S_ERROR_NONE;
hperh->state = ALD_I2S_STATE_READY;
return ALD_OK;
}
/**
* @}
*/
/** @defgroup I2S_Public_Functions_Group2 IO operation functions
* @brief I2S Transmit and Receive functions
*
* @verbatim
==============================================================================
##### IO operation functions #####
===============================================================================
This subsection provides a set of functions allowing to manage the I2S
data transfers.
[..] The I2S supports master or slave mode:
(#) There are two modes of transfer:
(++) Blocking mode: The communication is performed in polling mode.
The ALD status of all data processing is returned by the same function
after finishing transfer.
(++) No-Blocking mode: The communication is performed using Interrupts
or DMA, These APIs return the ALD status.
The end of the data processing will be indicated through the
dedicated I2S IRQ when using Interrupt mode or the DMA IRQ when
using DMA mode.
The hperh->tx_cplt_cbk(), hperh->rx_cplt_cbk() user callbacks
will be executed respectivelly at the end of the transmit or Receive process
The hperh->err_cbk() user callback will be executed when a communication error is detected
(#) APIs provided for these 2 transfer modes (Blocking mode or Non blocking mode using either Interrupt or DMA).
* @endverbatim
* @{
*/
/**
* @brief Master mode transmit an amount of data in blocking mode.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param buf: Pointer to data buffer
* @param size: Amount of data to be sent
* @param timeout: Timeout duration
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_master_send(ald_i2s_handle_t *hperh, uint16_t *buf, uint32_t size, uint32_t timeout)
{
assert_param(IS_I2S(hperh->perh));
if (hperh->state != ALD_I2S_STATE_READY)
return ALD_BUSY;
if (buf == NULL || size == 0)
return ALD_ERROR;
hperh->state = ALD_I2S_STATE_BUSY_TX;
hperh->err_code = ALD_I2S_ERROR_NONE;
MODIFY_REG(hperh->perh->I2SCFG, SPI_I2SCFG_I2SCFG_MSK, ALD_I2S_MASTER_TRANSMIT << SPI_I2SCFG_I2SCFG_POSS);
if (hperh->init.mck_en)
MODIFY_REG(hperh->perh->I2SPR, SPI_I2SPR_MCKOE_MSK, hperh->init.mck_en << SPI_I2SPR_MCKOE_POS);
if (READ_BIT(hperh->perh->I2SCFG, SPI_I2SCFG_I2SE_MSK) == 0)
ALD_I2S_ENABLE(hperh);
while (size > 0) {
if (i2s_wait_status(hperh, ALD_I2S_STATUS_TXE, SET, timeout) != ALD_OK) {
ALD_I2S_DISABLE(hperh);
hperh->state = ALD_I2S_STATE_READY;
return ALD_TIMEOUT;
}
hperh->side = READ_BITS(hperh->perh->STAT, SPI_STAT_CHSIDE_MSK, SPI_STAT_CHSIDE_POS);
hperh->perh->DATA = *buf++;
--size;
}
if ((i2s_wait_status(hperh, ALD_I2S_STATUS_TXE, SET, timeout) != ALD_OK)
|| (i2s_wait_status(hperh, ALD_I2S_STATUS_BUSY, RESET, timeout) != ALD_OK)) {
ALD_I2S_DISABLE(hperh);
hperh->state = ALD_I2S_STATE_READY;
return ALD_TIMEOUT;
}
ALD_I2S_DISABLE(hperh);
hperh->state = ALD_I2S_STATE_READY;
return ALD_OK;
}
/**
* @brief Master mode receive an amount of data in blocking mode.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param buf: Pointer to data buffer
* @param size: Amount of data to be received
* @param timeout: Timeout duration
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_master_recv(ald_i2s_handle_t *hperh, uint16_t *buf, uint32_t size, uint32_t timeout)
{
assert_param(IS_I2S(hperh->perh));
if (hperh->state != ALD_I2S_STATE_READY)
return ALD_BUSY;
if (buf == NULL || size == 0)
return ALD_ERROR;
hperh->state = ALD_I2S_STATE_BUSY_RX;
hperh->err_code = ALD_I2S_ERROR_NONE;
MODIFY_REG(hperh->perh->I2SCFG, SPI_I2SCFG_I2SCFG_MSK, ALD_I2S_MASTER_RECEIVE << SPI_I2SCFG_I2SCFG_POSS);
if (hperh->init.mck_en)
MODIFY_REG(hperh->perh->I2SPR, SPI_I2SPR_MCKOE_MSK, hperh->init.mck_en << SPI_I2SPR_MCKOE_POS);
if (READ_BIT(hperh->perh->I2SCFG, SPI_I2SCFG_I2SE_MSK) == 0)
ALD_I2S_ENABLE(hperh);
while (size > 0) {
hperh->perh->DATA = 0xffff;
if (i2s_wait_status(hperh, ALD_I2S_STATUS_RXNE, SET, timeout) != ALD_OK) {
ALD_I2S_DISABLE(hperh);
hperh->state = ALD_I2S_STATE_READY;
return ALD_TIMEOUT;
}
*buf++ = hperh->perh->DATA;
--size;
}
hperh->state = ALD_I2S_STATE_READY;
return ALD_OK;
}
/**
* @brief Wraps up master mode transmission in non blocking mode.
* @param hperh: pointer to a i2s_handle_t structure.
* @param buf: Pointer to data transmitted buffer
* @param size: Amount of data to be sent
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_master_send_by_it(ald_i2s_handle_t *hperh, uint16_t *buf, uint32_t size)
{
assert_param(IS_I2S(hperh->perh));
if (hperh->state != ALD_I2S_STATE_READY)
return ALD_BUSY;
if (buf == NULL || size == 0)
return ALD_ERROR;
hperh->state = ALD_I2S_STATE_BUSY_TX;
hperh->err_code = ALD_I2S_ERROR_NONE;
hperh->tx_buf = buf;
hperh->tx_size = size;
hperh->tx_count = size;
hperh->rx_buf = NULL;
hperh->rx_size = 0;
hperh->rx_count = 0;
WRITE_REG(hperh->perh->ICR, 0xffffffff);
ALD_I2S_DISABLE(hperh);
MODIFY_REG(hperh->perh->I2SCFG, SPI_I2SCFG_I2SCFG_MSK, ALD_I2S_MASTER_TRANSMIT << SPI_I2SCFG_I2SCFG_POSS);
if (hperh->init.mck_en)
MODIFY_REG(hperh->perh->I2SPR, SPI_I2SPR_MCKOE_MSK, hperh->init.mck_en << SPI_I2SPR_MCKOE_POS);
if (READ_BIT(hperh->perh->I2SCFG, SPI_I2SCFG_I2SE_MSK) == 0)
ALD_I2S_ENABLE(hperh);
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_TXE, ENABLE);
return ALD_OK;
}
/**
* @brief Master mode receives an amount of data in non blocking mode
* @param hperh: Pointer to a i2s_handle_t structure.
* @param buf: Pointer to data received buffer
* @param size: Amount of data to be sent
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_master_recv_by_it(ald_i2s_handle_t *hperh, uint16_t *buf, uint32_t size)
{
assert_param(IS_I2S(hperh->perh));
if (hperh->state != ALD_I2S_STATE_READY)
return ALD_BUSY;
if (buf == NULL || size == 0)
return ALD_ERROR;
hperh->state = ALD_I2S_STATE_BUSY_TX_RX;
hperh->err_code = ALD_I2S_ERROR_NONE;
hperh->rx_buf = buf;
hperh->rx_size = size;
hperh->rx_count = size;
hperh->tx_buf = 0;
hperh->tx_size = size;
hperh->tx_count = size;
WRITE_REG(hperh->perh->ICR, 0xffffffff);
ALD_I2S_DISABLE(hperh);
MODIFY_REG(hperh->perh->I2SCFG, SPI_I2SCFG_I2SCFG_MSK, ALD_I2S_MASTER_RECEIVE << SPI_I2SCFG_I2SCFG_POSS);
if (hperh->init.mck_en)
MODIFY_REG(hperh->perh->I2SPR, SPI_I2SPR_MCKOE_MSK, hperh->init.mck_en << SPI_I2SPR_MCKOE_POS);
if (READ_BIT(hperh->perh->I2SCFG, SPI_I2SCFG_I2SE_MSK) == 0)
ALD_I2S_ENABLE(hperh);
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_RXTH, ENABLE);
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_TXE, ENABLE);
return ALD_OK;
}
/**
* @brief Master mode transmit an amount of data used dma channel
* @param hperh: Pointer to a i2s_handle_t structure.
* @param buf: Pointer to data buffer
* @param size: Amount of data to be sent
* @param channel: DMA channel as I2S transmit
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_master_send_by_dma(ald_i2s_handle_t *hperh, uint16_t *buf, uint32_t size, uint8_t channel)
{
assert_param(IS_I2S(hperh->perh));
if (hperh->state != ALD_I2S_STATE_READY)
return ALD_BUSY;
if (buf == NULL || size == 0)
return ALD_ERROR;
hperh->state = ALD_I2S_STATE_BUSY_TX;
hperh->err_code = ALD_I2S_ERROR_NONE;
hperh->tx_buf = buf;
hperh->tx_size = size;
hperh->tx_count = size;
hperh->rx_buf = NULL;
hperh->rx_size = 0;
hperh->rx_count = 0;
WRITE_REG(hperh->perh->ICR, 0xffffffff);
ALD_I2S_DISABLE(hperh);
MODIFY_REG(hperh->perh->I2SCFG, SPI_I2SCFG_I2SCFG_MSK, ALD_I2S_MASTER_TRANSMIT << SPI_I2SCFG_I2SCFG_POSS);
if (hperh->init.mck_en)
MODIFY_REG(hperh->perh->I2SPR, SPI_I2SPR_MCKOE_MSK, hperh->init.mck_en << SPI_I2SPR_MCKOE_POS);
hperh->hdmatx.cplt_tc_arg = (void *)hperh;
hperh->hdmatx.cplt_tc_cbk = i2s_dma_send_cplt;
/* Configure I2S DMA transmit */
ald_dma_config_struct(&(hperh->hdmatx.config));
hperh->hdmatx.perh = DMA;
hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_HALFWORD;
hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_HALFWORD;
hperh->hdmatx.config.src = (void *)buf;
hperh->hdmatx.config.dst = (void *)&hperh->perh->DATA;
hperh->hdmatx.config.size = size;
hperh->hdmatx.config.src_inc = ALD_DMA_DATA_INC_ENABLE;
hperh->hdmatx.config.dst_inc = ALD_DMA_DATA_INC_DISABLE;
hperh->hdmatx.config.msel = ALD_DMA_MSEL_SPI0;
hperh->hdmatx.config.msigsel = ALD_DMA_MSIGSEL_SPI_TXEMPTY;
hperh->hdmatx.config.channel = channel;
ald_dma_config_basic(&(hperh->hdmatx));
ald_dma_interrupt_config(channel, ALD_DMA_IT_FLAG_TC, ENABLE);
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_TX, ENABLE);
if (READ_BIT(hperh->perh->I2SCFG, SPI_I2SCFG_I2SE_MSK) == 0)
ALD_I2S_ENABLE(hperh);
return ALD_OK;
}
/**
* @brief Master mode receive an amount of data used dma channel
* @param hperh: Pointer to a i2s_handle_t structure.
* @param buf: Pointer to data buffer
* @param size: Amount of data to be sent
* @param dma_ch: DMA channel for I2S receive
* @param _dma_ch: DMA channel for sending clock
* @retval Status, see @ref ald_status_t.
*/
ald_status_t ald_i2s_master_recv_by_dma(ald_i2s_handle_t *hperh, uint16_t *buf, uint32_t size, uint8_t dma_ch, uint8_t _dma_ch)
{
assert_param(IS_I2S(hperh->perh));
if (hperh->state != ALD_I2S_STATE_READY)
return ALD_BUSY;
if (buf == NULL || size == 0)
return ALD_ERROR;
hperh->state = ALD_I2S_STATE_BUSY_RX;
hperh->err_code = ALD_I2S_ERROR_NONE;
hperh->rx_buf = buf;
hperh->rx_size = size;
hperh->rx_count = size;
hperh->tx_buf = NULL;
hperh->tx_size = 0;
hperh->tx_count = 0;
buf[size - 1] = 0xFFFF;
WRITE_REG(hperh->perh->ICR, 0xffffffff);
ALD_I2S_DISABLE(hperh);
MODIFY_REG(hperh->perh->I2SCFG, SPI_I2SCFG_I2SCFG_MSK, ALD_I2S_MASTER_RECEIVE << SPI_I2SCFG_I2SCFG_POSS);
if (hperh->init.mck_en)
MODIFY_REG(hperh->perh->I2SPR, SPI_I2SPR_MCKOE_MSK, hperh->init.mck_en << SPI_I2SPR_MCKOE_POS);
hperh->hdmatx.cplt_tc_arg = (void *)hperh;
hperh->hdmatx.cplt_tc_cbk = i2s_dma_send_cplt;
ald_dma_config_struct(&(hperh->hdmatx.config));
hperh->hdmatx.perh = DMA;
hperh->hdmatx.config.src_data_width = ALD_DMA_DATA_SIZE_HALFWORD;
hperh->hdmatx.config.dst_data_width = ALD_DMA_DATA_SIZE_HALFWORD;
hperh->hdmatx.config.src = (void *)&buf[size - 1];
hperh->hdmatx.config.dst = (void *)&hperh->perh->DATA;
hperh->hdmatx.config.size = size;
hperh->hdmatx.config.src_inc = ALD_DMA_DATA_INC_DISABLE;
hperh->hdmatx.config.dst_inc = ALD_DMA_DATA_INC_DISABLE;
hperh->hdmatx.config.msel = ALD_DMA_MSEL_SPI0;
hperh->hdmatx.config.msigsel = ALD_DMA_MSIGSEL_SPI_TXEMPTY;
hperh->hdmatx.config.channel = _dma_ch;
ald_dma_config_basic(&(hperh->hdmatx));
ald_dma_interrupt_config(_dma_ch, ALD_DMA_IT_FLAG_TC, ENABLE);
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_TX, ENABLE);
hperh->hdmarx.cplt_tc_arg = (void *)hperh;
hperh->hdmarx.cplt_tc_cbk = i2s_dma_recv_cplt;
/* Configure DMA Receive */
ald_dma_config_struct(&(hperh->hdmarx.config));
hperh->hdmarx.perh = DMA;
hperh->hdmarx.config.src_data_width = ALD_DMA_DATA_SIZE_HALFWORD;
hperh->hdmarx.config.dst_data_width = ALD_DMA_DATA_SIZE_HALFWORD;
hperh->hdmarx.config.src = (void *)&hperh->perh->DATA;
hperh->hdmarx.config.dst = (void *)buf;
hperh->hdmarx.config.size = size;
hperh->hdmarx.config.src_inc = ALD_DMA_DATA_INC_DISABLE;
hperh->hdmarx.config.dst_inc = ALD_DMA_DATA_INC_ENABLE;
hperh->hdmarx.config.msel = ALD_DMA_MSEL_SPI0;
hperh->hdmarx.config.msigsel = ALD_DMA_MSIGSEL_SPI_RNR;
hperh->hdmarx.config.channel = dma_ch;
ald_dma_config_basic(&(hperh->hdmarx));
ald_dma_interrupt_config(dma_ch, ALD_DMA_IT_FLAG_TC, ENABLE);
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_RX, ENABLE);
if (READ_BIT(hperh->perh->I2SCFG, SPI_I2SCFG_I2SE_MSK) == 0)
ALD_I2S_ENABLE(hperh);
return ALD_OK;
}
/**
* @brief Pauses the DMA Transfer.
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval Status
*/
ald_status_t ald_i2s_dma_pause(ald_i2s_handle_t *hperh)
{
assert_param(IS_I2S(hperh->perh));
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_TX, DISABLE);
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_RX, DISABLE);
return ALD_OK;
}
/**
* @brief Resumes the DMA Transfer.
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval Status
*/
ald_status_t ald_i2s_dma_resume(ald_i2s_handle_t *hperh)
{
assert_param(IS_I2S(hperh->perh));
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_TX, ENABLE);
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_RX, ENABLE);
return ALD_OK;
}
/**
* @brief Stops the DMA Transfer.
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval Status
*/
ald_status_t ald_i2s_dma_stop(ald_i2s_handle_t *hperh)
{
assert_param(IS_I2S(hperh->perh));
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_TX, DISABLE);
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_RX, DISABLE);
hperh->state = ALD_I2S_STATE_READY;
return ALD_OK;
}
/**
* @}
*/
/** @defgroup I2S_Public_Functions_Group3 Control functions
* @brief I2S Control functions
*
* @verbatim
===============================================================================
##### Peripheral Control functions #####
===============================================================================
[..]
This subsection provides a set of functions allowing to control the I2S.
(+) Handle interrupt about I2S module. The ald_i2s_irq_handler() function must
be invoked by I2S-IRQ function.
(+) Configure the interrupt DISABLE/ENABLE.
(+) Configure the DMA request.
(+) Get interrupt source status.
(+) Get interrupt flag status.
(+) Clear interrupt flag
@endverbatim
* @{
*/
/**
* @brief This function handles I2S interrupt request.
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval None
*/
void ald_i2s_irq_handler(ald_i2s_handle_t *hperh)
{
if (ald_i2s_get_mask_flag_status(hperh, ALD_I2S_IF_RXTH) == SET) {
ald_i2s_clear_flag_status(hperh, ALD_I2S_IF_RXTH);
if ((hperh->state == ALD_I2S_STATE_BUSY_TX_RX) || (hperh->state == ALD_I2S_STATE_BUSY_RX))
__i2s_recv_by_it(hperh);
}
if (ald_i2s_get_mask_flag_status(hperh, ALD_I2S_IF_TXE) == SET) {
ald_i2s_clear_flag_status(hperh, ALD_I2S_IF_TXE);
if (hperh->state == ALD_I2S_STATE_BUSY_TX)
__i2s_send_by_it(hperh);
else if (hperh->state == ALD_I2S_STATE_BUSY_TX_RX)
__i2s_tx_recv_by_it(hperh);
}
if (hperh->err_code != ALD_I2S_ERROR_NONE) {
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_RXTH, DISABLE);
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_TXE, DISABLE);
hperh->state = ALD_I2S_STATE_READY;
if (hperh->err_cbk)
hperh->err_cbk(hperh);
}
return;
}
/**
* @brief Enables or disables the specified I2S interrupts.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param it: Specifies the I2S interrupt sources to be enabled or disabled.
* This parameter can be one of the @ref i2s_it_t.
* @param state: New status
* - ENABLE
* - DISABLE
* @retval None
*/
void ald_i2s_interrupt_config(ald_i2s_handle_t *hperh, ald_i2s_it_t it, type_func_t state)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_IT(it));
assert_param(IS_FUNC_STATE(state));
if (state == ENABLE)
hperh->perh->IER = (uint32_t)it;
else
hperh->perh->IDR = (uint32_t)it;
return;
}
/**
* @brief Enables or disables the dma request.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param req: Specifies the I2S dma request sources to be enabled or disabled.
* This parameter can be one of the @ref i2s_dma_req_t.
* @param state: New status
* - ENABLE
* - DISABLE
* @retval None
*/
void ald_i2s_dma_req_config(ald_i2s_handle_t *hperh, ald_i2s_dma_req_t req, type_func_t state)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_DMA_REQ(req));
assert_param(IS_FUNC_STATE(state));
if (state == ENABLE) {
if (req == ALD_I2S_DMA_REQ_TX)
SET_BIT(hperh->perh->CON2, SPI_CON2_TXDMA_MSK);
else
SET_BIT(hperh->perh->CON2, SPI_CON2_RXDMA_MSK);
}
else {
if (req == ALD_I2S_DMA_REQ_TX)
CLEAR_BIT(hperh->perh->CON2, SPI_CON2_TXDMA_MSK);
else
CLEAR_BIT(hperh->perh->CON2, SPI_CON2_RXDMA_MSK);
}
return;
}
/** @brief Check whether the specified I2S flag is set or not.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param status: specifies the flag to check.
* This parameter can be one of the @ref i2s_status_t.
* @retval Status
* - SET
* - RESET
*/
flag_status_t ald_i2s_get_status(ald_i2s_handle_t *hperh, ald_i2s_status_t status)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_STATUS(status));
if (hperh->perh->STAT & status)
return SET;
return RESET;
}
/**
* @brief Checks whether the specified I2S interrupt has occurred or not.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param it: Specifies the I2S interrupt source to check.
* This parameter can be one of the @ref i2s_it_t.
* @retval Status
* - SET
* - RESET
*/
it_status_t ald_i2s_get_it_status(ald_i2s_handle_t *hperh, ald_i2s_it_t it)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_IT(it));
if (hperh->perh->IVS & it)
return SET;
return RESET;
}
/** @brief Check whether the specified I2S interrupt flag is set or not.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param flag: specifies the flag to check.
* This parameter can be one of the @ref i2s_flag_t.
* @retval Status
* - SET
* - RESET
*/
flag_status_t ald_i2s_get_flag_status(ald_i2s_handle_t *hperh, ald_i2s_flag_t flag)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_IF(flag));
if (hperh->perh->RIF & flag)
return SET;
return RESET;
}
/** @brief Check whether the specified I2S interrupt flag is set or not.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param flag: specifies the flag to check.
* This parameter can be one of the @ref i2s_flag_t.
* @retval Status
* - SET
* - RESET
*/
flag_status_t ald_i2s_get_mask_flag_status(ald_i2s_handle_t *hperh, ald_i2s_flag_t flag)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_IF(flag));
if (hperh->perh->IFM & flag)
return SET;
return RESET;
}
/** @brief Clear the specified I2S interrupt flags.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param flag: specifies the flag to check.
* This parameter can be one of the @ref i2s_flag_t.
* @retval None
*/
void ald_i2s_clear_flag_status(ald_i2s_handle_t *hperh, ald_i2s_flag_t flag)
{
assert_param(IS_I2S(hperh->perh));
assert_param(IS_I2S_IF(flag));
hperh->perh->ICR = flag;
return;
}
/**
* @}
*/
/** @defgroup I2S_Public_Functions_Group4 Peripheral State and Errors functions
* @brief I2S State and Errors functions
*
* @verbatim
===============================================================================
##### Peripheral State and Errors functions #####
===============================================================================
[..]
This subsection provides a set of functions allowing to control the I2S.
(+) ald_i2s_get_state() API can check in run-time the state of the I2S peripheral
(+) ald_i2s_get_error() check in run-time Errors occurring during communication
@endverbatim
* @{
*/
/**
* @brief Returns the I2S state.
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval Status, see @ref i2s_state_t.
*/
ald_i2s_state_t ald_i2s_get_state(ald_i2s_handle_t *hperh)
{
assert_param(IS_I2S(hperh->perh));
return hperh->state;
}
/**
* @brief Return the I2S error code
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval I2S Error Code
*/
uint32_t ald_i2s_get_error(ald_i2s_handle_t *hperh)
{
assert_param(IS_I2S(hperh->perh));
return hperh->err_code;
}
/**
* @}
*/
/**
* @}
*/
/** @defgroup I2S_Private_Functions I2S Private Functions
* @brief I2S Private functions
* @{
*/
/**
* @brief This function wait I2S status until timeout.
* @param hperh: Pointer to a i2s_handle_t structure.
* @param flag: specifies the I2S flag to check.
* @param status: The new Flag status (SET or RESET).
* @param timeout: Timeout duration
* @retval Status, see @ref ald_status_t.
*/
static ald_status_t i2s_wait_status(ald_i2s_handle_t *hperh, ald_i2s_status_t flag, flag_status_t status, uint32_t timeout)
{
uint32_t tick = ald_get_tick();
assert_param(timeout > 0);
while ((ald_i2s_get_status(hperh, flag)) != status) {
if (((ald_get_tick()) - tick) > timeout) {
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_TXE, DISABLE);
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_RXTH, DISABLE);
return ALD_TIMEOUT;
}
}
return ALD_OK;
}
/**
* @brief handle program when an tx empty interrupt flag arrived in non block mode
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval None.
*/
static void __i2s_send_by_it(ald_i2s_handle_t *hperh)
{
int cnt = 8000;
if (hperh->tx_count == 0) {
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_TXE, DISABLE);
hperh->state = ALD_I2S_STATE_READY;
while ((hperh->perh->STAT & SPI_STAT_BUSY_MSK) && (--cnt));
if (cnt == 0) {
if (hperh->err_cbk)
hperh->err_cbk(hperh);
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_RXTH, DISABLE);
return;
}
if (hperh->tx_cplt_cbk)
hperh->tx_cplt_cbk(hperh);
return;
}
hperh->side = READ_BITS(hperh->perh->STAT, SPI_STAT_CHSIDE_MSK, SPI_STAT_CHSIDE_POS);
hperh->perh->DATA = *hperh->tx_buf++;
--hperh->tx_count;
return;
}
/**
* @brief handle program when an rx no empty interrupt flag arrived in non block mode
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval None.
*/
static void __i2s_recv_by_it(ald_i2s_handle_t *hperh)
{
while (READ_BITS(hperh->perh->STAT, SPI_STAT_RXFLV_MSK, SPI_STAT_RXFLV_POSS)) {
*(hperh->rx_buf++) = hperh->perh->DATA;
--hperh->rx_count;
}
if (hperh->rx_count == 0) {
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_RXTH, DISABLE);
hperh->state = ALD_I2S_STATE_READY;
if (hperh->rx_cplt_cbk)
hperh->rx_cplt_cbk(hperh);
}
return;
}
/**
* @brief handle program when an rx no empty interrupt flag arrived in non block mode
* @param hperh: Pointer to a i2s_handle_t structure.
* @retval None.
*/
static void __i2s_tx_recv_by_it(ald_i2s_handle_t *hperh)
{
if (hperh->tx_count != 0) {
ald_i2s_clear_flag_status(hperh, ALD_I2S_IF_TXE);
hperh->perh->DATA = 0xffff;
--hperh->tx_count;
if (hperh->tx_count == 0)
ald_i2s_interrupt_config(hperh, ALD_I2S_IT_TXE, DISABLE);
}
}
/**
* @brief DMA I2S transmit process complete callback.
* @param arg: Pointer to a void structure.
* @retval None
*/
static void i2s_dma_send_cplt(void *arg)
{
int cnt = 8000;
ald_i2s_handle_t *hperh = (ald_i2s_handle_t *)arg;
hperh->tx_count = 0;
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_TX, DISABLE);
hperh->state = ALD_I2S_STATE_READY;
while ((hperh->perh->STAT & SPI_STAT_BUSY_MSK) && (--cnt));
if (cnt == 0)
hperh->err_code |= ALD_I2S_ERROR_FLAG;
if (hperh->err_code == ALD_I2S_ERROR_NONE) {
if (hperh->tx_cplt_cbk)
hperh->tx_cplt_cbk(hperh);
}
else {
if (hperh->err_cbk)
hperh->err_cbk(hperh);
}
return;
}
/**
* @brief DMA I2S receive process complete callback.
* @param arg: Pointer to a void structure.
* @retval None
*/
static void i2s_dma_recv_cplt(void *arg)
{
ald_i2s_handle_t *hperh = (ald_i2s_handle_t *)arg;
hperh->rx_count = 0;
ald_i2s_dma_req_config(hperh, ALD_I2S_DMA_REQ_RX, DISABLE);
hperh->state = ALD_I2S_STATE_READY;
if (hperh->err_code == ALD_I2S_ERROR_NONE) {
if (hperh->rx_cplt_cbk)
hperh->rx_cplt_cbk(hperh);
}
else {
if (hperh->err_cbk)
hperh->err_cbk(hperh);
}
return;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/