/* ****************************************************************************** * @file driver_i2s.c * @author FreqChip Firmware Team * @version V1.0.0 * @date 2021 * @brief I2S module driver. * This file provides firmware functions to manage the * Inter¡ªIC Sound (I2S) peripheral ****************************************************************************** * @attention * * Copyright (c) 2021 FreqChip. * All rights reserved. ****************************************************************************** */ #include "fr30xx.h" /************************************************************************************ * @fn i2s_IRQHandler * * @brief Handle I2S interrupt request. * * @param huart: I2S handle. */ //void i2s_IRQHandler(I2S_HandleTypeDef *hi2s) //{ // /* Format 20Bit/24Bit/32bit */ // if (hi2s->Init.DataFormat > I2S_DATA_FORMAT_16BIT) // { // /* TxFIFO half full interrupt enable */ // if (__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_TX_FIFOS_HALF_FULL)) // { // /* FIFO half full */ // if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_FIFOS_HALF_FULL)) // { // while(!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_FIFOS_FULL)) // { // /* right channel*/ // if (hi2s->u32_TxCount % 2) // { // hi2s->I2Sx->DATA_R = hi2s->p_TxData[hi2s->u32_TxCount++]; // } // /* left channel*/ // else // { // hi2s->I2Sx->DATA_L = hi2s->p_TxData[hi2s->u32_TxCount++]; // } // // if (hi2s->u32_TxCount >= hi2s->u32_TxSize) // { // __I2S_INT_DISABLE(hi2s->I2Sx, I2S_TX_FIFOS_HALF_FULL); // // hi2s->b_TxBusy = false; // // break; // } // } // } // } // /* Rx FIFO full interrupt enable */ // if(__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_RX_FIFOS_HALF_FULL)) // { // /* FIFO half full */ // if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_FIFOS_HALF_FULL)) // { // while(!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_FIFOS_EMPTY)) // { // /* right channel*/ // if (hi2s->u32_RxCount % 2) // { // hi2s->p_RxData[hi2s->u32_RxCount++] = hi2s->I2Sx->DATA_R; // } // /* left channel*/ // else // { // hi2s->p_RxData[hi2s->u32_RxCount++] = hi2s->I2Sx->DATA_L; // } // // if(hi2s->u32_RxCount >= hi2s->u32_RxSize) // { // __I2S_INT_DISABLE(hi2s->I2Sx, I2S_RX_FIFOS_HALF_FULL); // // hi2s->b_RxBusy = false; // break; // } // } // } // } // } // /* Format 8Bit/16Bit */ // else // { // /* TxFIFO half full interrupt enable */ // if (__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_TX_L_FIFO_HALF_FULL)) // { // /* FIFO half full */ // if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_L_FIFO_HALF_FULL)) // { // while(!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_L_FIFO_FULL)) // { // hi2s->I2Sx->DATA_L = hi2s->p_TxData[hi2s->u32_TxCount++]; // // if (hi2s->u32_TxCount >= hi2s->u32_TxSize) // { // __I2S_INT_DISABLE(hi2s->I2Sx, I2S_TX_L_FIFO_HALF_FULL); // // hi2s->b_TxBusy = false; // // break; // } // } // } // } // /* Rx FIFO full interrupt enable */ // if(__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_RX_L_FIFO_HALF_FULL)) // { // /* FIFO half full */ // if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_L_FIFO_HALF_FULL)) // { // while(!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_L_FIFO_EMPTY)) // { // hi2s->p_RxData[hi2s->u32_RxCount++] = hi2s->I2Sx->DATA_L; // // if(hi2s->u32_RxCount >= hi2s->u32_RxSize) // { // __I2S_INT_DISABLE(hi2s->I2Sx, I2S_RX_L_FIFO_HALF_FULL); // // hi2s->b_RxBusy = false; // break; // } // } // } // } // } //} void i2s_IRQHandler(I2S_HandleTypeDef *hi2s) { /* Format 20Bit/24Bit/32bit */ if (hi2s->Init.DataFormat > I2S_DATA_FORMAT_16BIT) { /* TxFIFO half full interrupt enable */ if (__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_TX_FIFOS_ALMOST_EMPTY)) { /* FIFO half full */ if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_FIFOS_ALMOST_EMPTY)) { if (hi2s->TxIntCallback) { hi2s->TxIntCallback(hi2s); } else { for (uint32_t i=0; iI2Sx->DATA_L = 0; hi2s->I2Sx->DATA_R = 0; } } } } /* Rx FIFO full interrupt enable */ if(__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_RX_FIFOS_HALF_FULL)) { /* FIFO half full */ if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_FIFOS_HALF_FULL)) { if (hi2s->RxIntCallback) { hi2s->RxIntCallback(hi2s); } else { for (uint32_t i=0; iI2Sx->DATA_L; volatile uint32_t r_data = hi2s->I2Sx->DATA_R; } } } } } /* Format 8Bit/16Bit */ else { /* TxFIFO half full interrupt enable */ if (__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_TX_L_FIFO_ALMOST_EMPTY)) { /* FIFO half full */ if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_L_FIFO_ALMOST_EMPTY)) { if (hi2s->TxIntCallback) { hi2s->TxIntCallback(hi2s); } else { for (uint32_t i=0; iI2Sx->DATA_L = 0; } } } } /* Rx FIFO full interrupt enable */ if(__I2S_INT_IS_ENANLE(hi2s->I2Sx, I2S_RX_L_FIFO_HALF_FULL)) { /* FIFO half full */ if ((__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_L_FIFO_HALF_FULL)) { if (hi2s->RxIntCallback) { hi2s->RxIntCallback(hi2s); } else { for (uint32_t i=0; iI2Sx->DATA_L; } } } } } } /************************************************************************************ * @fn i2s_init * * @brief Initialize the I2S peripheral according to the specified * parameters in the struct_I2SInit_t * * @param hi2s: I2S handle. */ void i2s_init(I2S_HandleTypeDef *hi2s) { hi2s->b_TxBusy = false; hi2s->b_RxBusy = false; /* disable all interrupt */ __I2S_INT_DISABLE(hi2s->I2Sx, I2S_ALL_FIFO_STATUS); /* Enable Rx/Tx FIFO */ __I2S_RxFIFO_EN(hi2s->I2Sx); __I2S_TxFIFO_EN(hi2s->I2Sx); /* Clear Rx/Tx FIFO */ __I2S_RxFIFO_CLR(hi2s->I2Sx); __I2S_TxFIFO_CLR(hi2s->I2Sx); /* left and right channels operate simultaneously */ __I2S_WD_SWAP_HIGH16SIZE_RIGHT(hi2s->I2Sx); /* config mode */ hi2s->I2Sx->CTRL0.MSTSLV = hi2s->Init.Mode; /* Calculate audio frequency */ hi2s->I2Sx->FrmDiv.BCLKDIV = hi2s->Init.BCLKDIV - 1; /* Calculate channel length */ hi2s->I2Sx->FrmDiv.FRMDIV = hi2s->Init.ChannelLength - 1; /* Data length */ hi2s->I2Sx->CTRL1.I2S_DATA_LENGTH = hi2s->Init.DataFormat; switch (hi2s->Init.Standard) { /* philips */ case I2S_STANDARD_PHILIPS: { hi2s->I2Sx->CTRL1.I2S_NORMAL = 1; __I2S_ENABLE(hi2s->I2Sx); }break; /* MSB */ case I2S_STANDARD_MSB: { hi2s->I2Sx->CTRL1.I2S_NORMAL = 0; hi2s->I2Sx->CTRL1.I2S_ADJUST = 0; __I2S_FRAME_INV_ENABLE(hi2s->I2Sx); __I2S_ENABLE(hi2s->I2Sx); }break; /* LSB */ case I2S_STANDARD_LSB: { uint32_t data_length[5] = {8,16,20,24,32}; hi2s->I2Sx->CTRL1.I2S_NORMAL = 0; hi2s->I2Sx->CTRL1.I2S_ADJUST = 1; hi2s->I2Sx->CTRL1.I2SFBOFF = hi2s->Init.ChannelLength - data_length[hi2s->Init.DataFormat]; __I2S_FRAME_INV_ENABLE(hi2s->I2Sx); __I2S_ENABLE(hi2s->I2Sx); }break; /* PCM */ case I2S_STANDARD_PCM: { /* Mono or Stereo select*/ hi2s->I2Sx->PCM_RHYCTRL.PCM_SAMPTYPE = 0; hi2s->I2Sx->PCM_GENCTRL.PCM_MONO_STEREO = 0; /* Frame Synchronization signal */ hi2s->I2Sx->PCM_RHYCTRL.PCM_FSYNCSHP = 0; /* Sample size and slots */ __I2S_PCM_SLOTS_SET(hi2s->I2Sx, 1); __I2S_PCM_SAMPLESIZE_16BIT(hi2s->I2Sx); /* ENABLE PCM */ hi2s->I2Sx->PCM_RHYCTRL.PCM_DOUTCFG = 1; __I2S_PCM_ENABLE(hi2s->I2Sx); }break; default: break; } } /************************************************************************************ * @fn i2s_deinit * * @brief deinit an ongoing i2s. * * @param hi2s: I2S handle. */ void i2s_deinit(I2S_HandleTypeDef *hi2s) { __I2S_PCM_DISABLE(hi2s->I2Sx); __I2S_DISABLE(hi2s->I2Sx); hi2s->b_TxBusy = false; hi2s->b_RxBusy = false; } /************************************************************************************ * @fn i2s_transmit_IT * * @brief Transmit an I2S data in interrupt mode, user should send data in TxCallback with * i2s_send_data function. * * @param huart: I2S handle. */ bool i2s_transmit_IT(I2S_HandleTypeDef *hi2s) { if (hi2s->b_TxBusy) return false; hi2s->b_TxBusy = true; __I2S_L_TxFIFO_EMPTY_LEVEL(hi2s->I2Sx, I2S_FIFO_HALF_DEPTH); __I2S_R_TxFIFO_EMPTY_LEVEL(hi2s->I2Sx, I2S_FIFO_HALF_DEPTH); /* Format 20Bit/24Bit/32bit */ if (hi2s->Init.DataFormat > I2S_DATA_FORMAT_16BIT) { __I2S_LR_WD_DISABLE(hi2s->I2Sx); __I2S_INT_ENABLE(hi2s->I2Sx, I2S_TX_FIFOS_ALMOST_EMPTY); } /* Format 8Bit/16Bit */ else { __I2S_LR_WD_ENABLE(hi2s->I2Sx); __I2S_INT_ENABLE(hi2s->I2Sx, I2S_TX_L_FIFO_ALMOST_EMPTY); } return true; } /************************************************************************************ * @fn i2s_receive_IT * * @brief Receive an I2S data in interrupt mode. User read received data with function * i2s_read_data in RxCallback. * * @param huart: I2S handle. */ bool i2s_receive_IT(I2S_HandleTypeDef *hi2s) { if (hi2s->b_RxBusy) return false; hi2s->b_RxBusy = true; __I2S_L_RxFIFO_FULL_LEVEL(hi2s->I2Sx, 16); __I2S_R_RxFIFO_FULL_LEVEL(hi2s->I2Sx, 16); /* Format 20Bit/24Bit/32bit */ if (hi2s->Init.DataFormat > I2S_DATA_FORMAT_16BIT) { __I2S_LR_WD_DISABLE(hi2s->I2Sx); __I2S_INT_ENABLE(hi2s->I2Sx, I2S_RX_FIFOS_HALF_FULL); } /* Format 8Bit/16Bit */ else { __I2S_LR_WD_ENABLE(hi2s->I2Sx); __I2S_INT_ENABLE(hi2s->I2Sx, I2S_RX_L_FIFO_HALF_FULL); } return true; } /************************************************************************************ * @fn i2s_read_data * * @brief Read data from RX FIFO. One sample contains left and right channel data. When * DataFormat is not more than 16-bits, one sample takes 32-bits, left channel data * is stored in high 16-bits and right channel data is stored in low 16-bits. When * DataFormat is more than 16-bits, one sample takes 2 words. * * @param huart: I2S handle. * buffer: buffer used to store received data. * samples: How many samples expected to receive. * * @return How many samples read from RX FIFO */ uint32_t i2s_read_data(I2S_HandleTypeDef *hi2s, uint32_t *buffer, uint32_t samples) { uint32_t count = 0; if (hi2s->Init.DataFormat > I2S_DATA_FORMAT_16BIT) { while ((!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_FIFOS_EMPTY)) && (count < samples)) { buffer[count] = hi2s->I2Sx->DATA_L; buffer[count] = hi2s->I2Sx->DATA_R; count++; } } else { __I2S_LR_WD_ENABLE(hi2s->I2Sx); while ((!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_RX_L_FIFO_EMPTY)) && (count < samples)) { buffer[count] = hi2s->I2Sx->DATA_L; count++; } } return count; } /************************************************************************************ * @fn i2s_send_data * * @brief write data to TX FIFO. One sample contains left and right channel data. When * DataFormat is not more than 16-bits, one sample takes 32-bits, left channel data * is stored in high 16-bits and right channel data is stored in low 16-bits. When * DataFormat is more than 16-bits, one sample takes 2 words. * * @param huart: I2S handle. * buffer: buffer used to store sending data. * samples: How many samples expected to send. * * @return How many samples sent to TX FIFO */ uint32_t i2s_send_data(I2S_HandleTypeDef *hi2s, uint32_t *buffer, uint32_t samples) { uint32_t count = 0; if (hi2s->Init.DataFormat > I2S_DATA_FORMAT_16BIT) { while ((!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_FIFOS_FULL)) && (count < samples)) { hi2s->I2Sx->DATA_L = buffer[count]; hi2s->I2Sx->DATA_R = buffer[count]; count++; } } else { __I2S_LR_WD_ENABLE(hi2s->I2Sx); while ((!(__I2S_GET_INT_STATUS(hi2s->I2Sx) & I2S_TX_L_FIFO_FULL)) && (count < samples)) { hi2s->I2Sx->DATA_L = buffer[count]; count++; } } return count; } /************************************************************************************ * @fn i2s_transmit_DMA * * @brief Transmit an amount of data in DMA mode. */ void i2s_transmit_DMA(I2S_HandleTypeDef *hi2s) { hi2s->I2Sx->DMA_CFG.DMACR_L_TX = 1; hi2s->I2Sx->DMA_CFG.DMACR_R_TX = 1; /* Tx empty dma level */ __I2S_TX_DMA_EMPTY_LEVEL(hi2s->I2Sx, 16); } /************************************************************************************ * @fn i2s_receive_DMA * * @brief Receive an amount of data in DMA mode. */ void i2s_receive_DMA(I2S_HandleTypeDef *hi2s) { hi2s->I2Sx->DMA_CFG.DMACR_L_RX = 1; hi2s->I2Sx->DMA_CFG.DMACR_R_RX = 1; /* Rx empty dma level */ __I2S_RX_DMA_EMPTY_LEVEL(hi2s->I2Sx, 16); }