
/******************************************************************************
*@file  : spl_i2s.c
*@brief : I2S SPL module driver.
******************************************************************************/

#include "acm32g103_spl_conf.h" 


/******************************************************************************
*@brief : Initializes the I2Sx peripheral according to the specified 
*         parameters in the I2S_InitStruct.
*@param : I2Sx: where x can be 1 or 2.
*@param : I2S_InitStruct: pointer to an I2S_InitTypeDef structure that contains the
*                         configuration information for the specified I2S peripheral.
*@return: None.
******************************************************************************/
void I2S_Init(I2S_TypeDef *I2Sx, I2S_InitTypeDef *I2S_InitStruct)
{
    uint32_t Freq;
    uint32_t div;
    
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_MODE(I2S_InitStruct->Mode));
    assert_param(IS_I2S_STANDARD(I2S_InitStruct->Standard));
    assert_param(IS_I2S_DATAFORMAT(I2S_InitStruct->DataFormat));
    assert_param(IS_I2S_FUNCTIONAL_STATE(I2S_InitStruct->MCLKOutput));
    assert_param(IS_I2S_CLOCKPOLARITY(I2S_InitStruct->ClockPolarity));
    
    assert_param(I2S_InitStruct->AudioFreq != 0u);
    
    Freq = RCC_GetPCLK1Freq();
    
    if (I2S_InitStruct->MCLKOutput == DISABLE)
    {
        if (I2S_InitStruct->DataFormat == I2S_DATAFORMAT_16B_EXTENDED_TO_16B)
            Freq = Freq >> 5;
        else
            Freq = Freq >> 6;
    }
    else
    {
        Freq = Freq >> 8;
    }
    assert_param(Freq >= I2S_InitStruct->AudioFreq);
    div = Freq / I2S_InitStruct->AudioFreq;
    if (div & 1u)
    {
        div = (div - 1) >> 1;
        assert_param(div <= 0x1FFU);
        div += I2S_PR_OF;
    }
    else
    {
        div = div >> 1;
        assert_param(div <= 0x1FFU);
    }
    
    /* Disable I2S */
    I2S_Cmd(I2Sx, DISABLE);
    
    I2S_Reset(I2Sx);
    
    I2Sx->CR = I2S_InitStruct->Mode | I2S_InitStruct->Standard | \
               I2S_InitStruct->DataFormat | I2S_InitStruct->ClockPolarity;
    
    if (I2S_InitStruct->MCLKOutput != DISABLE)
        I2Sx->PR = I2S_PR_MCKOE | div;
    else
        I2Sx->PR = div;
}

/******************************************************************************
*@brief : De-initialize the I2Sx peripheral registers to their default reset values.
*@param : I2Sx: where x can be 1 or 2.
*@return: None
******************************************************************************/
void I2S_DeInit(I2S_TypeDef *I2Sx)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    
    if (I2Sx == I2S1)
    {
        __RCC_I2S1_CLK_ENABLE();
        __RCC_I2S1_CLK_DISABLE();
    }
    else if (I2Sx == I2S2)
    {
        __RCC_I2S2_CLK_ENABLE();
        __RCC_I2S2_CLK_DISABLE();
    }
}

/******************************************************************************
*@brief : Fills each I2S_InitStruct member with its default value.
*@param : I2S_InitStruct: pointer to a I2S_InitTypeDef structure that contains
*                         the configuration information for I2S module.
*@return: None
******************************************************************************/
void I2S_StructInit(I2S_InitTypeDef *I2S_InitStruct)
{
    /* Check the parameters */
    assert_param(I2S_InitStruct != NULL);
    
    I2S_InitStruct->Mode = I2S_MODE_MASTER_TX;
    I2S_InitStruct->Standard = I2S_STANDARD_PHILIPS;
    I2S_InitStruct->DataFormat = I2S_DATAFORMAT_16B_EXTENDED_TO_16B;
    I2S_InitStruct->MCLKOutput = ENABLE;
    I2S_InitStruct->ClockPolarity = I2S_CLOCKPOLARITY_LOW;
    I2S_InitStruct->AudioFreq = 16000;
}

/******************************************************************************
*@brief : Reset the I2Sx peripheral registers to their default reset values.
*@param : I2Sx: where x can be 1 or 2.
*@return: None.
******************************************************************************/
void I2S_Reset(I2S_TypeDef *I2Sx)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));

    if (I2Sx == I2S1)
    {
        __RCC_I2S1_RESET();
    }
    else if (I2Sx == I2S2)
    {
        __RCC_I2S2_RESET();
    }
}

/******************************************************************************
*@brief : Enables or disables the specified I2S peripheral.
*@param : I2Sx: where x can be 1 or 2.
*@param : NewState: new state of the I2Sx peripheral.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void I2S_Cmd(I2S_TypeDef* I2Sx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        I2Sx->CR |= I2S_CR_EN;
    else
        I2Sx->CR &= ~I2S_CR_EN;
}

/******************************************************************************
*@brief : Enables or disables the I2Sx TX DMA interface.
*@param : I2Sx: where x can be 1 or 2.
*@param : NewState: new state of the selected I2S TX DMA transfer request.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void I2S_TxDMACmd(I2S_TypeDef* I2Sx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        I2Sx->DIER |= I2S_DIER_TXDMATEN;
    else
        I2Sx->DIER &= ~I2S_DIER_TXDMATEN;
}

/******************************************************************************
*@brief : Enables or disables the I2Sx RX DMA interface.
*@param : I2Sx: where x can be 1 or 2.
*@param : NewState: new state of the selected I2S RX DMA transfer request.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void I2S_RxDMACmd(I2S_TypeDef* I2Sx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        I2Sx->DIER |= I2S_DIER_RXDMAREN;
    else
        I2Sx->DIER &= ~I2S_DIER_RXDMAREN;
}

/******************************************************************************
*@brief : Configures the operation mode.
*@param : I2Sx: where x can be 1 or 2.
*@param : Mode: specifies the I2Sx operation mode.
*               This parameter can be one of @ref I2S_Mode.
*                 @arg I2S_MODE_SLAVE_TX: Slave transmission mode.
*                 @arg I2S_MODE_SLAVE_RX: Slave receive mode.
*                 @arg I2S_MODE_MASTER_TX: Master transmission mode.
*                 @arg I2S_MODE_MASTER_RX: Master receive mode.
*@return: None
******************************************************************************/
void I2S_ConfigMode(I2S_TypeDef* I2Sx, uint32_t Mode)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_MODE(Mode));
    
    I2Sx->CR = (I2Sx->CR & ~I2S_CR_MODE) | Mode;
}

/******************************************************************************
*@brief : Configures the I2S standard .
*@param : I2Sx: where x can be 1 or 2.
*@param : Standard: specifies the I2S standard.
*                   This parameter can be one of @ref I2S_Standard.
*                     @arg I2S_STANDARD_PHILIPS: Philips standard.
*                     @arg I2S_STANDARD_MSB: MSB alignment standard.
*                     @arg I2S_STANDARD_LSB: LSB alignment standard.
*                     @arg I2S_STANDARD_PCM_SHORT: PCM Short frame synchronization.
*                     @arg I2S_STANDARD_PCM_LONG: PCM Long frame synchronization.
*@return: None
******************************************************************************/
void I2S_ConfigStandard(I2S_TypeDef* I2Sx, uint32_t Standard)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_STANDARD(Standard));
    
    I2Sx->CR = (I2Sx->CR & ~(I2S_CR_STD | I2S_CR_PCMSMOD)) | Standard;
}

/******************************************************************************
*@brief : Configures the data format.
*@param : I2Sx: where x can be 1 or 2.
*@param : DataFormat: specifies the data format.
*                     This parameter can be one of @ref I2S_DataFormat.
*                       @arg I2S_DATAFORMAT_16B_EXTENDED_TO_16B: 16-bit channel length, 16-bit data length.
*                       @arg I2S_DATAFORMAT_16B_EXTENDED_TO_32B: 32-bit channel length, 16-bit data length.
*                       @arg I2S_DATAFORMAT_24B_EXTENDED_TO_32B: 32-bit channel length, 24-bit data length.
*                       @arg I2S_DATAFORMAT_32B_EXTENDED_TO_32B: 32-bit channel length, 32-bit data length.
*@return: None
******************************************************************************/
void I2S_ConfigDataFormat(I2S_TypeDef* I2Sx, uint32_t DataFormat)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_DATAFORMAT(DataFormat));
    
    I2Sx->CR = (I2Sx->CR & ~(I2S_CR_CHLEN | I2S_CR_DTLEN)) | DataFormat;
}

/******************************************************************************
*@brief : Configures clock polarity in idle state.
*@param : I2Sx: where x can be 1 or 2.
*@param : ClockPolarity: specifies clock polarity in idle state.
*                        This parameter can be one of @ref I2S_ClockPolarity.
*                          @arg I2S_CLOCKPOLARITY_LOW: low level in idle state.
*                          @arg I2S_CLOCKPOLARITY_HIGH: high level in idle state.
*@return: None
******************************************************************************/
void I2S_ConfigClockPolarity(I2S_TypeDef* I2Sx, uint32_t ClockPolarity)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_CLOCKPOLARITY(ClockPolarity));
    
    I2Sx->CR = (I2Sx->CR & ~I2S_CR_CKPL) | ClockPolarity;
}

/******************************************************************************
*@brief : Enables or disables the MCLK output.
*@param : I2Sx: where x can be 1 or 2.
*@param : NewState: new state of the MCLK output.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void I2S_MCLKOutputCmd(I2S_TypeDef* I2Sx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        I2Sx->PR |= I2S_PR_MCKOE;
    else
        I2Sx->PR &= ~I2S_PR_MCKOE;
}

/******************************************************************************
*@brief : Configures the audio frequency.
*@param : I2Sx: where x can be 1 or 2.
*@param : AudioFreq: the audio frequency.
*@param : MCLKOutput: the state of the MCLK output.
*                     This parameter can be: ENABLE or DISABLE.
*@param : DataFormat: specifies the data format.
*                     This parameter can be one of @ref I2S_DataFormat.
*                       @arg I2S_DATAFORMAT_16B_EXTENDED_TO_16B: 16-bit channel length, 16-bit data length.
*                       @arg I2S_DATAFORMAT_16B_EXTENDED_TO_32B: 32-bit channel length, 16-bit data length.
*                       @arg I2S_DATAFORMAT_24B_EXTENDED_TO_32B: 32-bit channel length, 24-bit data length.
*                       @arg I2S_DATAFORMAT_32B_EXTENDED_TO_32B: 32-bit channel length, 32-bit data length.
*@return: None
******************************************************************************/
void I2S_ConfigAudioFreq(I2S_TypeDef* I2Sx, uint32_t AudioFreq, FunctionalState MCLKOutput, uint32_t DataFormat)
{
    uint32_t Freq;
    uint32_t div;
    
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(AudioFreq != 0u);
    assert_param(IS_I2S_FUNCTIONAL_STATE(MCLKOutput));
    assert_param(IS_I2S_DATAFORMAT(DataFormat));
    
    Freq = RCC_GetHCLKFreq();
    
    if (MCLKOutput == DISABLE)
    {
        if (DataFormat == I2S_DATAFORMAT_16B_EXTENDED_TO_16B)
            Freq = Freq >> 5;
        else
            Freq = Freq >> 6;
    }
    else
    {
        Freq = Freq >> 8;
    }
    assert_param(Freq >= AudioFreq);
    div = Freq / AudioFreq;
    if (div & 1u)
    {
        div = (div - 1) >> 1;
        assert_param(div <= 0x1FFU);
        div += I2S_PR_OF;
    }
    else
    {
        div = div >> 1;
        assert_param(div <= 0x1FFU);
    }
    
    I2Sx->PR = (I2Sx->PR & ~(I2S_PR_OF | I2S_PR_DIV)) | div;
}



/******************************************************************************
*@brief : Enables or disables the specified I2Sx interrupts.
*@param : I2Sx: where x can be 1 or 2.
*@param : IT: specifies the I2Sx interrupts sources to be enabled or disabled.
*             This parameter can be combination of @ref I2S_IT.
*               @arg I2S_IT_TXE: tx buffer empty interrupt source.
*               @arg I2S_IT_RXNE: rx buffer no empty interrupt source.
*               @arg I2S_IT_ERR: error interrupt source.
*@param : NewState: new state of the interrupts sources.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void I2S_ITConfig(I2S_TypeDef* I2Sx, uint32_t IT, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_ALL_IT(IT));
    assert_param(IS_I2S_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        I2Sx->DIER |= IT;
    else
        I2Sx->DIER &= ~IT;
}

/******************************************************************************
*@brief : Checks whether the specified I2Sx flag is set or not.
*@param : I2Sx: where x can be 1 or 2.
*@param : Flag: specifies the flag to check.
*               This parameter can be one of @ref I2S_Flags.
*                 @arg I2S_FLAG_FE: frame error flag.
*                 @arg I2S_FLAG_BUSY: communication busy flag.
*                 @arg I2S_FLAG_RXOVE: receive overload error flag.
*                 @arg I2S_FLAG_TXUDE: send underload error flag.
*                 @arg I2S_FLAG_CHF: channel flag.
*                 @arg I2S_FLAG_TXE: tx buffer empty flag.
*                 @arg I2S_FLAG_RXNE: rx buffer no empty flag.
*@return: None
******************************************************************************/
FlagStatus I2S_GetFlagStatus(I2S_TypeDef* I2Sx, uint32_t Flag)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_ALL_FLAG(Flag));
    
    if (I2Sx->SR & Flag)
        return (SET);
    else
        return (RESET);
}

/******************************************************************************
*@brief : Clears the I2Sx's flag.
*@param : I2Sx: where x can be 1 or 2.
*@param : Flag: specifies the flag to clear.
*               This parameter can be one of @ref I2S_Flags.
*                 @arg I2S_FLAG_FE: frame error flag.
*@return: None
******************************************************************************/
void I2S_ClearFlag(I2S_TypeDef* I2Sx, uint32_t Flag)
{
    /* Check the parameters */
    assert_param(IS_I2S_ALL_PERIPH(I2Sx));
    assert_param(IS_I2S_CLEAR_FLAG(Flag));
    
    I2Sx->SR &= ~Flag;
}




