(1) The host has opened the stream by selecting the operational AS interface. It then sets the sampling frequency (for instance, 48 kHz). The function USBD_Audio_DrvCtrlAS_SamplingFreqManage() will be called for that operation. The sampling frequency is configured by accessing some codec registers. The register access will be accomplished by sending several I2C commands. (2) Once the playback stream priming is completed within the Audio Processing module, that is a certain number of audio buffers has been accumulated, the function USBD_Audio_DrvStreamStart is called. Usually, you may have to enable playback operations within the codec through some registers configuration. Here again, I2C controller will be used. The function USBD_Audio_PlaybackTxCmpl() is called by the driver to signal the audio transfer completion. The driver can call USBD_Audio_PlaybackTxCmpl() up to the number of buffers it can queue. Tip |
---|
The audio peripheral driver should support at least the double-buffering to optimize the playback stream processing. |
(3) The playback task will receive an AudioStreaming interface handle and will submit to the audio peripheral driver a ready buffer by calling USBD_Audio_DrvStreamPlaybackTx . The initial DMA transfer will be prepared with the first ready buffer. Note that the driver should start the initial DMA transfer after accumulating at least two ready buffers. This allows to start a sort of pipeline in which the audio peripheral driver won't wait after the playback task for providing a ready buffer to prepare the next DMA transfer. Once the pipeline is launched, any subsequent call to USBD_Audio_DrvStreamPlaybackTx() should store the ready buffer. Any buffer memory management method may be used to store the ready buffer (for instance, double-buffering, circular buffer, etc.). Depending on the DMA controller, you may have to configure some registers and/or a DMA descriptor in order to describe the transfer. The DMA controller moves the audio data from the memory to the I 2 S controller. This one will forward the data to the codec that will play audio data to the speaker. (4) A DMA interrupt will be fired upon transfer completion. An ISR associated to this interrupt is called. This ISR processes the DMA transfer completion by freeing the consumed buffer. For that, the function USBD_Audio_PlaybackBufFree() is called. This function updates one of the indexes of the ring buffer queue. The ISR continues by signaling to the playback task that the audio transfer has finished with USBD_Audio_PlaybackTxCmpl . Once again, the playback will provide a ready buffer via USBD_Audio_DrvStreamPlaybackTx() as described in step (2). The ISR will get a new ready buffer from its internal buffer storage. A new DMA transfer is prepared and started. If no playback buffer is available from the internal storage, you may have to play a silence buffer to keep the driver in sync with audio class, that is you want to continue receiving DMA transfer completion interrupt to re-signal the audio transfer completion to the playback task. The silence buffer is filled with zeros interpreted by the codec as silence. The silence buffer can be allocated and initialized in the function USBD_Audio_DrvInit() . Tip |
---|
The DMA implementation in this example processes the playback buffers one after the other using a single interrupt. Depending on your DMA controller, it may be possible to optimize the performance by using several DMA channels. In that case, you could pipeline the DMA transfers. The DMA controller may also offer to link DMA descriptors. In that case, you could get several ready buffers and link several DMA descriptors. |
(5) The host decides to stop the stream. The function USBD_Audio_DrvStreamStop is called. You should abort any ongoing DMA transfers. You don't have to call USBD_Audio_PlaybackBufFree() to free any aborted buffers nor to free ready buffers stored internally in the driver and not yet processed. The buffers are implicitly freed by the audio class during the ring buffer queue reset. You can also disable the playback operation on the codec if it is needed. |