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)
BSP_AUDIO_OUT_Play((uint16_t *)pbuf, size);
BSP_AUDIO_OUT_ChangeBuffer((uint16_t *)pbuf, size);
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
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.