|
|
/**
|
|
|
******************************************************************************
|
|
|
* @file stm32f4_discovery_audio_codec.c
|
|
|
* @author MCD Application Team
|
|
|
* @version V1.0.0
|
|
|
* @date 19-September-2011
|
|
|
* @brief This file includes the low layer driver for CS43L22 Audio Codec
|
|
|
* available on STM32F4-Discovery Kit.
|
|
|
******************************************************************************
|
|
|
* @attention
|
|
|
*
|
|
|
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
|
|
|
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
|
|
|
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
|
|
|
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
|
|
|
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
|
|
|
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
|
|
|
*
|
|
|
* <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>
|
|
|
******************************************************************************
|
|
|
*/
|
|
|
|
|
|
/*==============================================================================================================================
|
|
|
User NOTES
|
|
|
1. How To use this driver:
|
|
|
--------------------------
|
|
|
- This driver supports STM32F4xx devices on STM32F4-Discovery Kit.
|
|
|
|
|
|
- Configure the options in file stm32f4_discovery_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 (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 stm32f4_discovery_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 stm32f4_discovery_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 stm32f4_discovery_audio_codec.h file
|
|
|
(EVAL_AUDIO_Init(), EVAL_AUDIO_Play() ...)
|
|
|
o Codec Control layer: consists of the functions API controlling the audio codec (CS43L22) and
|
|
|
included as local functions in file stm32f4_discovery_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 stm32f4_discovery_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 "stm32f4_discovery_audio_codec.h"
|
|
|
//ADDED BY ME!!!!!!!!!!!!!!!!!!!!
|
|
|
#include "stm32f4xx_conf.h"
|
|
|
|
|
|
/** @addtogroup Utilities
|
|
|
* @{
|
|
|
*/
|
|
|
|
|
|
/** @addtogroup STM32F4_DISCOVERY
|
|
|
* @{
|
|
|
*/
|
|
|
|
|
|
/** @addtogroup STM32F4_DISCOVERY_AUDIO_CODEC
|
|
|
* @brief This file includes the low layer driver for CS43L22 Audio Codec
|
|
|
* available on STM32F4-Discovery Kit.
|
|
|
* @{
|
|
|
*/
|
|
|
|
|
|
/** @defgroup STM32F4_DISCOVERY_AUDIO_CODEC_Private_Types
|
|
|
* @{
|
|
|
*/
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/** @defgroup STM32F4_DISCOVERY_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 /* b00100111 */
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/** @defgroup STM32F4_DISCOVERY_AUDIO_CODEC_Private_Macros
|
|
|
* @{
|
|
|
*/
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/** @defgroup STM32F4_DISCOVERY_AUDIO_CODEC_Private_Variables
|
|
|
* @{
|
|
|
*/
|
|
|
/* This structure is declared global because it is handled by two different functions */
|
|
|
static 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;
|
|
|
|
|
|
|
|
|
__IO uint32_t CurrAudioInterface = AUDIO_INTERFACE_I2S; //AUDIO_INTERFACE_DAC
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/** @defgroup STM32F4_DISCOVERY_AUDIO_CODEC_Private_Function_Prototypes
|
|
|
* @{
|
|
|
*/
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/** @defgroup STM32F4_DISCOVERY_AUDIO_CODEC_Private_Functions
|
|
|
* @{
|
|
|
*/
|
|
|
static void Audio_MAL_IRQHandler(void);
|
|
|
/*-----------------------------------
|
|
|
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);
|
|
|
static void Codec_Reset(void);
|
|
|
static uint32_t Codec_WriteRegister(uint8_t RegisterAddr, uint8_t RegisterValue);
|
|
|
static uint32_t Codec_ReadRegister(uint8_t RegisterAddr);
|
|
|
static void Codec_GPIO_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_Play(uint32_t Addr, uint32_t Size);
|
|
|
static void Audio_MAL_PauseResume(uint32_t Cmd, uint32_t Addr);
|
|
|
static void Audio_MAL_Stop(void);
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
/* DMA Stream definitions */
|
|
|
uint32_t AUDIO_MAL_DMA_CLOCK = AUDIO_I2S_DMA_CLOCK;
|
|
|
DMA_Stream_TypeDef * AUDIO_MAL_DMA_STREAM = AUDIO_I2S_DMA_STREAM ;
|
|
|
uint32_t AUDIO_MAL_DMA_DREG = AUDIO_I2S_DMA_DREG;
|
|
|
uint32_t AUDIO_MAL_DMA_CHANNEL = AUDIO_I2S_DMA_CHANNEL;
|
|
|
uint32_t AUDIO_MAL_DMA_IRQ = AUDIO_I2S_DMA_IRQ ;
|
|
|
uint32_t AUDIO_MAL_DMA_FLAG_TC = AUDIO_I2S_DMA_FLAG_TC;
|
|
|
uint32_t AUDIO_MAL_DMA_FLAG_HT = AUDIO_I2S_DMA_FLAG_HT;
|
|
|
uint32_t AUDIO_MAL_DMA_FLAG_FE = AUDIO_I2S_DMA_FLAG_FE;
|
|
|
uint32_t AUDIO_MAL_DMA_FLAG_TE = AUDIO_I2S_DMA_FLAG_TE;
|
|
|
uint32_t AUDIO_MAL_DMA_FLAG_DME = AUDIO_I2S_DMA_FLAG_DME;
|
|
|
|
|
|
/**
|
|
|
* @brief Set the current audio interface (I2S or DAC).
|
|
|
* @param Interface: AUDIO_INTERFACE_I2S or AUDIO_INTERFACE_DAC
|
|
|
* @retval None
|
|
|
*/
|
|
|
void EVAL_AUDIO_SetAudioInterface(uint32_t Interface)
|
|
|
{
|
|
|
CurrAudioInterface = Interface;
|
|
|
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_I2S)
|
|
|
{
|
|
|
/* DMA Stream definitions */
|
|
|
AUDIO_MAL_DMA_CLOCK = AUDIO_I2S_DMA_CLOCK;
|
|
|
AUDIO_MAL_DMA_STREAM = AUDIO_I2S_DMA_STREAM;
|
|
|
AUDIO_MAL_DMA_DREG = AUDIO_I2S_DMA_DREG;
|
|
|
AUDIO_MAL_DMA_CHANNEL = AUDIO_I2S_DMA_CHANNEL;
|
|
|
AUDIO_MAL_DMA_IRQ = AUDIO_I2S_DMA_IRQ ;
|
|
|
AUDIO_MAL_DMA_FLAG_TC = AUDIO_I2S_DMA_FLAG_TC;
|
|
|
AUDIO_MAL_DMA_FLAG_HT = AUDIO_I2S_DMA_FLAG_HT;
|
|
|
AUDIO_MAL_DMA_FLAG_FE = AUDIO_I2S_DMA_FLAG_FE;
|
|
|
AUDIO_MAL_DMA_FLAG_TE = AUDIO_I2S_DMA_FLAG_TE;
|
|
|
AUDIO_MAL_DMA_FLAG_DME = AUDIO_I2S_DMA_FLAG_DME;
|
|
|
}
|
|
|
else if (Interface == AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
/* DMA Stream definitions */
|
|
|
AUDIO_MAL_DMA_CLOCK = AUDIO_DAC_DMA_CLOCK;
|
|
|
AUDIO_MAL_DMA_STREAM = AUDIO_DAC_DMA_STREAM;
|
|
|
AUDIO_MAL_DMA_DREG = AUDIO_DAC_DMA_DREG;
|
|
|
AUDIO_MAL_DMA_CHANNEL = AUDIO_DAC_DMA_CHANNEL;
|
|
|
AUDIO_MAL_DMA_IRQ = AUDIO_DAC_DMA_IRQ ;
|
|
|
AUDIO_MAL_DMA_FLAG_TC = AUDIO_DAC_DMA_FLAG_TC;
|
|
|
AUDIO_MAL_DMA_FLAG_HT = AUDIO_DAC_DMA_FLAG_HT;
|
|
|
AUDIO_MAL_DMA_FLAG_FE = AUDIO_DAC_DMA_FLAG_FE;
|
|
|
AUDIO_MAL_DMA_FLAG_TE = AUDIO_DAC_DMA_FLAG_TE;
|
|
|
AUDIO_MAL_DMA_FLAG_DME = AUDIO_DAC_DMA_FLAG_DME;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @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 the Media layer */
|
|
|
Audio_MAL_DeInit();
|
|
|
|
|
|
/* DeInitialize Codec */
|
|
|
Codec_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
|
|
|
*/
|
|
|
static 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_STREAM, 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 Stream to be effectively disabled */
|
|
|
while (DMA_GetCmdStatus(AUDIO_MAL_DMA_STREAM) != DISABLE)
|
|
|
{}
|
|
|
|
|
|
/* Clear the Interrupt flag */
|
|
|
DMA_ClearFlag(AUDIO_MAL_DMA_STREAM, AUDIO_MAL_DMA_FLAG_TC);
|
|
|
|
|
|
/* Re-Configure the buffer address and size */
|
|
|
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) CurrentPos;
|
|
|
DMA_InitStructure.DMA_BufferSize = (uint32_t) (DMA_MAX(AudioRemSize));
|
|
|
|
|
|
/* Configure the DMA Stream with the new parameters */
|
|
|
DMA_Init(AUDIO_MAL_DMA_STREAM, &DMA_InitStructure);
|
|
|
|
|
|
/* Enable the I2S DMA Stream*/
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, 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 Stream*/
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, DISABLE);
|
|
|
|
|
|
/* Clear the Interrupt flag */
|
|
|
DMA_ClearFlag(AUDIO_MAL_DMA_STREAM, 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 stm32f4_discovery_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 stm32f4_discovery_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_STREAM, 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 stm32f4_discovery_audio_codec.h) */
|
|
|
EVAL_AUDIO_HalfTransfer_CallBack((uint32_t)pAddr, Size);
|
|
|
|
|
|
/* Clear the Interrupt flag */
|
|
|
DMA_ClearFlag(AUDIO_MAL_DMA_STREAM, 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_STREAM, AUDIO_MAL_DMA_FLAG_TE) != RESET) || \
|
|
|
(DMA_GetFlagStatus(AUDIO_MAL_DMA_STREAM, AUDIO_MAL_DMA_FLAG_FE) != RESET) || \
|
|
|
(DMA_GetFlagStatus(AUDIO_MAL_DMA_STREAM, 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 stm32f4_discovery_audio_codec.h) */
|
|
|
EVAL_AUDIO_Error_CallBack((uint32_t*)&pAddr);
|
|
|
|
|
|
/* Clear the Interrupt flag */
|
|
|
DMA_ClearFlag(AUDIO_MAL_DMA_STREAM, AUDIO_MAL_DMA_FLAG_TE | AUDIO_MAL_DMA_FLAG_FE | \
|
|
|
AUDIO_MAL_DMA_FLAG_DME);
|
|
|
}
|
|
|
#endif /* AUDIO_MAL_DMA_IT_TE_EN */
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief This function handles main I2S interrupt.
|
|
|
* @param None
|
|
|
* @retval 0 if correct communication, else wrong communication
|
|
|
*/
|
|
|
void Audio_MAL_I2S_IRQHandler(void)
|
|
|
{
|
|
|
Audio_MAL_IRQHandler();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief This function handles main DAC interrupt.
|
|
|
* @param None
|
|
|
* @retval 0 if correct communication, else wrong communication
|
|
|
*/
|
|
|
void Audio_MAL_DAC_IRQHandler(void)
|
|
|
{
|
|
|
Audio_MAL_IRQHandler();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief I2S interrupt management
|
|
|
* @param None
|
|
|
* @retval None
|
|
|
*/
|
|
|
void Audio_I2S_IRQHandler(void)
|
|
|
{
|
|
|
/* Check on the I2S TXE flag */
|
|
|
if (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) != RESET)
|
|
|
{
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
/* Wirte data to the DAC interface */
|
|
|
DAC_SetChannel1Data(DAC_Align_12b_L, EVAL_AUDIO_GetSampleCallBack());
|
|
|
}
|
|
|
|
|
|
/* Send dummy data on I2S to avoid the underrun condition */
|
|
|
SPI_I2S_SendData(CODEC_I2S, EVAL_AUDIO_GetSampleCallBack());
|
|
|
}
|
|
|
}
|
|
|
/*========================
|
|
|
|
|
|
CS43L22 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();
|
|
|
|
|
|
/* Reset the Codec Registers */
|
|
|
Codec_Reset();
|
|
|
|
|
|
/* Initialize the Control interface of the Audio Codec */
|
|
|
Codec_CtrlInterface_Init();
|
|
|
|
|
|
/* Keep Codec powered OFF */
|
|
|
counter += Codec_WriteRegister(0x02, 0x01);
|
|
|
|
|
|
counter += Codec_WriteRegister(0x04, 0xAF); /* SPK always OFF & HP always ON */
|
|
|
OutputDev = 0xAF;
|
|
|
|
|
|
/* Clock configuration: Auto detection */
|
|
|
counter += Codec_WriteRegister(0x05, 0x81);
|
|
|
|
|
|
/* Set the Slave Mode and the audio Standard */
|
|
|
counter += Codec_WriteRegister(0x06, CODEC_STANDARD);
|
|
|
|
|
|
/* Set the Master volume */
|
|
|
Codec_VolumeCtrl(Volume);
|
|
|
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
/* Enable the PassThrough on AIN1A and AIN1B */
|
|
|
counter += Codec_WriteRegister(0x08, 0x01);
|
|
|
counter += Codec_WriteRegister(0x09, 0x01);
|
|
|
|
|
|
/* Route the analog input to the HP line */
|
|
|
counter += Codec_WriteRegister(0x0E, 0xC0);
|
|
|
|
|
|
/* Set the Passthough volume */
|
|
|
counter += Codec_WriteRegister(0x14, 0x00);
|
|
|
counter += Codec_WriteRegister(0x15, 0x00);
|
|
|
}
|
|
|
|
|
|
/* Power on the Codec */
|
|
|
counter += Codec_WriteRegister(0x02, 0x9E);
|
|
|
|
|
|
/* Additional configuration for the CODEC. These configurations are done to reduce
|
|
|
the time needed for the Codec to power off. If these configurations are removed,
|
|
|
then a long delay should be added between powering off the Codec and switching
|
|
|
off the I2S peripheral MCLK clock (which is the operating clock for Codec).
|
|
|
If this delay is not inserted, then the codec will not shut down properly and
|
|
|
it results in high noise after shut down. */
|
|
|
|
|
|
/* Disable the analog soft ramp */
|
|
|
counter += Codec_WriteRegister(0x0A, 0x00);
|
|
|
if (CurrAudioInterface != AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
/* Disable the digital soft ramp */
|
|
|
counter += Codec_WriteRegister(0x0E, 0x04);
|
|
|
}
|
|
|
/* Disable the limiter attack level */
|
|
|
counter += Codec_WriteRegister(0x27, 0x00);
|
|
|
/* Adjust Bass and Treble levels */
|
|
|
counter += Codec_WriteRegister(0x1F, 0x0F);
|
|
|
/* Adjust PCM volume level */
|
|
|
counter += Codec_WriteRegister(0x1A, 0x0A);
|
|
|
counter += Codec_WriteRegister(0x1B, 0x0A);
|
|
|
|
|
|
/* Configure the I2S peripheral */
|
|
|
Codec_AudioInterface_Init(AudioFreq);
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
/* Reset the Codec Registers */
|
|
|
Codec_Reset();
|
|
|
|
|
|
/* Keep Codec powered OFF */
|
|
|
counter += Codec_WriteRegister(0x02, 0x01);
|
|
|
|
|
|
/* Deinitialize all use GPIOs */
|
|
|
Codec_GPIO_DeInit();
|
|
|
|
|
|
/* Disable the Codec control interface */
|
|
|
Codec_CtrlInterface_DeInit();
|
|
|
|
|
|
/* Deinitialize the Codec audio interface (I2S) */
|
|
|
Codec_AudioInterface_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 DAC components */
|
|
|
counter += Codec_WriteRegister(0x02, 0x9F);
|
|
|
|
|
|
/* Wait at least 100us */
|
|
|
Delay(0xFFF);
|
|
|
|
|
|
/* Reset The pin */
|
|
|
GPIO_WriteBit(AUDIO_RESET_GPIO, AUDIO_RESET_PIN, Bit_RESET);
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
else /* AUDIO_MUTE_OFF Disable the Mute */
|
|
|
{
|
|
|
counter += Codec_WriteRegister(0x04, OutputDev);
|
|
|
}
|
|
|
|
|
|
return counter;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Resets the audio codec. It restores the default configuration of the
|
|
|
* codec (this function shall be called before initializing the codec).
|
|
|
* @note This function calls an external driver function: The IO Expander driver.
|
|
|
* @param None
|
|
|
* @retval None
|
|
|
*/
|
|
|
static void Codec_Reset(void)
|
|
|
{
|
|
|
/* Power Down the codec */
|
|
|
GPIO_WriteBit(AUDIO_RESET_GPIO, AUDIO_RESET_PIN, Bit_RESET);
|
|
|
|
|
|
/* wait for a delay to insure registers erasing */
|
|
|
Delay(CODEC_RESET_DELAY);
|
|
|
|
|
|
/* Power on the codec */
|
|
|
GPIO_WriteBit(AUDIO_RESET_GPIO, AUDIO_RESET_PIN, Bit_SET);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @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
|
|
|
*/
|
|
|
static uint32_t Codec_WriteRegister(uint8_t RegisterAddr, uint8_t RegisterValue)
|
|
|
{
|
|
|
uint32_t result = 0;
|
|
|
|
|
|
/*!< While the bus is busy */
|
|
|
CODECTimeout = CODEC_LONG_TIMEOUT;
|
|
|
while(I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BUSY))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Start the config sequence */
|
|
|
I2C_GenerateSTART(CODEC_I2C, ENABLE);
|
|
|
|
|
|
/* Test on EV5 and clear it */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_MODE_SELECT))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Transmit the slave address and enable writing operation */
|
|
|
I2C_Send7bitAddress(CODEC_I2C, CODEC_ADDRESS, I2C_Direction_Transmitter);
|
|
|
|
|
|
/* Test on EV6 and clear it */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Transmit the first address for write operation */
|
|
|
I2C_SendData(CODEC_I2C, RegisterAddr);
|
|
|
|
|
|
/* Test on EV8 and clear it */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Prepare the register value to be sent */
|
|
|
I2C_SendData(CODEC_I2C, RegisterValue);
|
|
|
|
|
|
/*!< Wait till all data have been physically transferred on the bus */
|
|
|
CODECTimeout = CODEC_LONG_TIMEOUT;
|
|
|
while(!I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BTF))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* End the configuration sequence */
|
|
|
I2C_GenerateSTOP(CODEC_I2C, ENABLE);
|
|
|
|
|
|
#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.
|
|
|
*/
|
|
|
static uint32_t Codec_ReadRegister(uint8_t RegisterAddr)
|
|
|
{
|
|
|
uint32_t result = 0;
|
|
|
|
|
|
/*!< While the bus is busy */
|
|
|
CODECTimeout = CODEC_LONG_TIMEOUT;
|
|
|
while(I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BUSY))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Start the config sequence */
|
|
|
I2C_GenerateSTART(CODEC_I2C, ENABLE);
|
|
|
|
|
|
/* Test on EV5 and clear it */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_MODE_SELECT))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Transmit the slave address and enable writing operation */
|
|
|
I2C_Send7bitAddress(CODEC_I2C, CODEC_ADDRESS, I2C_Direction_Transmitter);
|
|
|
|
|
|
/* Test on EV6 and clear it */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/* Transmit the register address to be read */
|
|
|
I2C_SendData(CODEC_I2C, RegisterAddr);
|
|
|
|
|
|
/* Test on EV8 and clear it */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while (I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BTF) == RESET)
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/*!< Send START condition a second time */
|
|
|
I2C_GenerateSTART(CODEC_I2C, ENABLE);
|
|
|
|
|
|
/*!< Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while(!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_MODE_SELECT))
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/*!< Send Codec address for read */
|
|
|
I2C_Send7bitAddress(CODEC_I2C, CODEC_ADDRESS, I2C_Direction_Receiver);
|
|
|
|
|
|
/* Wait on ADDR flag to be set (ADDR is still not cleared at this level */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while(I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_ADDR) == RESET)
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/*!< Disable Acknowledgment */
|
|
|
I2C_AcknowledgeConfig(CODEC_I2C, DISABLE);
|
|
|
|
|
|
/* Clear ADDR register by reading SR1 then SR2 register (SR1 has already been read) */
|
|
|
(void)CODEC_I2C->SR2;
|
|
|
|
|
|
/*!< Send STOP Condition */
|
|
|
I2C_GenerateSTOP(CODEC_I2C, ENABLE);
|
|
|
|
|
|
/* Wait for the byte to be received */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while(I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_RXNE) == RESET)
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/*!< Read the byte received from the Codec */
|
|
|
result = I2C_ReceiveData(CODEC_I2C);
|
|
|
|
|
|
/* Wait to make sure that STOP flag has been cleared */
|
|
|
CODECTimeout = CODEC_FLAG_TIMEOUT;
|
|
|
while(CODEC_I2C->CR1 & I2C_CR1_STOP)
|
|
|
{
|
|
|
if((CODECTimeout--) == 0) return Codec_TIMEOUT_UserCallback();
|
|
|
}
|
|
|
|
|
|
/*!< Re-Enable Acknowledgment to be ready for another reception */
|
|
|
I2C_AcknowledgeConfig(CODEC_I2C, ENABLE);
|
|
|
|
|
|
/* Clear AF flag for next communication */
|
|
|
I2C_ClearFlag(CODEC_I2C, I2C_FLAG_AF);
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
/* Enable the CODEC_I2C peripheral clock */
|
|
|
RCC_APB1PeriphClockCmd(CODEC_I2C_CLK, ENABLE);
|
|
|
|
|
|
/* CODEC_I2C peripheral configuration */
|
|
|
I2C_DeInit(CODEC_I2C);
|
|
|
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
|
|
|
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
|
|
|
I2C_InitStructure.I2C_OwnAddress1 = 0x33;
|
|
|
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
|
|
|
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
|
|
|
I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;
|
|
|
/* Enable the I2C peripheral */
|
|
|
I2C_Cmd(CODEC_I2C, ENABLE);
|
|
|
I2C_Init(CODEC_I2C, &I2C_InitStructure);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @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;
|
|
|
DAC_InitTypeDef DAC_InitStructure;
|
|
|
|
|
|
/* Enable the CODEC_I2S peripheral clock */
|
|
|
RCC_APB1PeriphClockCmd(CODEC_I2S_CLK, ENABLE);
|
|
|
|
|
|
/* CODEC_I2S peripheral configuration */
|
|
|
SPI_I2S_DeInit(CODEC_I2S);
|
|
|
I2S_InitStructure.I2S_AudioFreq = AudioFreq;
|
|
|
I2S_InitStructure.I2S_Standard = I2S_STANDARD;
|
|
|
I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;
|
|
|
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
|
|
|
#ifdef DAC_USE_I2S_DMA
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#else
|
|
|
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
|
|
|
#endif
|
|
|
#ifdef DAC_USE_I2S_DMA
|
|
|
}
|
|
|
#endif /* DAC_USE_I2S_DMA */
|
|
|
#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);
|
|
|
|
|
|
|
|
|
/* Configure the DAC interface */
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
/* DAC Periph clock enable */
|
|
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
|
|
|
|
|
|
/* DAC channel1 Configuration */
|
|
|
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
|
|
|
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
|
|
|
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
|
|
|
DAC_Init(AUDIO_DAC_CHANNEL, &DAC_InitStructure);
|
|
|
|
|
|
/* Enable DAC Channel1 */
|
|
|
DAC_Cmd(AUDIO_DAC_CHANNEL, ENABLE);
|
|
|
}
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
/* Enable Reset GPIO Clock */
|
|
|
RCC_AHB1PeriphClockCmd(AUDIO_RESET_GPIO_CLK,ENABLE);
|
|
|
|
|
|
/* Audio reset pin configuration -------------------------------------------------*/
|
|
|
GPIO_InitStructure.GPIO_Pin = AUDIO_RESET_PIN;
|
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
|
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
|
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
|
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
|
|
GPIO_Init(AUDIO_RESET_GPIO, &GPIO_InitStructure);
|
|
|
|
|
|
/* Enable I2S and I2C GPIO clocks */
|
|
|
RCC_AHB1PeriphClockCmd(CODEC_I2C_GPIO_CLOCK | CODEC_I2S_GPIO_CLOCK, ENABLE);
|
|
|
|
|
|
/* CODEC_I2C SCL and SDA pins configuration -------------------------------------*/
|
|
|
GPIO_InitStructure.GPIO_Pin = CODEC_I2C_SCL_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_NOPULL;
|
|
|
GPIO_Init(CODEC_I2C_GPIO, &GPIO_InitStructure);
|
|
|
/* Connect pins to I2C peripheral */
|
|
|
GPIO_PinAFConfig(CODEC_I2C_GPIO, CODEC_I2S_SCL_PINSRC, CODEC_I2C_GPIO_AF);
|
|
|
GPIO_PinAFConfig(CODEC_I2C_GPIO, CODEC_I2S_SDA_PINSRC, CODEC_I2C_GPIO_AF);
|
|
|
|
|
|
/* CODEC_I2S pins configuration: WS, SCK and SD pins -----------------------------*/
|
|
|
GPIO_InitStructure.GPIO_Pin = CODEC_I2S_SCK_PIN | CODEC_I2S_SD_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);
|
|
|
|
|
|
/* Connect pins to I2S peripheral */
|
|
|
GPIO_PinAFConfig(CODEC_I2S_WS_GPIO, CODEC_I2S_WS_PINSRC, CODEC_I2S_GPIO_AF);
|
|
|
GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SCK_PINSRC, CODEC_I2S_GPIO_AF);
|
|
|
|
|
|
if (CurrAudioInterface != AUDIO_INTERFACE_DAC)
|
|
|
{
|
|
|
GPIO_InitStructure.GPIO_Pin = CODEC_I2S_WS_PIN ;
|
|
|
GPIO_Init(CODEC_I2S_WS_GPIO, &GPIO_InitStructure);
|
|
|
GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SD_PINSRC, CODEC_I2S_GPIO_AF);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/* GPIOA clock enable (to be used with DAC) */
|
|
|
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
|
|
|
|
|
/* DAC channel 1 & 2 (DAC_OUT1 = PA.4) configuration */
|
|
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
|
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
|
|
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
|
|
GPIO_Init(GPIOA, &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_GPIO, &GPIO_InitStructure);
|
|
|
/* Connect pins to I2S peripheral */
|
|
|
GPIO_PinAFConfig(CODEC_I2S_MCK_GPIO, CODEC_I2S_MCK_PINSRC, CODEC_I2S_GPIO_AF);
|
|
|
#endif /* CODEC_MCLK_ENABLED */
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @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 */
|
|
|
GPIO_InitStructure.GPIO_Pin = CODEC_I2S_SCK_PIN | CODEC_I2S_SD_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_WS_GPIO, &GPIO_InitStructure);
|
|
|
|
|
|
/* Disconnect pins from I2S peripheral */
|
|
|
GPIO_PinAFConfig(CODEC_I2S_WS_GPIO, CODEC_I2S_WS_PINSRC, 0x00);
|
|
|
GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SCK_PINSRC, 0x00);
|
|
|
GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SD_PINSRC, 0x00);
|
|
|
|
|
|
#ifdef CODEC_MCLK_ENABLED
|
|
|
/* CODEC_I2S pins deinitialization: MCK pin */
|
|
|
GPIO_InitStructure.GPIO_Pin = CODEC_I2S_MCK_PIN;
|
|
|
GPIO_Init(CODEC_I2S_MCK_GPIO, &GPIO_InitStructure);
|
|
|
/* Disconnect pins from I2S peripheral */
|
|
|
GPIO_PinAFConfig(CODEC_I2S_MCK_GPIO, CODEC_I2S_MCK_PINSRC, CODEC_I2S_GPIO_AF);
|
|
|
#endif /* CODEC_MCLK_ENABLED */
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Inserts a delay time (not accurate timing).
|
|
|
* @param nCount: specifies the delay time length.
|
|
|
* @retval None
|
|
|
*/
|
|
|
static 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)
|
|
|
{
|
|
|
/* Block communication and all processes */
|
|
|
while (1)
|
|
|
{
|
|
|
}
|
|
|
}
|
|
|
#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)
|
|
|
{
|
|
|
|
|
|
#ifdef I2S_INTERRUPT
|
|
|
NVIC_InitTypeDef NVIC_InitStructure;
|
|
|
|
|
|
NVIC_InitStructure.NVIC_IRQChannel = SPI3_IRQn;
|
|
|
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
|
|
|
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;
|
|
|
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
|
NVIC_Init(&NVIC_InitStructure);
|
|
|
|
|
|
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE, ENABLE);
|
|
|
|
|
|
I2S_Cmd(SPI3, ENABLE);
|
|
|
#else
|
|
|
#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
|
|
|
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_I2S)
|
|
|
{
|
|
|
/* Enable the DMA clock */
|
|
|
RCC_AHB1PeriphClockCmd(AUDIO_MAL_DMA_CLOCK, ENABLE);
|
|
|
|
|
|
/* Configure the DMA Stream */
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, DISABLE);
|
|
|
DMA_DeInit(AUDIO_MAL_DMA_STREAM);
|
|
|
/* Set the parameters to be configured */
|
|
|
DMA_InitStructure.DMA_Channel = AUDIO_MAL_DMA_CHANNEL;
|
|
|
DMA_InitStructure.DMA_PeripheralBaseAddr = AUDIO_MAL_DMA_DREG;
|
|
|
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0; /* This field will be configured in play function */
|
|
|
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
|
|
|
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_InitStructure.DMA_Priority = DMA_Priority_High;
|
|
|
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
|
|
|
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
|
|
|
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
|
|
|
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
|
|
|
DMA_Init(AUDIO_MAL_DMA_STREAM, &DMA_InitStructure);
|
|
|
|
|
|
/* Enable the selected DMA interrupts (selected in "stm32f4_discovery_eval_audio_codec.h" defines) */
|
|
|
#ifdef AUDIO_MAL_DMA_IT_TC_EN
|
|
|
DMA_ITConfig(AUDIO_MAL_DMA_STREAM, DMA_IT_TC, ENABLE);
|
|
|
#endif /* AUDIO_MAL_DMA_IT_TC_EN */
|
|
|
#ifdef AUDIO_MAL_DMA_IT_HT_EN
|
|
|
DMA_ITConfig(AUDIO_MAL_DMA_STREAM, DMA_IT_HT, ENABLE);
|
|
|
#endif /* AUDIO_MAL_DMA_IT_HT_EN */
|
|
|
#ifdef AUDIO_MAL_DMA_IT_TE_EN
|
|
|
DMA_ITConfig(AUDIO_MAL_DMA_STREAM, DMA_IT_TE | DMA_IT_FE | DMA_IT_DME, ENABLE);
|
|
|
#endif /* AUDIO_MAL_DMA_IT_TE_EN */
|
|
|
|
|
|
#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
|
|
|
}
|
|
|
|
|
|
#ifdef DAC_USE_I2S_DMA
|
|
|
else
|
|
|
{
|
|
|
/* Enable the DMA clock */
|
|
|
RCC_AHB1PeriphClockCmd(AUDIO_MAL_DMA_CLOCK, ENABLE);
|
|
|
|
|
|
/* Configure the DMA Stream */
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, DISABLE);
|
|
|
DMA_DeInit(AUDIO_MAL_DMA_STREAM);
|
|
|
/* Set the parameters to be configured */
|
|
|
DMA_InitStructure.DMA_Channel = AUDIO_MAL_DMA_CHANNEL;
|
|
|
DMA_InitStructure.DMA_PeripheralBaseAddr = AUDIO_MAL_DMA_DREG;
|
|
|
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0; /* This field will be configured in play function */
|
|
|
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
|
|
|
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_InitStructure.DMA_Priority = DMA_Priority_High;
|
|
|
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
|
|
|
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
|
|
|
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
|
|
|
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
|
|
|
DMA_Init(AUDIO_MAL_DMA_STREAM, &DMA_InitStructure);
|
|
|
|
|
|
/* Enable the selected DMA interrupts (selected in "stm32f4_discovery_eval_audio_codec.h" defines) */
|
|
|
#ifdef AUDIO_MAL_DMA_IT_TC_EN
|
|
|
DMA_ITConfig(AUDIO_MAL_DMA_STREAM, DMA_IT_TC, ENABLE);
|
|
|
#endif /* AUDIO_MAL_DMA_IT_TC_EN */
|
|
|
#ifdef AUDIO_MAL_DMA_IT_HT_EN
|
|
|
DMA_ITConfig(AUDIO_MAL_DMA_STREAM, DMA_IT_HT, ENABLE);
|
|
|
#endif /* AUDIO_MAL_DMA_IT_HT_EN */
|
|
|
#ifdef AUDIO_MAL_DMA_IT_TE_EN
|
|
|
DMA_ITConfig(AUDIO_MAL_DMA_STREAM, DMA_IT_TE | DMA_IT_FE | DMA_IT_DME, ENABLE);
|
|
|
#endif /* AUDIO_MAL_DMA_IT_TE_EN */
|
|
|
|
|
|
#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
|
|
|
}
|
|
|
#endif /* DAC_USE_I2S_DMA */
|
|
|
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_I2S)
|
|
|
{
|
|
|
/* Enable the I2S DMA request */
|
|
|
SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Tx, ENABLE);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/* Configure the STM32 DAC to geenrate audio analog signal */
|
|
|
DAC_Config();
|
|
|
|
|
|
#ifndef DAC_USE_I2S_DMA
|
|
|
/* Enable the I2S interrupt used to write into the DAC register */
|
|
|
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE, ENABLE);
|
|
|
|
|
|
/* I2S DMA IRQ Channel configuration */
|
|
|
NVIC_InitStructure.NVIC_IRQChannel = CODEC_I2S_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);
|
|
|
#else
|
|
|
/* Enable the I2S DMA request */
|
|
|
SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Rx, ENABLE);
|
|
|
#endif /* DAC_USE_I2S_DMA */
|
|
|
}
|
|
|
#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 Stream */
|
|
|
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 stream before the deinit */
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, DISABLE);
|
|
|
|
|
|
/* Dinitialize the DMA Stream */
|
|
|
DMA_DeInit(AUDIO_MAL_DMA_STREAM);
|
|
|
|
|
|
/*
|
|
|
The DMA clock is not disabled, since it can be used by other streams
|
|
|
*/
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Starts playing audio stream from the audio Media.
|
|
|
* @param None
|
|
|
* @retval None
|
|
|
*/
|
|
|
static void Audio_MAL_Play(uint32_t Addr, uint32_t Size)
|
|
|
{
|
|
|
if (CurrAudioInterface == AUDIO_INTERFACE_I2S)
|
|
|
{
|
|
|
/* Configure the buffer address and size */
|
|
|
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Addr;
|
|
|
DMA_InitStructure.DMA_BufferSize = (uint32_t)Size;
|
|
|
|
|
|
/* Configure the DMA Stream with the new parameters */
|
|
|
DMA_Init(AUDIO_MAL_DMA_STREAM, &DMA_InitStructure);
|
|
|
|
|
|
/* Enable the I2S DMA Stream*/
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, ENABLE);
|
|
|
}
|
|
|
#ifndef DAC_USE_I2S_DMA
|
|
|
else
|
|
|
{
|
|
|
/* Configure the buffer address and size */
|
|
|
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Addr;
|
|
|
DMA_InitStructure.DMA_BufferSize = (uint32_t)Size;
|
|
|
|
|
|
/* Configure the DMA Stream with the new parameters */
|
|
|
DMA_Init(AUDIO_MAL_DMA_STREAM, &DMA_InitStructure);
|
|
|
|
|
|
/* Enable the I2S DMA Stream*/
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, ENABLE);
|
|
|
}
|
|
|
#endif /* DAC_USE_I2S_DMA */
|
|
|
|
|
|
/* If the I2S peripheral is still not enabled, enable it */
|
|
|
if ((CODEC_I2S->I2SCFGR & I2S_ENABLE_MASK) == 0)
|
|
|
{
|
|
|
I2S_Cmd(CODEC_I2S, 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 Stream
|
|
|
Note. For the STM32F4xx devices, the DMA implements a pause feature,
|
|
|
by disabling the stream, all configuration is preserved and data
|
|
|
transfer is paused till the next enable of the stream.
|
|
|
This feature is not available on STM32F4xx devices. */
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, DISABLE);
|
|
|
}
|
|
|
else /* AUDIO_RESUME */
|
|
|
{
|
|
|
/* Enable the I2S DMA request */
|
|
|
SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Tx, ENABLE);
|
|
|
|
|
|
/* Resume the I2S DMA Stream
|
|
|
Note. For the STM32F4xx devices, the DMA implements a pause feature,
|
|
|
by disabling the stream, all configuration is preserved and data
|
|
|
transfer is paused till the next enable of the stream.
|
|
|
This feature is not available on STM32F4xx devices. */
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, 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 stream */
|
|
|
DMA_Cmd(AUDIO_MAL_DMA_STREAM, DISABLE);
|
|
|
|
|
|
/* Clear all the DMA flags for the next transfer */
|
|
|
DMA_ClearFlag(AUDIO_MAL_DMA_STREAM, AUDIO_MAL_DMA_FLAG_TC |AUDIO_MAL_DMA_FLAG_HT | \
|
|
|
AUDIO_MAL_DMA_FLAG_FE | 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);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief DAC Channel1 Configuration
|
|
|
* @param None
|
|
|
* @retval None
|
|
|
*/
|
|
|
void DAC_Config(void)
|
|
|
{
|
|
|
DAC_InitTypeDef DAC_InitStructure;
|
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
|
|
|
|
/* DMA1 clock and GPIOA clock enable (to be used with DAC) */
|
|
|
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_GPIOA, ENABLE);
|
|
|
|
|
|
/* DAC Periph clock enable */
|
|
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
|
|
|
|
|
|
/* DAC channel 1 & 2 (DAC_OUT1 = PA.4) configuration */
|
|
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
|
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
|
|
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
|
|
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
|
|
|
|
|
/* DAC channel1 Configuration */
|
|
|
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
|
|
|
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
|
|
|
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
|
|
|
DAC_Init(AUDIO_DAC_CHANNEL, &DAC_InitStructure);
|
|
|
|
|
|
/* Enable DAC Channel1 */
|
|
|
DAC_Cmd(AUDIO_DAC_CHANNEL, ENABLE);
|
|
|
}
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/**
|
|
|
* @}
|
|
|
*/
|
|
|
|
|
|
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
|
|
|
|