diff options
Diffstat (limited to 'thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Utilities/STM32_EVAL/STM32303C_EVAL/stm32303c_eval_audio_codec.c')
-rw-r--r-- | thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Utilities/STM32_EVAL/STM32303C_EVAL/stm32303c_eval_audio_codec.c | 1597 |
1 files changed, 1597 insertions, 0 deletions
diff --git a/thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Utilities/STM32_EVAL/STM32303C_EVAL/stm32303c_eval_audio_codec.c b/thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Utilities/STM32_EVAL/STM32303C_EVAL/stm32303c_eval_audio_codec.c new file mode 100644 index 0000000..9d0991d --- /dev/null +++ b/thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Utilities/STM32_EVAL/STM32303C_EVAL/stm32303c_eval_audio_codec.c @@ -0,0 +1,1597 @@ +/** + ****************************************************************************** + * @file stm32303c_eval_audio_codec.c + * @author MCD Application Team + * @version V1.0.1 + * @date 23-October-2012 + * @brief This file includes the low layer driver for CS42L52 Audio Codec + * available on STM32303C_EVAL evaluation board(MB1019). + ****************************************************************************** + * @attention + * + * <h2><center>© COPYRIGHT 2012 STMicroelectronics</center></h2> + * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +/*============================================================================================================================== + User NOTES +1. How To use this driver: +-------------------------- + - This driver supports STM32F30x devices on STM32303C_EVAL (MB1019) Evaluation boards. + + - Configure the options in file stm32303c_eval_audio_codec.h in the section CONFIGURATION. + Refer to the sections 2 and 3 to have more details on the possible configurations. + + - Call the function EVAL_AUDIO_Init( + OutputDevice: physical output mode (OUTPUT_DEVICE_SPEAKER, + OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_AUTO or + OUTPUT_DEVICE_BOTH) + Volume: initial volume to be set (0 is min (mute), 100 is max (100%) + AudioFreq: Audio frequency in Hz (8000, 16000, 22500, 32000 ...) + this parameter is relative to the audio file/stream type. + ) + This function configures all the hardware required for the audio application (codec, I2C, I2S, + GPIOs, DMA and interrupt if needed). This function returns 0 if configuration is OK. + if the returned value is different from 0 or the function is stuck then the communication with + the codec has failed (try to un-plug the power or reset device in this case). + + OUTPUT_DEVICE_SPEAKER: only speaker will be set as output for the audio stream. + + OUTPUT_DEVICE_HEADPHONE: only headphones will be set as output for the audio stream. + + OUTPUT_DEVICE_AUTO: Selection of output device is made through external switch (implemented + into the audio jack on the evaluation board). When the Headphone is connected it is used + as output. When the headphone is disconnected from the audio jack, the output is + automatically switched to Speaker. + + OUTPUT_DEVICE_BOTH: both Speaker and Headphone are used as outputs for the audio stream + at the same time. + + - Call the function EVAL_AUDIO_Play( + pBuffer: pointer to the audio data file address + Size: size of the buffer to be sent in Bytes + ) + to start playing (for the first time) from the audio file/stream. + + - Call the function EVAL_AUDIO_PauseResume( + Cmd: AUDIO_PAUSE (or 0) to pause playing or AUDIO_RESUME (or + any value different from 0) to resume playing. + ) + Note. After calling EVAL_AUDIO_PauseResume() function for pause, only EVAL_AUDIO_PauseResume() should be called + for resume (it is not allowed to call EVAL_AUDIO_Play() in this case). + Note. This function should be called only when the audio file is played or paused (not stopped). + + - For each mode, you may need to implement the relative callback functions into your code. + The Callback functions are named EVAL_AUDIO_XXX_CallBack() and only their prototypes are declared in + the stm32303c_eval_audio_codec.h file. (refer to the example for more details on the callbacks implementations) + + - To Stop playing, to modify the volume level or to mute, use the functions + EVAL_AUDIO_Stop(), EVAL_AUDIO_VolumeCtl() and EVAL_AUDIO_Mute(). + + - The driver API and the callback functions are at the end of the stm32303c_eval_audio_codec.h file. + + + Driver architecture: + -------------------- + This driver is composed of three main layers: + o High Audio Layer: consists of the function API exported in the stm32303c_eval_audio_codec.h file + (EVAL_AUDIO_Init(), EVAL_AUDIO_Play() ...) + o Codec Control layer: consists of the functions API controlling the audio codec (CS42L52) and + included as local functions in file stm32303c_eval_audio_codec.c (Codec_Init(), Codec_Play() ...) + o Media Access Layer (MAL): which consists of functions allowing to access the media containing/ + providing the audio file/stream. These functions are also included as local functions into + the stm32303c_eval_audio_codec.c file (Audio_MAL_Init(), Audio_MAL_Play() ...) + Each set of functions (layer) may be implemented independently of the others and customized when + needed. + +2. Modes description: +--------------------- + + AUDIO_MAL_MODE_NORMAL : is suitable when the audio file is in a memory location. + + AUDIO_MAL_MODE_CIRCULAR: is suitable when the audio data are read either from a + memory location or from a device at real time (double buffer could be used). + +3. DMA interrupts description: +------------------------------ + + EVAL_AUDIO_IT_TC_ENABLE: Enable this define to use the DMA end of transfer interrupt. + then, a callback should be implemented by user to perform specific actions + when the DMA has finished the transfer. + + EVAL_AUDIO_IT_HT_ENABLE: Enable this define to use the DMA end of half transfer interrupt. + then, a callback should be implemented by user to perform specific actions + when the DMA has reached the half of the buffer transfer (generally, it is useful + to load the first half of buffer while DMA is loading from the second half). + + EVAL_AUDIO_IT_ER_ENABLE: Enable this define to manage the cases of error on DMA transfer. + +4. Known Limitations: +--------------------- + 1- When using the Speaker, if the audio file quality is not high enough, the speaker output + may produce high and uncomfortable noise level. To avoid this issue, to use speaker + output properly, try to increase audio file sampling rate (typically higher than 48KHz). + This operation will lead to larger file size. + 2- Communication with the audio codec (through I2C) may be corrupted if it is interrupted by some + user interrupt routines (in this case, interrupts could be disabled just before the start of + communication then re-enabled when it is over). Note that this communication is only done at + the configuration phase (EVAL_AUDIO_Init() or EVAL_AUDIO_Stop()) and when Volume control modification is + performed (EVAL_AUDIO_VolumeCtl() or EVAL_AUDIO_Mute()). When the audio data is played, no communication is + required with the audio codec. + 3- Parsing of audio file is not implemented (in order to determine audio file properties: Mono/Stereo, Data size, + File size, Audio Frequency, Audio Data header size ...). The configuration is fixed for the given audio file. + 4- Mono audio streaming is not supported (in order to play mono audio streams, each data should be sent twice + on the I2S or should be duplicated on the source buffer. Or convert the stream in stereo before playing). + 5- Supports only 16-bit audio data size. +===============================================================================================================================*/ + + +/* Includes ------------------------------------------------------------------*/ +#include "stm32303c_eval_audio_codec.h" + +/** @addtogroup Utilities + * @{ + */ + +/** @addtogroup STM32_EVAL + * @{ + */ + +/** @addtogroup STM32303C_EVAL + * @{ + */ + +/** @addtogroup STM32303C_EVAL_AUDIO_CODEC + * @brief This file includes the low layer driver for CS42L52 Audio Codec + * available on STM32303C_EVAL evaluation board(MB1019). + * @{ + */ + +/** @defgroup STM32303C_EVAL_AUDIO_CODEC_Private_Types + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32303C_EVAL_AUDIO_CODEC_Private_Defines + * @{ + */ + +/* Mask for the bit EN of the I2S CFGR register */ +#define I2S_ENABLE_MASK 0x0400 + +/* Delay for the Codec to be correctly reset */ +#define CODEC_RESET_DELAY 0x4FFF + +/* Codec audio Standards */ +#ifdef I2S_STANDARD_PHILLIPS + #define CODEC_STANDARD 0x04 + #define I2S_STANDARD I2S_Standard_Phillips +#elif defined(I2S_STANDARD_MSB) + #define CODEC_STANDARD 0x00 + #define I2S_STANDARD I2S_Standard_MSB +#elif defined(I2S_STANDARD_LSB) + #define CODEC_STANDARD 0x08 + #define I2S_STANDARD I2S_Standard_LSB +#else + #error "Error: No audio communication standard selected !" +#endif /* I2S_STANDARD */ + +/* The 7 bits Codec address (sent through I2C interface) */ +#define CODEC_ADDRESS 0x94 /* b1001010. */ +/** + * @} + */ + +/** @defgroup STM32303C_EVAL_AUDIO_CODEC_Private_Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32303C_EVAL_AUDIO_CODEC_Private_Variables + * @{ + */ +/* This structure is declared global because it is handled by two different functions */ +DMA_InitTypeDef DMA_InitStructure; +DMA_InitTypeDef AUDIO_MAL_DMA_InitStructure; + +uint32_t AudioTotalSize = 0xFFFF; /* This variable holds the total size of the audio file */ +uint32_t AudioRemSize = 0xFFFF; /* This variable holds the remaining data in audio file */ +uint16_t *CurrentPos; /* This variable holds the current position of audio pointer */ + +__IO uint32_t CODECTimeout = CODEC_LONG_TIMEOUT; +__IO uint8_t OutputDev = 0; + +/** + * @} + */ + +/** @defgroup STM32303C_EVAL_AUDIO_CODEC_Private_Function_Prototypes + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32303C_EVAL_AUDIO_CODEC_Private_Functions + * @{ + */ + +/*---------------------------------------------------------------------------- + Audio Codec functions + ----------------------------------------------------------------------------*/ +/* High Layer codec functions */ +static uint32_t Codec_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq); +static uint32_t Codec_DeInit(void); +static uint32_t Codec_Play(void); +static uint32_t Codec_PauseResume(uint32_t Cmd); +static uint32_t Codec_Stop(uint32_t Cmd); +static uint32_t Codec_VolumeCtrl(uint8_t Volume); +static uint32_t Codec_Mute(uint32_t Cmd); +/* Low layer codec functions */ +static void Codec_CtrlInterface_Init(void); +static void Codec_CtrlInterface_DeInit(void); +static void Codec_AudioInterface_Init(uint32_t AudioFreq); +static void Codec_AudioInterface_DeInit(void); +uint32_t Codec_ReadRegister(uint8_t RegisterAddr); +static void Codec_GPIO_Init(void); +static void Codec_GPIO_Recorder_Init(void); +static void Codec_GPIO_DeInit(void); +static void Delay(__IO uint32_t nCount); +/*----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + MAL (Media Access Layer) functions + ----------------------------------------------------------------------------*/ +/* Peripherals configuration functions */ +static void Audio_MAL_Init(void); +static void Audio_MAL_DeInit(void); +static void Audio_MAL_PauseResume(uint32_t Cmd, uint32_t Addr); +static void Audio_MAL_Stop(void); +/*----------------------------------------------------------------------------*/ + +/** + * @brief Configure the audio peripherals. + * @param OutputDevice: OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE, + * OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO . + * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max)) + * @param AudioFreq: Audio frequency used to play the audio stream. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq) +{ + /* Perform low layer Codec initialization */ + if (Codec_Init(OutputDevice, VOLUME_CONVERT(Volume), AudioFreq) != 0) + { + return 1; + } + else + { + /* I2S data transfer preparation: + Prepare the Media to be used for the audio transfer from memory to I2S peripheral */ + Audio_MAL_Init(); + + /* Return 0 when all operations are OK */ + return 0; + } +} + +/** + * @brief Deinitializes all the resources used by the codec (those initialized + * by EVAL_AUDIO_Init() function). + * @param None + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_DeInit(void) +{ + /* DeInitialize Codec */ + Codec_DeInit(); + + /* DeInitialize the Media layer */ + Audio_MAL_DeInit(); + + return 0; +} + +/** + * @brief Starts playing audio stream from a data buffer for a determined size. + * @param pBuffer: Pointer to the buffer + * @param Size: Number of audio data BYTES. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_Play(uint16_t* pBuffer, uint32_t Size) +{ + /* Set the total number of data to be played (count in half-word) */ + AudioTotalSize = Size/2; + + /* Call the audio Codec Play function */ + Codec_Play(); + + /* Update the Media layer and enable it for play */ + Audio_MAL_Play((uint32_t)pBuffer, (uint32_t)(DMA_MAX(AudioTotalSize / 2))); + + /* Update the remaining number of data to be played */ + AudioRemSize = (Size/2) - DMA_MAX(AudioTotalSize); + + /* Update the current audio pointer position */ + CurrentPos = pBuffer + DMA_MAX(AudioTotalSize); + + return 0; +} + +/** + * @brief This function Pauses or Resumes the audio file stream. In case + * of using DMA, the DMA Pause feature is used. In all cases the I2S + * peripheral is disabled. + * + * @WARNING When calling EVAL_AUDIO_PauseResume() function for pause, only + * this function should be called for resume (use of EVAL_AUDIO_Play() + * function for resume could lead to unexpected behavior). + * + * @param Cmd: AUDIO_PAUSE (or 0) to pause, AUDIO_RESUME (or any value different + * from 0) to resume. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_PauseResume(uint32_t Cmd) +{ + /* Call the Audio Codec Pause/Resume function */ + if (Codec_PauseResume(Cmd) != 0) + { + return 1; + } + else + { + /* Call the Media layer pause/resume function */ + Audio_MAL_PauseResume(Cmd, 0); + + /* Return 0 if all operations are OK */ + return 0; + } +} + +/** + * @brief Stops audio playing and Power down the Audio Codec. + * @param Option: could be one of the following parameters + * - CODEC_PDWN_SW: for software power off (by writing registers). + * Then no need to reconfigure the Codec after power on. + * - CODEC_PDWN_HW: completely shut down the codec (physically). + * Then need to reconfigure the Codec after power on. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_Stop(uint32_t Option) +{ + /* Call Audio Codec Stop function */ + if (Codec_Stop(Option) != 0) + { + return 1; + } + else + { + /* Call Media layer Stop function */ + Audio_MAL_Stop(); + + /* Update the remaining data number */ + AudioRemSize = AudioTotalSize; + + /* Return 0 when all operations are correctly done */ + return 0; + } +} + +/** + * @brief Controls the current audio volume level. + * @param Volume: Volume level to be set in percentage from 0% to 100% (0 for + * Mute and 100 for Max volume level). + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_VolumeCtl(uint8_t Volume) +{ + /* Call the codec volume control function with converted volume value */ + return (Codec_VolumeCtrl(VOLUME_CONVERT(Volume))); +} + +/** + * @brief Enables or disables the MUTE mode by software + * @param Command: could be AUDIO_MUTE_ON to mute sound or AUDIO_MUTE_OFF to + * unmute the codec and restore previous volume level. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t EVAL_AUDIO_Mute(uint32_t Cmd) +{ + /* Call the Codec Mute function */ + return (Codec_Mute(Cmd)); +} + +/** + * @brief This function handles main Media layer interrupt. + * @param None + * @retval 0 if correct communication, else wrong communication + */ +void Audio_MAL_IRQHandler(void) +{ +#ifndef AUDIO_MAL_MODE_NORMAL + uint16_t *pAddr = (uint16_t *)CurrentPos; + uint32_t Size = AudioRemSize; +#endif /* AUDIO_MAL_MODE_NORMAL */ + +#ifdef AUDIO_MAL_DMA_IT_TC_EN + /* Transfer complete interrupt */ + if (DMA_GetFlagStatus(AUDIO_MAL_DMA_FLAG_TC) != RESET) + { + #ifdef AUDIO_MAL_MODE_NORMAL + /* Check if the end of file has been reached */ + if (AudioRemSize > 0) + { + /* Wait the DMA Channel to be effectively disabled */ + while (DMA_GetFlagStatus(AUDIO_MAL_DMA_FLAG_TC) != SET) + {} + + /* Clear the Interrupt flag */ + DMA_ClearFlag(AUDIO_MAL_DMA_FLAG_TC); + + /* Re-Configure the buffer address and size */ + DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) CurrentPos; + DMA_InitStructure.DMA_BufferSize = (uint32_t) (DMA_MAX(AudioRemSize)); + + /* Configure the DMA Channel with the new parameters */ + DMA_Init(AUDIO_MAL_DMA_CHANNEL , &DMA_InitStructure); + + /* Enable the I2S DMA Channel */ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL , ENABLE); + + /* Update the current pointer position */ + CurrentPos += DMA_MAX(AudioRemSize); + + /* Update the remaining number of data to be played */ + AudioRemSize -= DMA_MAX(AudioRemSize); + } + else + { + /* Disable the I2S DMA Channel */ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL , DISABLE); + + /* Clear the Interrupt flag */ + DMA_ClearFlag(AUDIO_MAL_DMA_FLAG_TC); + + /* Manage the remaining file size and new address offset: This function + should be coded by user (its prototype is already declared in stm32_eval_audio_codec.h) */ + EVAL_AUDIO_TransferComplete_CallBack((uint32_t)CurrentPos, 0); + } + + #elif defined(AUDIO_MAL_MODE_CIRCULAR) + /* Manage the remaining file size and new address offset: This function + should be coded by user (its prototype is already declared in stm32_eval_audio_codec.h) */ + EVAL_AUDIO_TransferComplete_CallBack(pAddr, Size); + + /* Clear the Interrupt flag */ + DMA_ClearFlag(AUDIO_MAL_DMA_STREAM, AUDIO_MAL_DMA_FLAG_TC); + #endif /* AUDIO_MAL_MODE_NORMAL */ + } +#endif /* AUDIO_MAL_DMA_IT_TC_EN */ + +#ifdef AUDIO_MAL_DMA_IT_HT_EN + /* Half Transfer complete interrupt */ + if (DMA_GetFlagStatus(AUDIO_MAL_DMA_CHANNEL, AUDIO_MAL_DMA_FLAG_HT) != RESET) + { + /* Manage the remaining file size and new address offset: This function + should be coded by user (its prototype is already declared in stm32_eval_audio_codec.h) */ + EVAL_AUDIO_HalfTransfer_CallBack((uint32_t)pAddr, Size); + + /* Clear the Interrupt flag */ + DMA_ClearFlag(AUDIO_MAL_DMA_CHANNEL, AUDIO_MAL_DMA_FLAG_HT); + } +#endif /* AUDIO_MAL_DMA_IT_HT_EN */ + +#ifdef AUDIO_MAL_DMA_IT_TE_EN + /* FIFO Error interrupt */ + if ((DMA_GetFlagStatus(AUDIO_MAL_DMA_CHANNEL, AUDIO_MAL_DMA_FLAG_TE) != RESET) || \ + (DMA_GetFlagStatus(AUDIO_MAL_DMA_CHANNEL, AUDIO_MAL_DMA_FLAG_FE) != RESET) || \ + (DMA_GetFlagStatus(AUDIO_MAL_DMA_CHANNEL, AUDIO_MAL_DMA_FLAG_DME) != RESET)) + + { + /* Manage the error generated on DMA FIFO: This function + should be coded by user (its prototype is already declared in stm32_eval_audio_codec.h) */ + EVAL_AUDIO_Error_CallBack((uint32_t*)&pAddr); + + /* Clear the Interrupt flag */ + DMA_ClearFlag(AUDIO_MAL_DMA_CHANNEL, AUDIO_MAL_DMA_FLAG_TE | AUDIO_MAL_DMA_FLAG_FE | \ + AUDIO_MAL_DMA_FLAG_DME); + } +#endif /* AUDIO_MAL_DMA_IT_TE_EN */ +} + +/*============================================================================ + CS42L52 Audio Codec Control Functions + ============================================================================*/ +/** + * @brief Initializes the audio codec and all related interfaces (control + * interface: I2C and audio interface: I2S) + * @param OutputDevice: can be OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE, + * OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO . + * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max)) + * @param AudioFreq: Audio frequency used to play the audio stream. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq) +{ + uint32_t counter = 0; + + /* Configure the Codec related IOs */ + Codec_GPIO_Init(); + + /* Initialize the Control interface of the Audio Codec */ + Codec_CtrlInterface_Init(); + + /* Keep Codec powered OFF */ + counter += Codec_WriteRegister(0x02, 0x9E/*0x01*/); + + switch (OutputDevice) + { + case OUTPUT_DEVICE_SPEAKER: + counter += Codec_WriteRegister(0x04, 0xFA); /* SPK always ON & HP always OFF */ + OutputDev = 0xFA; + break; + + case OUTPUT_DEVICE_HEADPHONE: + counter += Codec_WriteRegister(0x04, 0xAF); /* SPK always OFF & HP always ON */ + OutputDev = 0xAF; + break; + + case OUTPUT_DEVICE_BOTH: + counter += Codec_WriteRegister(0x04, 0xAA); /* SPK always ON & HP always ON */ + OutputDev = 0xAA; + break; + + case OUTPUT_DEVICE_AUTO: + counter += Codec_WriteRegister(0x04, 0x05); /* Detect the HP or the SPK automatically */ + OutputDev = 0x05; + break; + + default: + counter += Codec_WriteRegister(0x04, 0x05); /* Detect the HP or the SPK automatically */ + OutputDev = 0x05; + break; + } + + /* Clock configuration: Auto detection */ + counter += Codec_WriteRegister(0x05, 0x80); + /* Set the Slave Mode and the audio Standard */ + counter += Codec_WriteRegister(0x06, 0x03/*CODEC_STANDARD*/); + /* Interface Control 2: SCLK is Re-timed signal from MCLK*/ + counter +=Codec_WriteRegister(0x07, 0x00); + /* ADCA and PGAA Select: no input selected*/ + counter +=Codec_WriteRegister(0x08, 0x00); + /* ADCB and PGAB Select: no input selected*/ + counter +=Codec_WriteRegister(0x09, 0x00); + /*Play Back Control 1: headphone gain is 0.4, PCM not inverted, Master not mute*/ + counter +=Codec_WriteRegister(0x0D, 0x10);/* CS42L52 has different config than CS42L52*/ + /* Miscellaneous Controls: Passthrough Analog & Passthrough Mute off, Soft Ramp on @0x0E*/ + counter +=Codec_WriteRegister(0x0E, 0x02); + /* Play Back Control 2: Headphone Mute off, speaker mute off, mono enabled */ + counter +=Codec_WriteRegister(0x0F, 0x32); + /* PCM A Volume: PCM Mute disabled, Volume is 0db(default) */ + counter +=Codec_WriteRegister(0x1A, 0x00); + /* PCM B Volume: PCM Mute disabled, Volume is 0db(default) */ + counter +=Codec_WriteRegister(0x1B, 0x00); + /* Headphone A Volume: Headphone Volume is -6db */ + counter +=Codec_WriteRegister(0x22, (u8)(0-12)); + /* Headphone B Volume: Headphone Volume is -6db */ + counter +=Codec_WriteRegister(0x23, (u8)(0-12)); + /* Speaker A Volume: Speaker Volume is 0db (default) */ + counter +=Codec_WriteRegister(0x24, 0x00); + /* Speaker B Volume: Speaker Volume is 0db (default) */ + counter +=Codec_WriteRegister(0x25, 0x00); + /* Charge Pump Frequency: 5 (default) */ + counter +=Codec_WriteRegister(0x34, 5<<4); + /* Power Control 1: power up */ + counter += Codec_WriteRegister(0x02, 0x00); + counter += Codec_WriteRegister(0x20, 0xff); + counter += Codec_WriteRegister(0x21, 0xff); + + /* Configure the I2S peripheral */ + Codec_AudioInterface_Init(AudioFreq); + + /* Return communication control value */ + return counter; +} + +/** + * @brief Initializes the audio codec and all related interfaces (control + * interface: I2C and audio interface: I2S) + * @param OutputDevice: can be OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE, + * OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO . + * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max)) + * @param AudioFreq: Audio frequency used to play the audio stream. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t Codec_Record_Init(void) +{ + uint32_t counter = 0; + + /* Configure the Codec related IOs */ + Codec_GPIO_Recorder_Init(); + + /* Initialize the Control interface of the Audio Codec */ + Codec_CtrlInterface_Init(); + + /* Mono Record @ MIC1+ with Bias */ + /* Power Control 2[@03h]: MICB Off, MIC A & Bias On */ + counter += Codec_WriteRegister(0x03, 0x04); + /* Power Control 3[@04h]: headphone A/B Off, speaker A & B Off */ + counter += Codec_WriteRegister(0x04, 0xFF); + /* Clocking Control: auto sample rate(Fs) */ + counter += Codec_WriteRegister(0x05, 0x80); + /* Interface Control 1: slave, LeftJustified, 16-bit data */ + counter += Codec_WriteRegister(0x06, 0x03); + /* Interface Control 2: SCLK is Re-timed signal from MCLK, BIAS: 0.5*VA */ + counter += Codec_WriteRegister(0x07, 0x00); + /* ADCA and PGAA Select: MICx selected */ + counter += Codec_WriteRegister(0x08, 0x90); + /* ADCB and PGAB Select: MICx selected */ + counter += Codec_WriteRegister(0x09, 0x90); + /* Misc ADC Control: SDOUT Signal Source is ADC_or_DSP */ + counter += Codec_WriteRegister(0x0C, 0x00); + /* Play Back Control 1: headphone gain is 0.4, PCM not inverted, Master not mute */ + counter += Codec_WriteRegister(0x0D, 0x00); + /* Miscellaneous Controls: Passthrough Analog & Passthrough Mute off, Soft Ramp on @0x0E */ + counter += Codec_WriteRegister(0x0E, 0x02); + /* Play Back Control 2: Headphone Mute on, speaker mute off, mono enabled */ + counter += Codec_WriteRegister(0x0F, 0xC2); + /* MICA Amp Control: select MIC 2x, Single-Ended, Gain=16db */ + counter += Codec_WriteRegister(0x10, 0x2E); + /* MICB Amp Control: select MIC 2x, Single-Ended, Gain=16db */ + counter += Codec_WriteRegister(0x11, 0x40); + /* MICB Amp Control: try with Differential, Gain=16db */ + counter += Codec_WriteRegister(0x11, 0x20); + /* PGA Volume ALC Control: Gain=0db */ + counter += Codec_WriteRegister(0x12, 0x00); + /* PGA Volume ALC Control: Gain=0db */ + counter += Codec_WriteRegister(0x13, 0x00); + counter += Codec_WriteRegister(0x34, 5<<4); + + counter += Codec_WriteRegister(0x02, 0x14); + + /* Return communication control value */ + return counter; + +} + +/** + * @brief Restore the audio codec state to default state and free all used + * resources. + * @param None + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_DeInit(void) +{ + uint32_t counter = 0; + uint16_t i = 0; + uint8_t tmp = 0; + + /* Enable Master Playback Mute @0x0D */ + counter += Codec_WriteRegister(0x02, 0x03); + /* Power down CS42L52 @0x03 */ + counter += Codec_WriteRegister(0x03, 0x07); + /* Power down CS42L52 @0x04 */ + counter += Codec_WriteRegister(0x04, 0xFF); + /* config interface @0x06 */ + counter += Codec_WriteRegister(0x06, 0x03); + /* Disable Analog (Soft Ramp & Zero Cross) and HighPassFilter @0x0A */ + counter += Codec_WriteRegister(0x0A, 0x00); + /* Digital Soft Ramp on & Zero Cross off @0x0E */ + counter += Codec_WriteRegister(0x0E, 0x02); + /* Headphone Mute, Speaker Mute @0x0F */ + counter += Codec_WriteRegister(0x0F, 0xF2); + /* PCM A Volume: Mute @0x1A */ + counter += Codec_WriteRegister(0x1A, 0x80); + /* PCM B Volume: Mute @0x1B */ + counter += Codec_WriteRegister(0x1B, 0x80); + /* Limiter Attack Rate: 0x00 @0x29 */ + counter += Codec_WriteRegister(0x29, 0x00); + + i = 0; + do{ + // Power down CS42L52 @0x02 + counter += Codec_WriteRegister(0x02, 0x9F); + tmp = Codec_ReadRegister(0x02); + i++; + } + while (((tmp & 0x01) == 0) && (i < 10)); + + Delay(CODEC_RESET_DELAY); + + /* Deinitialize all use GPIOs */ + Codec_GPIO_DeInit(); + + /* Deinitialize the Codec audio interface (I2S) */ + Codec_AudioInterface_DeInit(); + /* Disable the Codec control interface */ + Codec_CtrlInterface_DeInit(); + + /* Return communication control value */ + return counter; +} + +/** + * @brief Start the audio Codec play feature. + * @note For this codec no Play options are required. + * @param None + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_Play(void) +{ + /* + No actions required on Codec level for play command + */ + + /* Return communication control value */ + return 0; +} + +/** + * @brief Pauses and resumes playing on the audio codec. + * @param Cmd: AUDIO_PAUSE (or 0) to pause, AUDIO_RESUME (or any value different + * from 0) to resume. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_PauseResume(uint32_t Cmd) +{ + uint32_t counter = 0; + + /* Pause the audio file playing */ + if (Cmd == AUDIO_PAUSE) + { + /* Mute the output first */ + counter += Codec_Mute(AUDIO_MUTE_ON); + + /* Put the Codec in Power save mode */ + counter += Codec_WriteRegister(0x02, 0x01); + } + else /* AUDIO_RESUME */ + { + /* Unmute the output first */ + counter += Codec_Mute(AUDIO_MUTE_OFF); + + counter += Codec_WriteRegister(0x04, OutputDev); + + /* Exit the Power save mode */ + counter += Codec_WriteRegister(0x02, 0x9E); + + } + + return counter; +} + +/** + * @brief Stops audio Codec playing. It powers down the codec. + * @param CodecPdwnMode: selects the power down mode. + * - CODEC_PDWN_SW: only mutes the audio codec. When resuming from this + * mode the codec keeps the previous initialization + * (no need to re-Initialize the codec registers). + * - CODEC_PDWN_HW: Physically power down the codec. When resuming from this + * mode, the codec is set to default configuration + * (user should re-Initialize the codec in order to + * play again the audio stream). + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_Stop(uint32_t CodecPdwnMode) +{ + uint32_t counter = 0; + + /* Mute the output first */ + Codec_Mute(AUDIO_MUTE_ON); + + if (CodecPdwnMode == CODEC_PDWN_SW) + { + /* Power down the DAC and the speaker (PMDAC and PMSPK bits)*/ + counter += Codec_WriteRegister(0x02, 0x9F); + } + else /* CODEC_PDWN_HW */ + { + /* Power down the components */ + counter += Codec_WriteRegister(0x02, 0x9F); + + /* Wait at least 100us */ + Delay(0xFFF); + } + return counter; +} + +/** + * @brief Sets higher or lower the codec volume level. + * @param Volume: a byte value from 0 to 255 (refer to codec registers + * description for more details). + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_VolumeCtrl(uint8_t Volume) +{ + uint32_t counter = 0; + + if (Volume > 0xE6) + { + /* Set the Master volume */ + counter += Codec_WriteRegister(0x20, Volume - 0xE7); + counter += Codec_WriteRegister(0x21, Volume - 0xE7); + } + else + { + /* Set the Master volume */ + counter += Codec_WriteRegister(0x20, Volume + 0x19); + counter += Codec_WriteRegister(0x21, Volume + 0x19); + } + + return counter; +} + +/** + * @brief Enables or disables the mute feature on the audio codec. + * @param Cmd: AUDIO_MUTE_ON to enable the mute or AUDIO_MUTE_OFF to disable the + * mute mode. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t Codec_Mute(uint32_t Cmd) +{ + uint32_t counter = 0; + + /* Set the Mute mode */ + if (Cmd == AUDIO_MUTE_ON) + { + counter += Codec_WriteRegister(0x04, 0xFF); + counter += Codec_WriteRegister(0x0F, 0xF0); + } + else /* AUDIO_MUTE_OFF Disable the Mute */ + { + counter += Codec_WriteRegister(0x04, 0x05); + counter += Codec_WriteRegister(0x0F, 0x02); + } + + return counter; +} + +/** + * @brief Switch dynamically (while audio file is played) the output target + * (speaker or headphone). + * @note This function modifies a global variable of the audio codec driver: OutputDev. + * @param Output: specifies the audio output target: OUTPUT_DEVICE_SPEAKER, + * OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO + * @retval 0 if correct communication, else wrong communication + */ +uint32_t Codec_SwitchOutput(uint8_t Output) +{ + uint8_t counter = 0; + + switch (Output) + { + case OUTPUT_DEVICE_SPEAKER: + counter += Codec_WriteRegister(0x04, 0xFA); /* SPK always ON & HP always OFF */ + OutputDev = 0xFA; + break; + + case OUTPUT_DEVICE_HEADPHONE: + counter += Codec_WriteRegister(0x04, 0xAF); /* SPK always OFF & HP always ON */ + OutputDev = 0xAF; + break; + + case OUTPUT_DEVICE_BOTH: + counter += Codec_WriteRegister(0x04, 0xAA); /* SPK always ON & HP always ON */ + OutputDev = 0xAA; + break; + + case OUTPUT_DEVICE_AUTO: + counter += Codec_WriteRegister(0x04, 0x05); /* Detect the HP or the SPK automatically */ + OutputDev = 0x05; + break; + + default: + counter += Codec_WriteRegister(0x04, 0x05); /* Detect the HP or the SPK automatically */ + OutputDev = 0x05; + break; + } + + return counter; +} + +/** + * @brief Writes a Byte to a given register into the audio codec through the + control interface (I2C) + * @param RegisterAddr: The address (location) of the register to be written. + * @param RegisterValue: the Byte value to be written into destination register. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t Codec_WriteRegister(uint8_t RegisterAddr, uint8_t RegisterValue) +{ + uint32_t result = 0; + + /* Test on BUSY Flag */ + CODECTimeout = CODEC_FLAG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_BUSY) != RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Configure slave address, nbytes, reload, end mode and start or stop generation */ + I2C_TransferHandling(CODEC_I2C, CODEC_ADDRESS, 2, I2C_AutoEnd_Mode, I2C_Generate_Start_Write); + + /* Wait until TXIS flag is set */ + CODECTimeout = CODEC_FLAG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_TXIS) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Send Register address */ + I2C_SendData(CODEC_I2C, (uint8_t)RegisterAddr ); + + /* Wait until TXIS flag is set */ + CODECTimeout = CODEC_FLAG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_TXIS) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Write data to TXDR */ + I2C_SendData(CODEC_I2C, (uint8_t)RegisterValue); + + /* Wait until STOPF flag is set */ + CODECTimeout = CODEC_FLAG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_STOPF) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Clear STOPF flag */ + I2C_ClearFlag(CODEC_I2C, I2C_ICR_STOPCF); + +#ifdef VERIFY_WRITTENDATA + /* Verify that the data has been correctly written */ + result = (Codec_ReadRegister(RegisterAddr) == RegisterValue)? 0:1; +#endif /* VERIFY_WRITTENDATA */ + + /* Return the verifying value: 0 (Passed) or 1 (Failed) */ + return result; +} + +/** + * @brief Reads and returns the value of an audio codec register through the + * control interface (I2C). + * @param RegisterAddr: Address of the register to be read. + * @retval Value of the register to be read or dummy value if the communication + * fails. + */ +uint32_t Codec_ReadRegister(uint8_t RegisterAddr) +{ + uint32_t result = 0; + + /* Test on BUSY Flag */ + CODECTimeout = CODEC_LONG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_BUSY) != RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Configure slave address, nbytes, reload, end mode and start or stop generation */ + I2C_TransferHandling(CODEC_I2C, CODEC_ADDRESS, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); + + /* Wait until TXIS flag is set */ + CODECTimeout = CODEC_LONG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_TXIS) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Send Register address */ + I2C_SendData(CODEC_I2C, (uint8_t)RegisterAddr); + + /* Wait until TC flag is set */ + CODECTimeout = CODEC_LONG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_TC) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Configure slave address, nbytes, reload, end mode and start or stop generation */ + I2C_TransferHandling(CODEC_I2C, CODEC_ADDRESS, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Read); + + /* Wait until RXNE flag is set */ + CODECTimeout = CODEC_LONG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_RXNE) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Read data from RXDR */ + result = I2C_ReceiveData(CODEC_I2C); + + /* Wait until STOPF flag is set */ + CODECTimeout = CODEC_LONG_TIMEOUT; + while(I2C_GetFlagStatus(CODEC_I2C, I2C_ISR_STOPF) == RESET) + { + if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback(); + } + + /* Clear STOPF flag */ + I2C_ClearFlag(CODEC_I2C, I2C_ICR_STOPCF); + + /* Return the byte read from Codec */ + return result; +} + +/** + * @brief Initializes the Audio Codec control interface (I2C). + * @param None + * @retval None + */ +static void Codec_CtrlInterface_Init(void) +{ + I2C_InitTypeDef I2C_InitStructure; + + /* Configure the I2C clock source. The clock is derived from the SYSCLK */ + RCC_I2CCLKConfig(RCC_I2C2CLK_SYSCLK); + + /* Enable the CODEC_I2C peripheral clock */ + RCC_APB1PeriphClockCmd(CODEC_I2C_CLK, ENABLE); + + /* CODEC_I2C configuration */ + I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; + I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable; + I2C_InitStructure.I2C_DigitalFilter = 0x00; + I2C_InitStructure.I2C_OwnAddress1 = 0x00; + I2C_InitStructure.I2C_Ack = I2C_Ack_Disable; + I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; + I2C_InitStructure.I2C_Timing = CODEC_I2C_TIMING; + + /* Enable the I2C peripheral */ + I2C_Init(CODEC_I2C, &I2C_InitStructure); + I2C_Cmd(CODEC_I2C, ENABLE); + +} + +/** + * @brief Restore the Audio Codec control interface to its default state. + * This function doesn't de-initialize the I2C because the I2C peripheral + * may be used by other modules. + * @param None + * @retval None + */ +static void Codec_CtrlInterface_DeInit(void) +{ + /* Disable the I2C peripheral */ /* This step is not done here because + the I2C interface can be used by other modules */ + /*I2C_DeInit(CODEC_I2C); */ +} + +/** + * @brief Initializes the Audio Codec audio interface (I2S) + * @note This function assumes that the I2S input clock (through PLL_R in + * Devices RevA/Z and through dedicated PLLI2S_R in Devices RevB/Y) + * is already configured and ready to be used. + * @param AudioFreq: Audio frequency to be configured for the I2S peripheral. + * @retval None + */ +static void Codec_AudioInterface_Init(uint32_t AudioFreq) +{ + I2S_InitTypeDef I2S_InitStructure; + + /* Enable the CODEC_I2S peripheral clock */ + RCC_APB1PeriphClockCmd(CODEC_I2S_CLK, ENABLE); + + /* Deinitialize SPI3_I2S3 peripheral */ + SPI_I2S_DeInit(CODEC_I2S); + + /* CODEC_I2S peripheral configuration */ + SPI_I2S_DeInit(CODEC_I2S); + I2S_InitStructure.I2S_AudioFreq = AudioFreq; + I2S_InitStructure.I2S_Standard = I2S_Standard_MSB; + I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b; + I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low; + I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx; +#ifdef CODEC_MCLK_ENABLED + I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable; +#elif defined(CODEC_MCLK_DISABLED) + I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable; +#else + #error "No selection for the MCLK output has been defined !" +#endif /* CODEC_MCLK_ENABLED */ + + /* Initialize the I2S peripheral with the structure above */ + I2S_Init(CODEC_I2S, &I2S_InitStructure); + + /* The I2S peripheral will be enabled only in the EVAL_AUDIO_Play() function + or by user functions if DMA mode not enabled */ +} + +/** + * @brief Restores the Audio Codec audio interface to its default state. + * @param None + * @retval None + */ +static void Codec_AudioInterface_DeInit(void) +{ + /* Disable the CODEC_I2S peripheral (in case it hasn't already been disabled) */ + I2S_Cmd(CODEC_I2S, DISABLE); + + /* Deinitialize the CODEC_I2S peripheral */ + SPI_I2S_DeInit(CODEC_I2S); + + /* Disable the CODEC_I2S peripheral clock */ + RCC_APB1PeriphClockCmd(CODEC_I2S_CLK, DISABLE); +} + +/** + * @brief Initializes IOs used by the Audio Codec (on the control and audio + * interfaces). + * @param None + * @retval None + */ +static void Codec_GPIO_Init(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + I2S_InitTypeDef I2S_InitStructure; + + /* Enable I2S and I2C GPIO clocks */ + RCC_AHBPeriphClockCmd(CODEC_I2C_GPIO_CLOCK | CODEC_I2S_GPIO_CLOCK, ENABLE); + + /* Connect pins to I2C peripheral */ + GPIO_PinAFConfig(CODEC_I2C_SCL_GPIO, CODEC_I2S_SCL_PINSRC, CODEC_I2C_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2C_SDA_GPIO, CODEC_I2S_SDA_PINSRC, CODEC_I2C_GPIO_AF); + + /* CODEC_I2C SCL and SDA pins configuration ----------------------------------*/ + GPIO_InitStructure.GPIO_Pin = CODEC_I2C_SDA_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(CODEC_I2C_SDA_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = CODEC_I2C_SCL_PIN ; + GPIO_Init(CODEC_I2C_SCL_GPIO, &GPIO_InitStructure); + + /* Connect pins to I2S peripheral */ + GPIO_PinAFConfig(CODEC_I2S_MCK_WS_GPIO, CODEC_I2S_WS_PINSRC, CODEC_I2S_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SCK_PINSRC, CODEC_I2S_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_DIN_PINSRC, CODEC_I2S_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_DOUT_PINSRC, CODEC_I2S_GPIO_AF); + + /* CODEC_I2S pins configuration: WS, SCK, DIN and DOUT pins -------------------------*/ + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_SCK_PIN | CODEC_I2S_DIN_PIN | CODEC_I2S_DOUT_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(CODEC_I2S_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_WS_PIN; + GPIO_Init(CODEC_I2S_MCK_WS_GPIO, &GPIO_InitStructure); + + #ifdef CODEC_MCLK_ENABLED + /* CODEC_I2S pins configuration: MCK pin */ + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_MCK_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(CODEC_I2S_MCK_WS_GPIO, &GPIO_InitStructure); + /* Connect pins to I2S peripheral */ + GPIO_PinAFConfig(CODEC_I2S_MCK_WS_GPIO, CODEC_I2S_MCK_PINSRC, CODEC_I2S_MCK_GPIO_AF); + #endif /* CODEC_MCLK_ENABLED */ + + /* SPI3/I2S3 IRQ Channel configuration */ + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + + NVIC_InitStructure.NVIC_IRQChannel = SPI3_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); + + /* Deinitialize SPI3_I2S3 peripheral */ + SPI_I2S_DeInit(CODEC_I2S); + /* I2S3 configuration ---------------------------------------// + SPI3_I2S3 - configured as follow: + - Work as Master & (transmiter) Rx + - 16bit data + - (Disable) Output MCLK + - Audio sample rate: 11kHz(have to use 8k/24k for CS42L52 ?) + - Default clock polarity: low level */ + I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx; + I2S_InitStructure.I2S_Standard = I2S_Standard_MSB; + I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b; + I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable; + I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_32k; + I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low; + I2S_Init(CODEC_I2S, &I2S_InitStructure); + /* Disable the I2S3 TXE Interrupt */ + SPI_I2S_ITConfig(CODEC_I2S, SPI_I2S_IT_TXE, DISABLE); + /* Enable the SPI3/I2S3 peripheral */ + I2S_Cmd(CODEC_I2S, ENABLE); + +} + +/** + * @brief Initializes IOs used by the Audio Codec (on the control and audio + * interfaces). + * @param None + * @retval None + */ +static void Codec_GPIO_Recorder_Init(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + I2S_InitTypeDef I2S_InitStructure; + + /* Enable I2S and I2C GPIO clocks */ + RCC_AHBPeriphClockCmd(CODEC_I2C_GPIO_CLOCK | CODEC_I2S_GPIO_CLOCK, ENABLE); + + /* Connect pins to I2C peripheral */ + GPIO_PinAFConfig(CODEC_I2C_SCL_GPIO, CODEC_I2S_SCL_PINSRC, CODEC_I2C_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2C_SDA_GPIO, CODEC_I2S_SDA_PINSRC, CODEC_I2C_GPIO_AF); + + /* CODEC_I2C SCL and SDA pins configuration ----------------------------------*/ + GPIO_InitStructure.GPIO_Pin = CODEC_I2C_SDA_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(CODEC_I2C_SDA_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = CODEC_I2C_SCL_PIN ; + GPIO_Init(CODEC_I2C_SCL_GPIO, &GPIO_InitStructure); + + /* Connect pins to I2S peripheral */ + GPIO_PinAFConfig(CODEC_I2S_MCK_WS_GPIO, CODEC_I2S_WS_PINSRC, CODEC_I2S_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SCK_PINSRC, CODEC_I2S_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_DIN_PINSRC, CODEC_I2S_GPIO_AF); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_DOUT_PINSRC, CODEC_I2S_GPIO_AF); + + /* CODEC_I2S pins configuration: WS, SCK, DIN and DOUT pins -------------------------*/ + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_SCK_PIN | CODEC_I2S_DIN_PIN | CODEC_I2S_DOUT_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(CODEC_I2S_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_WS_PIN; + GPIO_Init(CODEC_I2S_MCK_WS_GPIO, &GPIO_InitStructure); + + #ifdef CODEC_MCLK_ENABLED + /* CODEC_I2S pins configuration: MCK pin */ + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_MCK_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(CODEC_I2S_MCK_WS_GPIO, &GPIO_InitStructure); + /* Connect pins to I2S peripheral */ + GPIO_PinAFConfig(CODEC_I2S_MCK_WS_GPIO, CODEC_I2S_MCK_PINSRC, CODEC_I2S_MCK_GPIO_AF); + #endif /* CODEC_MCLK_ENABLED */ + + + /* SPI3/I2S3 IRQ Channel configuration */ + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + + NVIC_InitStructure.NVIC_IRQChannel = SPI3_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); + + /* Deinitialize SPI3_I2S3 peripheral */ + SPI_I2S_DeInit(CODEC_I2S); + /* I2S3 configuration ---------------------------------------// + SPI3_I2S3 - configured as follow: + - Work as Master & (transmiter) Rx + - 16bit data + - (Disable) Output MCLK + - Audio sample rate: 11kHz(have to use 8k/24k for CS42L52) + - Default clock polarity: low level */ + I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx; + I2S_InitStructure.I2S_Standard = I2S_Standard_MSB; + I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b; + I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable; + I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_8k; + I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low; + I2S_Init(CODEC_I2S, &I2S_InitStructure); + /* configure Full Duplex */ + I2S_FullDuplexConfig(I2S3ext, &I2S_InitStructure); + /* Disable the I2S3 TXE Interrupt */ + SPI_I2S_ITConfig(CODEC_I2S, SPI_I2S_IT_TXE, DISABLE); + /* Enable the SPI3/I2S3 peripheral */ + I2S_Cmd(CODEC_I2S, ENABLE); + I2S_Cmd(I2S3ext, ENABLE); +} + +/** + * @brief Restores the IOs used by the Audio Codec interface to their default state. + * @param None + * @retval None + */ +static void Codec_GPIO_DeInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + /* Deinitialize all the GPIOs used by the driver (EXCEPT the I2C IOs since + they are used by the IOExpander as well) */ + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_SCK_PIN | CODEC_I2S_DIN_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(CODEC_I2S_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_WS_PIN; + GPIO_Init(CODEC_I2S_MCK_WS_GPIO, &GPIO_InitStructure); + +#ifdef CODEC_MCLK_ENABLED + /* CODEC_I2S pins deinitialization: MCK pin */ + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_MCK_PIN; + GPIO_Init(CODEC_I2S_MCK_WS_GPIO, &GPIO_InitStructure); + /* Disconnect pins from I2S peripheral */ + GPIO_PinAFConfig(CODEC_I2S_MCK_WS_GPIO, CODEC_I2S_MCK_PINSRC, 0x00); +#endif /* CODEC_MCLK_ENABLED */ + + GPIO_InitStructure.GPIO_Pin = CODEC_I2S_DOUT_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(CODEC_I2S_GPIO, &GPIO_InitStructure); + + /* Disconnect pins from I2S peripheral */ + GPIO_PinAFConfig(CODEC_I2S_MCK_WS_GPIO, CODEC_I2S_WS_PINSRC, 0x00); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SCK_PINSRC, 0x00); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_DIN_PINSRC, 0x00); + GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_DOUT_PINSRC, 0x00); +} + +/** + * @brief Inserts a delay time (not accurate timing). + * @param nCount: specifies the delay time length. + * @retval None + */ +void Delay( __IO uint32_t nCount) +{ + for (; nCount != 0; nCount--); +} + +#ifdef USE_DEFAULT_TIMEOUT_CALLBACK +/** + * @brief Basic management of the timeout situation. + * @param None + * @retval None + */ +uint32_t Codec_TIMEOUT_UserCallback(void) +{ + /* The following code allows I2C error recovery and return to normal communication + if the error source doesn’t still exist (ie. hardware issue..) */ + I2C_InitTypeDef I2C_InitStructure; + + + /* LCD_ErrLog("> I2C Timeout error occurred\n"); */ + I2C_GenerateSTOP(CODEC_I2C, ENABLE); + /* Generated a Software reset */ + I2C_SoftwareResetCmd(CODEC_I2C); + + /* CODEC_I2C configuration */ + I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; + I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable; + I2C_InitStructure.I2C_DigitalFilter = 0x00; + I2C_InitStructure.I2C_OwnAddress1 = 0x00; + I2C_InitStructure.I2C_Ack = I2C_Ack_Disable; + I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; + I2C_InitStructure.I2C_Timing = CODEC_I2C_TIMING; + + /* Enable the I2C peripheral */ + I2C_Init(CODEC_I2C, &I2C_InitStructure); + I2C_Cmd(CODEC_I2C, ENABLE); + + /* At this stage the I2C error should be recovered and device can communicate + again (except if the error source still exist). + User can implement mechanism (ex. test on max trial number) to manage situation + when the I2C can't recover from current error. */ + + /* LCD_UsrLog("> I2C error recovered.\n"); */ + + return 0; +} +#endif /* USE_DEFAULT_TIMEOUT_CALLBACK */ + +/*============================================================================ + Audio MAL Interface Control Functions + ============================================================================*/ + +/** + * @brief Initializes and prepares the Media to perform audio data transfer + * from Media to the I2S peripheral. + * @param None + * @retval None + */ +static void Audio_MAL_Init(void) +{ +#if defined(AUDIO_MAL_DMA_IT_TC_EN) || defined(AUDIO_MAL_DMA_IT_HT_EN) || defined(AUDIO_MAL_DMA_IT_TE_EN) + NVIC_InitTypeDef NVIC_InitStructure; +#endif + + /* Enable the DMA clock */ + RCC_AHBPeriphClockCmd(AUDIO_MAL_DMA_CLOCK, ENABLE); + + /* Configure the DMA Channel */ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL, DISABLE); + DMA_DeInit(AUDIO_MAL_DMA_CHANNEL); + /* Set the parameters to be configured */ + + DMA_InitStructure.DMA_PeripheralBaseAddr = CODEC_I2S_ADDRESS; + DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0; /* This field will be configured in play function */ + DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; + DMA_InitStructure.DMA_BufferSize = (uint32_t)0xFFFE; /* This field will be configured in play function */ + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = AUDIO_MAL_DMA_PERIPH_DATA_SIZE; + DMA_InitStructure.DMA_MemoryDataSize = AUDIO_MAL_DMA_MEM_DATA_SIZE; +#ifdef AUDIO_MAL_MODE_NORMAL + DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; +#elif defined(AUDIO_MAL_MODE_CIRCULAR) + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; +#else + #error "AUDIO_MAL_MODE_NORMAL or AUDIO_MAL_MODE_CIRCULAR should be selected !!" +#endif /* AUDIO_MAL_MODE_NORMAL */ + DMA_Init(AUDIO_MAL_DMA_CHANNEL, &DMA_InitStructure); + + /* Enable the selected DMA interrupts (selected in "stm32_eval_audio_codec.h" defines) */ +#ifdef AUDIO_MAL_DMA_IT_TC_EN + DMA_ITConfig(AUDIO_MAL_DMA_CHANNEL, DMA_IT_TC, ENABLE); +#endif /* AUDIO_MAL_DMA_IT_TC_EN */ +#ifdef AUDIO_MAL_DMA_IT_HT_EN + DMA_ITConfig(AUDIO_MAL_DMA_CHANNEL, DMA_IT_HT, ENABLE); +#endif /* AUDIO_MAL_DMA_IT_HT_EN */ +#ifdef AUDIO_MAL_DMA_IT_TE_EN + DMA_ITConfig(AUDIO_MAL_DMA_CHANNEL, DMA_IT_TE | DMA_IT_FE | DMA_IT_DME, ENABLE); +#endif /* AUDIO_MAL_DMA_IT_TE_EN */ + + /* Enable the I2S DMA request */ + SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Tx, ENABLE); + +#if defined(AUDIO_MAL_DMA_IT_TC_EN) || defined(AUDIO_MAL_DMA_IT_HT_EN) || defined(AUDIO_MAL_DMA_IT_TE_EN) + /* I2S DMA IRQ Channel configuration */ + NVIC_InitStructure.NVIC_IRQChannel = AUDIO_MAL_DMA_IRQ; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = EVAL_AUDIO_IRQ_PREPRIO; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = EVAL_AUDIO_IRQ_SUBRIO; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +#endif +} + +/** + * @brief Restore default state of the used Media. + * @param None + * @retval None + */ +static void Audio_MAL_DeInit(void) +{ +#if defined(AUDIO_MAL_DMA_IT_TC_EN) || defined(AUDIO_MAL_DMA_IT_HT_EN) || defined(AUDIO_MAL_DMA_IT_TE_EN) + NVIC_InitTypeDef NVIC_InitStructure; + + /* Deinitialize the NVIC interrupt for the I2S DMA Channel */ + NVIC_InitStructure.NVIC_IRQChannel = AUDIO_MAL_DMA_IRQ; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = EVAL_AUDIO_IRQ_PREPRIO; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = EVAL_AUDIO_IRQ_SUBRIO; + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); +#endif + + /* Disable the DMA before the deinit */ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL, DISABLE); + + /* Dinitialize the DMA Channel */ + DMA_DeInit(AUDIO_MAL_DMA_CHANNEL); + + /* The DMA clock is not disabled */ +} + +/** + * @brief Starts playing audio stream from the audio Media. + * @param None + * @retval None + */ +void Audio_MAL_Play(uint32_t Addr, uint32_t Size) +{ + /* Configure the buffer address and size */ + DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Addr; + DMA_InitStructure.DMA_BufferSize = (uint32_t)Size/2; + + /* Configure the DMA Channel with the new parameters */ + DMA_Init(AUDIO_MAL_DMA_CHANNEL, &DMA_InitStructure); + + /* Enable the I2S DMA Channel */ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL, ENABLE); +} + +/** + * @brief Pauses or Resumes the audio stream playing from the Media. + * @param Cmd: AUDIO_PAUSE (or 0) to pause, AUDIO_RESUME (or any value different + * from 0) to resume. + * @param Addr: Address from/at which the audio stream should resume/pause. + * @retval None + */ +static void Audio_MAL_PauseResume(uint32_t Cmd, uint32_t Addr) +{ + /* Pause the audio file playing */ + if (Cmd == AUDIO_PAUSE) + { + /* Disable the I2S DMA request */ + SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Tx, DISABLE); + + /* Pause the I2S DMA Channel + Note. For the STM32F30x devices, the DMA implements a pause feature, + by disabling the channel, all configuration is preserved and data + transfer is paused till the next enable of the channel.*/ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL, DISABLE); + } + else /* AUDIO_RESUME */ + { + /* Enable the I2S DMA request */ + SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Tx, ENABLE); + + /* Resume the I2S DMA Channel + Note. For the STM32F30x devices, the DMA implements a pause feature, + by disabling the channel, all configuration is preserved and data + transfer is paused till the next enable of the channel.*/ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL, ENABLE); + + /* If the I2S peripheral is still not enabled, enable it */ + if ((CODEC_I2S->I2SCFGR & I2S_ENABLE_MASK) == 0) + { + I2S_Cmd(CODEC_I2S, ENABLE); + } + } +} + +/** + * @brief Stops audio stream playing on the used Media. + * @param None + * @retval None + */ +static void Audio_MAL_Stop(void) +{ + /* Stop the Transfer on the I2S side: Stop and disable the DMA channel */ + DMA_Cmd(AUDIO_MAL_DMA_CHANNEL, DISABLE); + + /* Clear all the DMA flags for the next transfer */ + DMA_ClearFlag(AUDIO_MAL_DMA_FLAG_TC|AUDIO_MAL_DMA_FLAG_HT|AUDIO_MAL_DMA_FLAG_TE); + + /* The I2S DMA requests are not disabled here. */ + + /* In all modes, disable the I2S peripheral */ + I2S_Cmd(CODEC_I2S, DISABLE); +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |