Something is wrong with the STM32 USB Audio Class driver

After I had a working demo, I studied the code, to understand how does it work in details. Here is a simplified description:

1) Initialize EP 1 OUT which is the ISO endpoint, and preapare it for receiving.

2) When it receives something in Data_Out, It starts to Play it in the DAC by sending the data to I2S interface with DMA.

3) During the DMA transfer, we have a HalfComlete and a Complete interrupt. They invoke a simple audio synchronization part, which compensates the clock difference between the USB clock and I2S clock. Here I read a bunch of standards and literature about the clock syncronization possibilities of USB Audio Class.

4) While DMA is sending the data to I2S, the USB still receives the packets and stores them in a buffer.

Here I found some confusing things about DMA

There is a function in the driver strucure with two possible parameters that can send the audio to the I2S DAC:
AUDIO_COMMAND_START which invokes  BSP_AUDIO_OUT_Play
AUDIO_COMMAND_PLAY  which invokes BSP_AUDIO_OUT_ChangeBuffer

Here is the function
/**
  * @brief  Handles AUDIO command.        
  * @param  pbuf: Pointer to buffer of data to be sent
  * @param  size: Number of data to be sent in bytes
  * @param  cmd: Command opcode
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t Audio_PlaybackCmd(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
  switch(cmd)
  {
  case AUDIO_CMD_START:
    BSP_AUDIO_OUT_Play((uint16_t *)pbuf, size);
    break;
 
  case AUDIO_CMD_PLAY:
    BSP_AUDIO_OUT_ChangeBuffer((uint16_t *)pbuf, size);
    break;
  }
  return 0;
}
The comment says we need to pass the size parameter in bytes, and is used that way in upper layers, so clear so far.

However in the BSP packages I can see that these two functions are used different size parameters for the DMA:

BSP_AUDIO_OUT_Play uses a division by the sample bitdepth which is correct if we passed the number of  bytes (not samples) to the previous function.
HAL_I2S_Transmit_DMA(&haudio_i2s, pBuffer, DMA_MAX(Size/AUDIODATA_SIZE));

BSP_AUDIO_OUT_ChangeBuffer uses the number of bytes and not have the dvision by 16bits, so it cannot be correct.
HAL_I2S_Transmit_DMA(&hAudioOutI2s, pBuffer, Size);

Another thing I don’t really understand about how DMA is used in usbd_audio_if.c file.

There is an interrapt for the DMA half complete which is:

/**
* @brief  Manages the DMA Half Transfer complete event.
* @param  None
* @retval None
*/
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
{
USBD_AUDIO_Sync(&USBD_Device, AUDIO_OFFSET_HALF);
}

But in the USBD_AUDIO_Sync function a new DMA request is issued for I2S. What is the sense of this? I thought you cannot issue another DMA while the previous is not completed. So I think this one should have no effect, however the example application seems to be working fine.

I asked these questions on ST Forum, but no answer yet.

In the Data_Out function I also do not understend why to fill the whole buffer, and start DMA after filling it. The problem with that is the USB data uses the same part of the buffer as the DMA, USB can easily owerwrite the tha data that the DMA will send in the next step. Maybe I missed something, but it is not OK for me.

So I decided to rewrite the whole sync, Data_Out and buffer handling.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s