Table of Contents |
---|
There are many audio codecs available on the market and each requires a driver to work with the audio class. The driver is referred as the Audio Peripheral Driver in the the general audio class architecture. The amount of code necessary to port a specific audio codec to the audio class greatly depends on the codec’s complexity.
...
Tip |
---|
It is highly recommended to use a DMA implementation of the driver as it will offload the CPU and ensure better overall audio performances. |
...
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
An audio codec will usually have two interfaces with the microcontroller:
...
All audio peripheral drivers must declare different instances of the appropriate driver API structure as global variables within the source code. Each API structure is an ordered list of function pointers utilized by the audio class when device hardware services are required. Each structure will encompass some functions belonging to a category: common, Output Terminal, Feature Unit, Mixer Unit, Selector Unit and AudioStreaming (AS) interface. The API structure will then be passed to the appropriate USBD_Audio_XX_Add()
functions. Theses different API structures offers two possibilities to handle the terminal and unit IDs management within a given codec driver function:
...
Sample device driver API structures are shown below.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||
---|---|---|---|---|
| ||||
const USBD_AUDIO_DRV_COMMON_API USBD_Audio_DrvCommonAPI_Template = {
USBD_Audio_DrvInit (1)
};
const USBD_AUDIO_DRV_AC_OT_API USBD_Audio_DrvOT_API_Template = {
USBD_Audio_DrvCtrlOT_CopyProtSet (2)
};
const USBD_AUDIO_DRV_AC_FU_API USBD_Audio_DrvFU_API_Template = {
USBD_Audio_DrvCtrlFU_MuteManage, (3)
USBD_Audio_DrvCtrlFU_VolManage, (4)
USBD_Audio_DrvCtrlFU_BassManage, (5)
USBD_Audio_DrvCtrlFU_MidManage, (6)
USBD_Audio_DrvCtrlFU_TrebleManage, (7)
USBD_Audio_DrvCtrlFU_GraphicEqualizerManage, (8)
USBD_Audio_DrvCtrlFU_AutoGainManage, (9)
USBD_Audio_DrvCtrlFU_DlyManage, (10)
USBD_Audio_DrvCtrlFU_BassBoostManage, (11)
USBD_Audio_DrvCtrlFU_LoudnessManage (12)
};
const USBD_AUDIO_DRV_AC_MU_API USBD_Audio_DrvMU_API_Template = {
USBD_Audio_DrvCtrlMU_CtrlManage (13)
};
const USBD_AUDIO_DRV_AC_SU_API USBD_Audio_DrvSU_API_Template = {
USBD_Audio_DrvCtrlSU_InPinManage (14)
};
const USBD_AUDIO_DRV_AS_API USBD_Audio_DrvAS_API_Template = {
USBD_Audio_DrvAS_SamplingFreqManage, (15)
USBD_Audio_DrvAS_PitchManage, (16)
USBD_Audio_DrvStreamStart, (17)
USBD_Audio_DrvStreamStop, (18)
USBD_Audio_DrvStreamRecordRx, (19)
USBD_Audio_DrvStreamPlaybackTx (20)
}; |
Panel |
---|
(1) Audio peripherals initialization. (2) Set Copy Protection Level. (3) Get or set m ute state. (4) Get or set volume level. (5) Get or set bass level. (6) Get or set middle level. (7) Get or set treble level. (8) Get or set graphical equalizer level. (9) Get or set auto gain state. (10) Get or set delay value. (11) Get or set bass boost state. (12) Get or set loudness state. (13) Get or set mix status. (14) Get or set selected Input Pin of a particular Selector Unit. (15) Get or set sampling frequency. (16) Get or set pitch state. (17) Start record or playback stream. (18) Stop record or playback stream. (19) Get a ready record buffer from codec. (20) Provide a ready playback buffer to codec. |
The audio peripheral driver functions can be divided into three API groups as shown in the the Table - Audio Peripheral Driver API Groups.
Anchor | ||||
---|---|---|---|---|
|
Panel | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Optional functions can be declared as null pointers if the audio chip does not support the associated functionality.
...
The audio peripheral driver has access to stream API offered by the Audio Processing module presented in 106070237. Basically, this stream API allows the audio peripheral driver to get buffers to transfer audio data to/from an audio codec or any other types of audio chip.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
|
In order to better understand the use of this stream API, we will consider the typical audio stereo codec configuration shown by 106070237. Moreover, a DMA controller used by the I2S controller will be assumed. 106070237 summarizes the use of stream functions for a playback stream. Please refer to Figure - Playback Stream Dataflow as a complement to know what is happening in the Audio Processing module.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||||
---|---|---|---|---|
| ||||
(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 (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
(3) The playback task will receive an AudioStreaming interface handle and will submit to the audio peripheral driver a ready buffer by calling 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
(5) The host decides to stop the stream. The function |
Figure - Record Stream Functions summarizes the use of stream functions for a record stream. Please refer to as to Figure - Record Stream Dataflow as a complement to know that is happening in the Audio Processing module.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||||||
---|---|---|---|---|---|---|
| ||||||
(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 (2) The Audio Processing will call the function
(3) 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 signaling to the record task that a buffer is ready with the function
(4) Upon reception of the signal, the record task will call the function (5) The host decides to stop the stream. The function |
Statistics
As described in the section Audio Statistics, the audio class offered some stream statistics that may be useful during your development. An audio statistics structure ( USBD_AUDIO_STAT
) specific to each AS interface can be retrieved by the application and consulted during debugging. describes 106070237 describes all the fields of USBD_AUDIO_STAT
. Among them, there are four interesting for the driver:
...
You can use the macro AUDIO_DRV_STAT_INC()
by specifying an AS handle and the name of the field to update statistics. 106070237 shows an example of AUDIO_DRV_STAT_INC()
usage.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||
---|---|---|---|---|
| ||||
static void Streaming_I2S_DMA_ISR_Handler (CPU_INT08U ch)
{
USBD_AUDIO_DRV_DATA *p_drv_data;
CPU_INT08U *p_buf;
CPU_INT16U buf_len;
p_drv_data = AudioDrvDataPtr;
...
if (DMA write interrupt) {
...
p_buf = Codec_PlaybackCircularBufGet(p_drv_data,
&buf_len);
if (p_buf != (CPU_INT08U *)0) {
(1)
USBD_AUDIO_DRV_STAT_INC(DMA_AsHandleTbl[ch], AudioDrv_Playback_DMA_NbrXferCmpl);
/* $$$$ Prepare a DMA transfer. */
} else {
/* $$$$ Prepare a DMA transfer to play a silence buffer. */
(2)
USBD_AUDIO_DRV_STAT_INC(DMA_AsHandleTbl[ch], AudioDrv_Playback_DMA_NbrSilenceBuf);
}
...
}
if (DMA write interrupt) {
...
p_buf = (CPU_INT08U *)USBD_Audio_RecordBufGet(DMA_AsHandleTbl[ch],
&buf_len);
if (p_buf != (CPU_INT08U *)0) {
(3)
USBD_AUDIO_DRV_STAT_INC(DMA_AsHandleTbl[ch], AudioDrv_Record_DMA_NbrXferCmpl);
/* $$$$ Prepare a DMA transfer. */
} else {
/* $$$$ Prepare a DMA transfer with the dummy record buffer to keep in sync with audio class. */
(4)
USBD_AUDIO_DRV_STAT_INC(DMA_AsHandleTbl[ch], AudioDrv_Record_DMA_NbrDummyBuf);
}
...
}
...
} |
Panel | ||
---|---|---|
| ||
(1) You can count a playback DMA transfer completed once you receive the interrupt indicating transfer completion. You can increase the counter (2) If no buffer is available, you may have to play a silence buffer to keep in sync with the audio class. In that case, increase the counter (3) You can count a record DMA transfer completed once you receive the interrupt indicating transfer completion. You can increase the counter (4) If no empty buffer is available, you may have to use a dummy buffer to get record data and to keep in sync with the audio class. In that case, increase the counter |