cancel
Showing results for 
Search instead for 
Did you mean: 

How to properly read in digital audio data coming out of a codec's ADC using I2S protocol?

AP.10
Associate II

I am using a STM32F4 MCU, currently prototyping on a Nucleo-F446RE. I have a PCM3060 audio CODEC to do the analog to digital conversion. I am attempting to use the I2S protocol in order to interface the stm32 chip with the CODEC to process real time audio signals. I have been using stm32CubeIDE to set up the peripherals and their settings.

Using I2C, I have configured the CODEC's ADC to act as a master, and the data coming out of it is 24Bit I2S format at 48KHz.

On the STM32 side of things, I have the I2S peripheral set up as half-duplex slave receive, data format as 24Bit on a 32Bit data frame, and 48Khz audio frequency.

I have been testing with inputting a low-frequency 440Hz sine wave to the codec and try to get some data coming out of the Digital Output pin. I have confirmed that the data coming out is correct by directly sending this ADC data out to the data in of the DAC, and seeing the analog output of the CODEC on an oscilloscope.

So far I have been using the HAL_I2S_receive functions to try and catch the CODEC's ADC output into a buffer in code but I am not able to see this buffer filling up at all. I first wanted to ask, what should be the size and data type of this receive buffer considering I am expecting 24Bit data? Should I be using DMA since I am attempting to capture constant real-time data? Are there any debugging tips anybody could give me to start finding some root issues with my approach? I am a newbie in the realm of audio processing so any answers are greatly appreciated!

6 REPLIES 6
MM..1
Chief II

Here you can learn mastering-stm32/stm32f4xx_hal_i2s_ex.c at master · cnoviello/mastering-stm32 · GitHub

And choice DMA or IRQ or POOL mode is based on type your application. But best seems be DMA with good buffer size and half / full complete callbacks.

For audio people typically use Circular DMA into large buffers where they can ping-pong between active and inactive halves of the same buffer, using the HT (Half Transfer) TC (Transfer Complete) interrupts to process, or save data, etc.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
AP.10
Associate II

Thanks for your answers!

What would be considered a good enough buffer size considering I am doing 24Bit on a 32Bit frame at 48Khz? I also have been reading through the HAL_I2S function but I am not entirely sure yet if I need to configure a separate timer and interrupt or will the HAL_I2S_DMA functions go into the half and full callbacks accordingly as they are?

LCE
Principal

Buffer size depends on what you want to do with the audio data.

Processing? So you need some more SRAM for that?

Or transmitting data via USB or another interface?

You do not need an extra timer, with the correct settings DMA half / complete interrupts will be triggered when ready.

Do not rely too much on Cube and HAL stuff. It's very good as a starting point, but eventually you'll find out that all this is not complete or even has some errors.

So get the reference manual and check the peripheral registers.

AP.10
Associate II

Hey LCE! I wanted to do some audio processing on the data before sending it back out to the DAC. I have been using a unit16_t array of size 32 have been using 48 and 96 for the buffer size argument of the HAL_I2S_Receive_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size).

My buffer seems to start getting filled up now but with what seems to be garbage values. I am also getting a weird effect where only half of the indexes in the buffer get filled up. Any ideas of what this could be?0693W00000GVxrKQAT.pngI know you have strongly suggested staying away from HAL since it has proven to be buggy and problematic in the past. I will certainly try to diverge from it and focus on the reference manual now.

/**
  * @brief Receive an amount of data in non-blocking mode with DMA 
  * @param  hi2s: pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @param pData: a 16-bit pointer to the Receive data buffer.
  * @param Size: number of data sample to be sent:
  * @note When a 16-bit data frame or a 16-bit data frame extended is selected during the I2S
  *       configuration phase, the Size parameter means the number of 16-bit data length 
  *       in the transaction and when a 24-bit data frame or a 32-bit data frame is selected 
  *       the Size parameter means the number of 16-bit data length. 
  * @note The I2S is kept enabled at the end of transaction to avoid the clock de-synchronization 
  *       between Master and Slave(example: audio streaming).
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2S_Receive_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size)
{
  uint32_t *tmp;
  uint32_t tmp1 = 0, tmp2 = 0;  
  
  if((pData == NULL) || (Size == 0))
  {
    return  HAL_ERROR;
  }
 
  if(hi2s->State == HAL_I2S_STATE_READY)
  {
    hi2s->pRxBuffPtr = pData;
    tmp1 = hi2s->Instance->I2SCFGR & (SPI_I2SCFGR_DATLEN | SPI_I2SCFGR_CHLEN);
    tmp2 = hi2s->Instance->I2SCFGR & (SPI_I2SCFGR_DATLEN | SPI_I2SCFGR_CHLEN);
    if((tmp1 == I2S_DATAFORMAT_24B)|| \
      (tmp2 == I2S_DATAFORMAT_32B))
    {
      hi2s->RxXferSize = Size*2;
      hi2s->RxXferCount = Size*2;
    }
    else
    {
      hi2s->RxXferSize = Size;
      hi2s->RxXferCount = Size;
    }

You say your frame is 32 then your size for DMA is multiplied 2. And pointer and array is only symbolic uint_16

your array need be 32 and source played or setup ADC need 24 bits for you can see second part not zero.