xref: /btstack/port/stm32-f4discovery-usb/bsp/stm32f4_discovery_audio.c (revision e0a94143c1f0305eb5125b599df7b54ca2d10b42)
1 /**
2   ******************************************************************************
3   * @file    stm32f4_discovery_audio.c
4   * @author  MCD Application Team
5   * @version V2.1.2
6   * @date    27-January-2017
7   * @brief   This file provides the Audio driver for the STM32F4-Discovery
8   *          board.
9   ******************************************************************************
10   * @attention
11   *
12   * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
13   *
14   * Redistribution and use in source and binary forms, with or without modification,
15   * are permitted provided that the following conditions are met:
16   *   1. Redistributions of source code must retain the above copyright notice,
17   *      this list of conditions and the following disclaimer.
18   *   2. Redistributions in binary form must reproduce the above copyright notice,
19   *      this list of conditions and the following disclaimer in the documentation
20   *      and/or other materials provided with the distribution.
21   *   3. Neither the name of STMicroelectronics nor the names of its contributors
22   *      may be used to endorse or promote products derived from this software
23   *      without specific prior written permission.
24   *
25   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35   *
36   ******************************************************************************
37   */
38 
39 /*==============================================================================
40                                              User NOTES
41 1. How To use this driver:
42 --------------------------
43    - This driver supports STM32F4xx devices on STM32F4-Discovery Kit:
44         a) to play an audio file (all functions names start by BSP_AUDIO_OUT_xxx)
45         b) to record an audio file through MP45DT02, ST MEMS (all functions names start by AUDIO_IN_xxx)
46 
47 a) PLAY A FILE:
48 ==============
49    + Call the function BSP_AUDIO_OUT_Init(
50                                     OutputDevice: physical output mode (OUTPUT_DEVICE_SPEAKER,
51                                                  OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_AUTO or
52                                                  OUTPUT_DEVICE_BOTH)
53                                     Volume: initial volume to be set (0 is min (mute), 100 is max (100%)
54                                     AudioFreq: Audio frequency in Hz (8000, 16000, 22500, 32000 ...)
55                                     this parameter is relative to the audio file/stream type.
56                                    )
57       This function configures all the hardware required for the audio application (codec, I2C, I2S,
58       GPIOs, DMA and interrupt if needed). This function returns 0 if configuration is OK.
59       If the returned value is different from 0 or the function is stuck then the communication with
60       the codec (try to un-plug the power or reset device in this case).
61       - OUTPUT_DEVICE_SPEAKER: only speaker will be set as output for the audio stream.
62       - OUTPUT_DEVICE_HEADPHONE: only headphones will be set as output for the audio stream.
63       - OUTPUT_DEVICE_AUTO: Selection of output device is made through external switch (implemented
64          into the audio jack on the discovery board). When the Headphone is connected it is used
65          as output. When the headphone is disconnected from the audio jack, the output is
66          automatically switched to Speaker.
67       - OUTPUT_DEVICE_BOTH: both Speaker and Headphone are used as outputs for the audio stream
68          at the same time.
69    + Call the function BSP_AUDIO_OUT_Play(
70                                   pBuffer: pointer to the audio data file address
71                                   Size: size of the buffer to be sent in Bytes
72                                  )
73       to start playing (for the first time) from the audio file/stream.
74    + Call the function BSP_AUDIO_OUT_Pause() to pause playing
75    + Call the function BSP_AUDIO_OUT_Resume() to resume playing.
76        Note. After calling BSP_AUDIO_OUT_Pause() function for pause, only BSP_AUDIO_OUT_Resume() should be called
77           for resume (it is not allowed to call BSP_AUDIO_OUT_Play() in this case).
78        Note. This function should be called only when the audio file is played or paused (not stopped).
79    + For each mode, you may need to implement the relative callback functions into your code.
80       The Callback functions are named BSP_AUDIO_OUT_XXXCallBack() and only their prototypes are declared in
81       the stm32f4_discovery_audio.h file. (refer to the example for more details on the callbacks implementations)
82    + To Stop playing, to modify the volume level, the frequency or to mute, use the functions
83        BSP_AUDIO_OUT_Stop(), BSP_AUDIO_OUT_SetVolume(), AUDIO_OUT_SetFrequency() BSP_AUDIO_OUT_SetOutputMode and BSP_AUDIO_OUT_SetMute().
84    + The driver API and the callback functions are at the end of the stm32f4_discovery_audio.h file.
85 
86 Driver architecture:
87 --------------------
88    + This driver provide the High Audio Layer: consists of the function API exported in the stm32f4_discovery_audio.h file
89        (BSP_AUDIO_OUT_Init(), BSP_AUDIO_OUT_Play() ...)
90    + This driver provide also the Media Access Layer (MAL): which consists of functions allowing to access the media containing/
91        providing the audio file/stream. These functions are also included as local functions into
92        the stm32f4_discovery_audio.c file (I2S3_Init()...)
93 
94 Known Limitations:
95 -------------------
96    1- When using the Speaker, if the audio file quality is not high enough, the speaker output
97       may produce high and uncomfortable noise level. To avoid this issue, to use speaker
98       output properly, try to increase audio file sampling rate (typically higher than 48KHz).
99       This operation will lead to larger file size.
100    2- Communication with the audio codec (through I2C) may be corrupted if it is interrupted by some
101       user interrupt routines (in this case, interrupts could be disabled just before the start of
102       communication then re-enabled when it is over). Note that this communication is only done at
103       the configuration phase (BSP_AUDIO_OUT_Init() or BSP_AUDIO_OUT_Stop()) and when Volume control modification is
104       performed (BSP_AUDIO_OUT_SetVolume() or BSP_AUDIO_OUT_SetMute()or BSP_AUDIO_OUT_SetOutputMode()).
105       When the audio data is played, no communication is required with the audio codec.
106    3- Parsing of audio file is not implemented (in order to determine audio file properties: Mono/Stereo, Data size,
107       File size, Audio Frequency, Audio Data header size ...). The configuration is fixed for the given audio file.
108    4- Supports only Stereo audio streaming. To play mono audio streams, each data should be sent twice
109       on the I2S or should be duplicated on the source buffer. Or convert the stream in stereo before playing.
110    5- Supports only 16-bits audio data size.
111 
112 b) RECORD A FILE:
113 ================
114    + Call the function BSP_AUDIO_IN_Init(
115                                     AudioFreq: Audio frequency in Hz (8000, 16000, 22500, 32000 ...)
116                                     )
117       This function configures all the hardware required for the audio application (I2S,
118       GPIOs, DMA and interrupt if needed). This function returns 0 if configuration is OK.
119 
120    + Call the function BSP_AUDIO_IN_Record(
121                             pbuf Main buffer pointer for the recorded data storing
122                             size Current size of the recorded buffer
123                             )
124       to start recording from the microphone.
125 
126    + User needs to implement user callbacks to retrieve data saved in the record buffer
127       (AUDIO_IN_RxHalfCpltCallback/BSP_AUDIO_IN_ReceiveComplete_CallBack)
128 
129    + Call the function AUDIO_IN_STOP() to stop recording
130 
131 ==============================================================================*/
132 
133 /* Includes ------------------------------------------------------------------*/
134 #include "stm32f4_discovery_audio.h"
135 #include "pdm_filter.h"
136 #include "btstack_config.h"
137 
138 /** @addtogroup BSP
139   * @{
140   */
141 
142 /** @addtogroup STM32F4_DISCOVERY
143   * @{
144   */
145 
146 /** @defgroup STM32F4_DISCOVERY_AUDIO STM32F4 DISCOVERY AUDIO
147   * @brief This file includes the low layer audio driver available on STM32F4-Discovery
148   *        discovery board.
149   * @{
150   */
151 
152 /** @defgroup STM32F4_DISCOVERY_AUDIO_Private_Types STM32F4 DISCOVERY AUDIO Private Types
153   * @{
154   */
155 /**
156   * @}
157   */
158 
159 /** @defgroup STM32F4_DISCOVERY_AUDIO_Private_Defines STM32F4 DISCOVERY AUDIO Private Defines
160   * @{
161   */
162 static uint32_t bsp_audio_out_actual_frequency = 0;
163 static uint32_t bsp_audio_out_frequency = 0;
164 
165 typedef struct {
166     uint32_t freq;
167     uint32_t actual;
168     uint16_t r;
169     uint16_t n;
170 } i2s_pll_entry_t;
171 
172 // HSE_VALUE = 8000000
173 // PLLM = 4
174 // MCK on
175 static const i2s_pll_entry_t i2s_pll_table[] = {
176     {  8000,  8000, 5, 128 }, /* i2sdiv:  12, odd: 1, rate error % (desired vs actual)%: 0.0000 */
177     { 11025, 11024, 2, 127 }, /* i2sdiv:  22, odd: 1, rate error % (desired vs actual)%: 0.0063 */
178     { 12000, 12000, 5, 192 }, /* i2sdiv:  12, odd: 1, rate error % (desired vs actual)%: 0.0000 */
179     { 16000, 16000, 4, 213 }, /* i2sdiv:  13, odd: 0, rate error % (desired vs actual)%: 0.0038 */
180     { 22050, 22048, 5, 127 }, /* i2sdiv:   4, odd: 1, rate error % (desired vs actual)%: 0.0063 */
181     { 24000, 24003, 3, 212 }, /* i2sdiv:  11, odd: 1, rate error % (desired vs actual)%: 0.0151 */
182     { 32000, 32001, 4, 213 }, /* i2sdiv:   6, odd: 1, rate error % (desired vs actual)%: 0.0038 */
183     { 44100, 44084, 2,  79 }, /* i2sdiv:   3, odd: 1, rate error % (desired vs actual)%: 0.0344 */
184     { 48000, 47991, 2,  86 }, /* i2sdiv:   3, odd: 1, rate error % (desired vs actual)%: 0.0186 */
185     { 96000, 95982, 2, 172 }, /* i2sdiv:   3, odd: 1, rate error % (desired vs actual)%: 0.0186 */
186 };
187 
188 #define BARRIER do { __asm__ volatile("" ::: "memory"); } while (0)
189 #define BINARY(I) do {                                            \
190                 base = ((base)[I].freq <= frequency)?base+I:base; \
191                 BARRIER;                                          \
192         } while (0)
193 
194 /** @brief  Searches I2S PLL parameter giving highest frequency accuracy possible
195   * @param  frequency: the frequency to seach PLL parameter for
196   * @retval The PLL config with helps setting specified frequency
197   * @note   This assembles a unrolled binary search for a fixed table size,
198   *         so changes to the table size need to be reflected here. Or should
199   *         be auto-generated
200   */
i2s_find_pll_params(uint32_t frequency)201 static i2s_pll_entry_t const *i2s_find_pll_params( uint32_t frequency ) {
202     i2s_pll_entry_t const* base = i2s_pll_table;
203     BINARY(5);
204     BINARY(2);
205     BINARY(1);
206     BINARY(1);
207     return base;
208 }
209 /**
210   * @}
211   */
212 
213 /** @defgroup STM32F4_DISCOVERY_AUDIO_Private_Macros STM32F4 DISCOVERY AUDIO Private Macros
214   * @{
215   */
216 /**
217   * @}
218   */
219 
220 /** @defgroup STM32F4_DISCOVERY_AUDIO_Private_Variables STM32F4 DISCOVERY AUDIO Private Variables
221   * @{
222   */
223 /*##### PLAY #####*/
224 static AUDIO_DrvTypeDef           *pAudioDrv;
225 I2S_HandleTypeDef                 hAudioOutI2s;
226 
227 /*### RECORDER ###*/
228 I2S_HandleTypeDef                 hAudioInI2s;
229 
230 static PDMFilter_InitStruct Filter[DEFAULT_AUDIO_IN_CHANNEL_NBR];
231 __IO uint16_t AudioInVolume = DEFAULT_AUDIO_IN_VOLUME;
232 /**
233   * @}
234   */
235 
236 /** @defgroup STM32F4_DISCOVERY_AUDIO_Private_Function_Prototypes STM32F4 DISCOVERY AUDIO Private Function Prototypes
237   * @{
238   */
239 static uint8_t I2S2_Init(uint32_t AudioFreq);
240 static uint8_t I2S3_Init(uint32_t AudioFreq);
241 static void PDMDecoder_Init(uint32_t AudioFreq, uint32_t ChnlNbr);
242 
243 /**
244   * @}
245   */
246 
247 /** @defgroup STM32F4_DISCOVERY_AUDIO_OUT_Private_Functions STM32F4 DISCOVERY AUDIO OUT Private Functions
248   * @{
249   */
250 
251 /**
252   * @brief  Configures the audio peripherals.
253   * @param  OutputDevice: OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE,
254   *                       OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO .
255   * @param  Volume: Initial volume level (from 0 (Mute) to 100 (Max))
256   * @param  AudioFreq: Audio frequency used to play the audio stream.
257   * @retval AUDIO_OK if correct communication, else wrong communication
258   */
BSP_AUDIO_OUT_Init(uint16_t OutputDevice,uint8_t Volume,uint32_t AudioFreq)259 uint8_t BSP_AUDIO_OUT_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq)
260 {
261   uint8_t ret = AUDIO_OK;
262 
263   /* PLL clock is set depending by the AudioFreq (44.1khz vs 48khz groups) */
264   BSP_AUDIO_OUT_ClockConfig(&hAudioOutI2s, AudioFreq, NULL);
265 
266   /* I2S data transfer preparation:
267   Prepare the Media to be used for the audio transfer from memory to I2S peripheral */
268   hAudioOutI2s.Instance = I2S3;
269   if(HAL_I2S_GetState(&hAudioOutI2s) == HAL_I2S_STATE_RESET)
270   {
271     /* Init the I2S MSP: this __weak function can be redefined by the application*/
272     BSP_AUDIO_OUT_MspInit(&hAudioOutI2s, NULL);
273   }
274 
275   /* I2S data transfer preparation:
276   Prepare the Media to be used for the audio transfer from memory to I2S peripheral */
277   /* Configure the I2S peripheral */
278   if(I2S3_Init(AudioFreq) != AUDIO_OK)
279   {
280     ret = AUDIO_ERROR;
281   }
282 
283   if(ret == AUDIO_OK)
284   {
285     /* Retieve audio codec identifier */
286     if(((cs43l22_drv.ReadID(AUDIO_I2C_ADDRESS)) & CS43L22_ID_MASK) == CS43L22_ID)
287     {
288       /* Initialize the audio driver structure */
289       pAudioDrv = &cs43l22_drv;
290     }
291     else
292     {
293       ret = AUDIO_ERROR;
294     }
295   }
296 
297   if(ret == AUDIO_OK)
298   {
299     pAudioDrv->Init(AUDIO_I2C_ADDRESS, OutputDevice, Volume, AudioFreq);
300   }
301 
302   return ret;
303 }
304 
305 /**
306   * @brief  Starts playing audio stream from a data buffer for a determined size.
307   * @param  pBuffer: Pointer to the buffer
308   * @param  Size: Number of audio data BYTES.
309   * @retval AUDIO_OK if correct communication, else wrong communication
310   */
BSP_AUDIO_OUT_Play(uint16_t * pBuffer,uint32_t Size)311 uint8_t BSP_AUDIO_OUT_Play(uint16_t* pBuffer, uint32_t Size)
312 {
313   /* Call the audio Codec Play function */
314   if(pAudioDrv->Play(AUDIO_I2C_ADDRESS, pBuffer, Size) != 0)
315   {
316     return AUDIO_ERROR;
317   }
318   else
319   {
320     bsp_audio_out_frequency = bsp_audio_out_actual_frequency;
321     /* Update the Media layer and enable it for play */
322     HAL_I2S_Transmit_DMA(&hAudioOutI2s, pBuffer, DMA_MAX(Size/AUDIODATA_SIZE));
323 
324     /* Return AUDIO_OK when all operations are correctly done */
325     return AUDIO_OK;
326   }
327 }
328 
329 /**
330   * @brief  Sends n-Bytes on the I2S interface.
331   * @param  pData: Pointer to data address
332   * @param  Size: Number of data to be written
333   */
BSP_AUDIO_OUT_ChangeBuffer(uint16_t * pData,uint16_t Size)334 void BSP_AUDIO_OUT_ChangeBuffer(uint16_t *pData, uint16_t Size)
335 {
336   HAL_I2S_Transmit_DMA(&hAudioOutI2s, pData, Size);
337 }
338 
339 /**
340   * @brief   Pauses the audio file stream. In case of using DMA, the DMA Pause
341   *          feature is used.
342   * WARNING: When calling BSP_AUDIO_OUT_Pause() function for pause, only the
343   *          BSP_AUDIO_OUT_Resume() function should be called for resume (use of BSP_AUDIO_OUT_Play()
344   *          function for resume could lead to unexpected behavior).
345   * @retval  AUDIO_OK if correct communication, else wrong communication
346   */
BSP_AUDIO_OUT_Pause(void)347 uint8_t BSP_AUDIO_OUT_Pause(void)
348 {
349   /* Call the Audio Codec Pause/Resume function */
350   if(pAudioDrv->Pause(AUDIO_I2C_ADDRESS) != 0)
351   {
352     return AUDIO_ERROR;
353   }
354   else
355   {
356     /* Call the Media layer pause function */
357     HAL_I2S_DMAPause(&hAudioOutI2s);
358 
359     /* Return AUDIO_OK when all operations are correctly done */
360     return AUDIO_OK;
361   }
362 }
363 
364 /**
365   * @brief   Resumes the audio file streaming.
366   * WARNING: When calling BSP_AUDIO_OUT_Pause() function for pause, only
367   *          BSP_AUDIO_OUT_Resume() function should be called for resume (use of BSP_AUDIO_OUT_Play()
368   *          function for resume could lead to unexpected behavior).
369   * @retval  AUDIO_OK if correct communication, else wrong communication
370   */
BSP_AUDIO_OUT_Resume(void)371 uint8_t BSP_AUDIO_OUT_Resume(void)
372 {
373   /* Call the Audio Codec Pause/Resume function */
374   if(pAudioDrv->Resume(AUDIO_I2C_ADDRESS) != 0)
375   {
376     return AUDIO_ERROR;
377   }
378   else
379   {
380     /* Call the Media layer resume function */
381     HAL_I2S_DMAResume(&hAudioOutI2s);
382 
383     /* Return AUDIO_OK when all operations are correctly done */
384     return AUDIO_OK;
385   }
386 }
387 
388 /**
389   * @brief  Stops audio playing and Power down the Audio Codec.
390   * @param  Option: could be one of the following parameters
391   *           - CODEC_PDWN_HW: completely shut down the codec (physically).
392   *                            Then need to reconfigure the Codec after power on.
393   * @retval AUDIO_OK if correct communication, else wrong communication
394   */
BSP_AUDIO_OUT_Stop(uint32_t Option)395 uint8_t BSP_AUDIO_OUT_Stop(uint32_t Option)
396 {
397   /* Call DMA Stop to disable DMA stream before stopping codec */
398   HAL_I2S_DMAStop(&hAudioOutI2s);
399 
400   bsp_audio_out_frequency = 0;
401   /* Call Audio Codec Stop function */
402   if(pAudioDrv->Stop(AUDIO_I2C_ADDRESS, Option) != 0)
403   {
404     return AUDIO_ERROR;
405   }
406   else
407   {
408     if(Option == CODEC_PDWN_HW)
409     {
410       /* Wait at least 1ms */
411       HAL_Delay(1);
412 
413       /* Reset the pin */
414       HAL_GPIO_WritePin(AUDIO_RESET_GPIO, AUDIO_RESET_PIN, GPIO_PIN_RESET);
415     }
416 
417     /* Return AUDIO_OK when all operations are correctly done */
418     return AUDIO_OK;
419   }
420 }
421 
422 /**
423   * @brief  Controls the current audio volume level.
424   * @param  Volume: Volume level to be set in percentage from 0% to 100% (0 for
425   *         Mute and 100 for Max volume level).
426   * @retval AUDIO_OK if correct communication, else wrong communication
427   */
BSP_AUDIO_OUT_SetVolume(uint8_t Volume)428 uint8_t BSP_AUDIO_OUT_SetVolume(uint8_t Volume)
429 {
430   /* Call the codec volume control function with converted volume value */
431   if(pAudioDrv->SetVolume(AUDIO_I2C_ADDRESS, Volume) != 0)
432   {
433     return AUDIO_ERROR;
434   }
435   else
436   {
437     /* Return AUDIO_OK when all operations are correctly done */
438     return AUDIO_OK;
439   }
440 }
441 
442 /**
443   * @brief  Enables or disables the MUTE mode by software
444   * @param  Cmd: could be AUDIO_MUTE_ON to mute sound or AUDIO_MUTE_OFF to
445   *         unmute the codec and restore previous volume level.
446   * @retval AUDIO_OK if correct communication, else wrong communication
447   */
BSP_AUDIO_OUT_SetMute(uint32_t Cmd)448 uint8_t BSP_AUDIO_OUT_SetMute(uint32_t Cmd)
449 {
450   /* Call the Codec Mute function */
451   if(pAudioDrv->SetMute(AUDIO_I2C_ADDRESS, Cmd) != 0)
452   {
453     return AUDIO_ERROR;
454   }
455   else
456   {
457     /* Return AUDIO_OK when all operations are correctly done */
458     return AUDIO_OK;
459   }
460 }
461 
462 /**
463   * @brief  Switch dynamically (while audio file is played) the output target
464   *         (speaker or headphone).
465   * @note   This function modifies a global variable of the audio codec driver: OutputDev.
466   * @param  Output: specifies the audio output target: OUTPUT_DEVICE_SPEAKER,
467   *         OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO
468   * @retval AUDIO_OK if correct communication, else wrong communication
469   */
BSP_AUDIO_OUT_SetOutputMode(uint8_t Output)470 uint8_t BSP_AUDIO_OUT_SetOutputMode(uint8_t Output)
471 {
472   /* Call the Codec output Device function */
473   if(pAudioDrv->SetOutputMode(AUDIO_I2C_ADDRESS, Output) != 0)
474   {
475     return AUDIO_ERROR;
476   }
477   else
478   {
479     /* Return AUDIO_OK when all operations are correctly done */
480     return AUDIO_OK;
481   }
482 }
483 
484 /**
485   * @brief  Update the audio frequency.
486   * @param  AudioFreq: Audio frequency used to play the audio stream.
487   * @note   This API should be called after the BSP_AUDIO_OUT_Init() to adjust the
488   *         audio frequency.
489   */
BSP_AUDIO_OUT_SetFrequency(uint32_t AudioFreq)490 void BSP_AUDIO_OUT_SetFrequency(uint32_t AudioFreq)
491 {
492   /* PLL clock is set depending by the AudioFreq (44.1khz vs 48khz groups) */
493   BSP_AUDIO_OUT_ClockConfig(&hAudioOutI2s, AudioFreq, NULL);
494 
495   /* Update the I2S audio frequency configuration */
496   I2S3_Init(AudioFreq);
497 }
498 
499 /**
500   * @brief  Tx Transfer completed callbacks.
501   * @param  hi2s: I2S handle
502   */
HAL_I2S_TxCpltCallback(I2S_HandleTypeDef * hi2s)503 void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
504 {
505   if(hi2s->Instance == I2S3)
506   {
507     /* Call the user function which will manage directly transfer complete */
508     BSP_AUDIO_OUT_TransferComplete_CallBack();
509   }
510 }
511 
512 /**
513   * @brief  Tx Half Transfer completed callbacks.
514   * @param  hi2s: I2S handle
515   */
HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef * hi2s)516 void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
517 {
518   if(hi2s->Instance == I2S3)
519   {
520     /* Manage the remaining file size and new address offset: This function should
521        be coded by user (its prototype is already declared in stm32f4_discovery_audio.h) */
522     BSP_AUDIO_OUT_HalfTransfer_CallBack();
523   }
524 }
525 
526 /**
527   * @brief  Clock Config.
528   * @param  hi2s: might be required to set audio peripheral predivider if any.
529   * @param  AudioFreq: Audio frequency used to play the audio stream.
530   * @note   This API is called by BSP_AUDIO_OUT_Init() and BSP_AUDIO_OUT_SetFrequency()
531   *         Being __weak it can be overwritten by the application
532   * @param  Params : pointer on additional configuration parameters, can be NULL.
533   */
BSP_AUDIO_OUT_ClockConfig(I2S_HandleTypeDef * hi2s,uint32_t AudioFreq,void * Params)534 __weak void BSP_AUDIO_OUT_ClockConfig(I2S_HandleTypeDef *hi2s, uint32_t AudioFreq, void *Params)
535 {
536   RCC_PeriphCLKInitTypeDef rccclkinit;
537 
538   i2s_pll_entry_t const *pll_params = i2s_find_pll_params( AudioFreq );
539 
540   bsp_audio_out_actual_frequency = pll_params->actual;
541   /* Enable PLLI2S clock */
542   HAL_RCCEx_GetPeriphCLKConfig(&rccclkinit);
543   /* PLLI2S_VCO Input = HSE_VALUE/PLL_M */
544 
545   /* I2S clock config
546   PLLI2S_VCO = f(VCO clock) = f(PLLI2S clock input) � (PLLI2SN/PLLM)
547   I2SCLK = f(PLLI2S clock output) = f(VCO clock) / PLLI2SR */
548   rccclkinit.PeriphClockSelection = RCC_PERIPHCLK_I2S;
549   rccclkinit.PLLI2S.PLLI2SN = pll_params->n;
550   rccclkinit.PLLI2S.PLLI2SR = pll_params->r;
551   HAL_RCCEx_PeriphCLKConfig(&rccclkinit);
552 }
553 
554 /**
555   * @brief  AUDIO OUT I2S MSP Init.
556   * @param  hi2s: might be required to set audio peripheral predivider if any.
557   * @param  Params : pointer on additional configuration parameters, can be NULL.
558   */
BSP_AUDIO_OUT_MspInit(I2S_HandleTypeDef * hi2s,void * Params)559 __weak void BSP_AUDIO_OUT_MspInit(I2S_HandleTypeDef *hi2s, void *Params)
560 {
561   static DMA_HandleTypeDef hdma_i2sTx;
562   GPIO_InitTypeDef  GPIO_InitStruct;
563 
564   /* Enable I2S3 clock */
565   I2S3_CLK_ENABLE();
566 
567   /*** Configure the GPIOs ***/
568   /* Enable I2S GPIO clocks */
569   I2S3_SCK_SD_CLK_ENABLE();
570   I2S3_WS_CLK_ENABLE();
571 
572   /* I2S3 pins configuration: WS, SCK and SD pins ----------------------------*/
573   GPIO_InitStruct.Pin         = I2S3_SCK_PIN | I2S3_SD_PIN;
574   GPIO_InitStruct.Mode        = GPIO_MODE_AF_PP;
575   GPIO_InitStruct.Pull        = GPIO_NOPULL;
576   GPIO_InitStruct.Speed       = GPIO_SPEED_FAST;
577   GPIO_InitStruct.Alternate   = I2S3_SCK_SD_WS_AF;
578   HAL_GPIO_Init(I2S3_SCK_SD_GPIO_PORT, &GPIO_InitStruct);
579 
580   GPIO_InitStruct.Pin         = I2S3_WS_PIN ;
581   HAL_GPIO_Init(I2S3_WS_GPIO_PORT, &GPIO_InitStruct);
582 
583   /* I2S3 pins configuration: MCK pin */
584   I2S3_MCK_CLK_ENABLE();
585   GPIO_InitStruct.Pin         = I2S3_MCK_PIN;
586   HAL_GPIO_Init(I2S3_MCK_GPIO_PORT, &GPIO_InitStruct);
587 
588   /* Enable the I2S DMA clock */
589   I2S3_DMAx_CLK_ENABLE();
590 
591   if(hi2s->Instance == I2S3)
592   {
593     /* Configure the hdma_i2sTx handle parameters */
594     hdma_i2sTx.Init.Channel             = I2S3_DMAx_CHANNEL;
595     hdma_i2sTx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
596     hdma_i2sTx.Init.PeriphInc           = DMA_PINC_DISABLE;
597     hdma_i2sTx.Init.MemInc              = DMA_MINC_ENABLE;
598     hdma_i2sTx.Init.PeriphDataAlignment = I2S3_DMAx_PERIPH_DATA_SIZE;
599     hdma_i2sTx.Init.MemDataAlignment    = I2S3_DMAx_MEM_DATA_SIZE;
600     hdma_i2sTx.Init.Mode                = DMA_NORMAL;
601 
602     // BK: use circular DMA for hal_audio.h
603 #ifdef HAVE_HAL_AUDIO
604     hdma_i2sTx.Init.Mode                = DMA_CIRCULAR;
605 #endif
606     // BK: use circular DMA (end)
607 
608     hdma_i2sTx.Init.Priority            = DMA_PRIORITY_HIGH;
609     hdma_i2sTx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
610     hdma_i2sTx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
611     hdma_i2sTx.Init.MemBurst            = DMA_MBURST_SINGLE;
612     hdma_i2sTx.Init.PeriphBurst         = DMA_PBURST_SINGLE;
613 
614     hdma_i2sTx.Instance                 = I2S3_DMAx_STREAM;
615 
616     /* Associate the DMA handle */
617     __HAL_LINKDMA(hi2s, hdmatx, hdma_i2sTx);
618 
619     /* Deinitialize the Stream for new transfer */
620     HAL_DMA_DeInit(&hdma_i2sTx);
621 
622     /* Configure the DMA Stream */
623     HAL_DMA_Init(&hdma_i2sTx);
624   }
625 
626   /* I2S DMA IRQ Channel configuration */
627   HAL_NVIC_SetPriority(I2S3_DMAx_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0);
628   HAL_NVIC_EnableIRQ(I2S3_DMAx_IRQ);
629 }
630 
631 /**
632   * @brief  De-Initializes BSP_AUDIO_OUT MSP.
633   * @param  hi2s: might be required to set audio peripheral predivider if any.
634   * @param  Params : pointer on additional configuration parameters, can be NULL.
635   */
BSP_AUDIO_OUT_MspDeInit(I2S_HandleTypeDef * hi2s,void * Params)636 __weak void BSP_AUDIO_OUT_MspDeInit(I2S_HandleTypeDef *hi2s, void *Params)
637 {
638   GPIO_InitTypeDef  GPIO_InitStruct;
639 
640   /* I2S DMA IRQ Channel deactivation */
641   HAL_NVIC_DisableIRQ(I2S3_DMAx_IRQ);
642 
643   if(hi2s->Instance == I2S3)
644   {
645     /* Deinitialize the Stream for new transfer */
646     HAL_DMA_DeInit(hi2s->hdmatx);
647   }
648 
649  /* Disable I2S block */
650   __HAL_I2S_DISABLE(hi2s);
651 
652   /* CODEC_I2S pins configuration: SCK and SD pins */
653   GPIO_InitStruct.Pin = I2S3_SCK_PIN | I2S3_SD_PIN;
654   HAL_GPIO_DeInit(I2S3_SCK_SD_GPIO_PORT, GPIO_InitStruct.Pin);
655 
656   /* CODEC_I2S pins configuration: WS pin */
657   GPIO_InitStruct.Pin = I2S3_WS_PIN;
658   HAL_GPIO_DeInit(I2S3_WS_GPIO_PORT, GPIO_InitStruct.Pin);
659 
660   /* CODEC_I2S pins configuration: MCK pin */
661   GPIO_InitStruct.Pin = I2S3_MCK_PIN;
662   HAL_GPIO_DeInit(I2S3_MCK_GPIO_PORT, GPIO_InitStruct.Pin);
663 
664   /* Disable I2S clock */
665   I2S3_CLK_DISABLE();
666 
667   /* GPIO pins clock and DMA clock can be shut down in the applic
668      by surcgarging this __weak function */
669 }
670 
671 /**
672   * @brief  Manages the DMA full Transfer complete event.
673   */
BSP_AUDIO_OUT_TransferComplete_CallBack(void)674 __weak void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
675 {
676 }
677 
678 /**
679   * @brief  Manages the DMA Half Transfer complete event.
680   */
BSP_AUDIO_OUT_HalfTransfer_CallBack(void)681 __weak void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
682 {
683 }
684 
685 /**
686   * @brief  Manages the DMA FIFO error event.
687   */
BSP_AUDIO_OUT_Error_CallBack(void)688 __weak void BSP_AUDIO_OUT_Error_CallBack(void)
689 {
690 }
691 
692 /*******************************************************************************
693                             Static Functions
694 *******************************************************************************/
695 
696 /**
697   * @brief  Initializes the Audio Codec audio interface (I2S).
698   * @param  AudioFreq: Audio frequency to be configured for the I2S peripheral.
699   */
I2S3_Init(uint32_t AudioFreq)700 static uint8_t I2S3_Init(uint32_t AudioFreq)
701 {
702   /* Initialize the hAudioOutI2s Instance parameter */
703   hAudioOutI2s.Instance         = I2S3;
704 
705  /* Disable I2S block */
706   __HAL_I2S_DISABLE(&hAudioOutI2s);
707 
708   /* I2S3 peripheral configuration */
709   hAudioOutI2s.Init.AudioFreq   = AudioFreq;
710   hAudioOutI2s.Init.ClockSource = I2S_CLOCK_PLL;
711   hAudioOutI2s.Init.CPOL        = I2S_CPOL_LOW;
712   hAudioOutI2s.Init.DataFormat  = I2S_DATAFORMAT_16B;
713   hAudioOutI2s.Init.MCLKOutput  = I2S_MCLKOUTPUT_ENABLE;
714   hAudioOutI2s.Init.Mode        = I2S_MODE_MASTER_TX;
715   hAudioOutI2s.Init.Standard    = I2S_STANDARD;
716   /* Initialize the I2S peripheral with the structure above */
717   if(HAL_I2S_Init(&hAudioOutI2s) != HAL_OK)
718   {
719     return AUDIO_ERROR;
720   }
721   else
722   {
723     return AUDIO_OK;
724   }
725 }
726 
727 /**
728   * @}
729   */
730 
731 /** @defgroup STM32F4_DISCOVERY_AUDIO_IN_Private_Functions STM32F4 DISCOVERY AUDIO IN Private Functions
732   * @{
733   */
734 
735 /**
736   * @brief  Initializes wave recording.
737   * @param  AudioFreq: Audio frequency to be configured for the I2S peripheral.
738   * @param  BitRes: Audio frequency to be configured for the I2S peripheral.
739   * @param  ChnlNbr: Audio frequency to be configured for the I2S peripheral.
740   * @retval AUDIO_OK if correct communication, else wrong communication
741   */
BSP_AUDIO_IN_Init(uint32_t AudioFreq,uint32_t BitRes,uint32_t ChnlNbr)742 uint8_t BSP_AUDIO_IN_Init(uint32_t AudioFreq, uint32_t BitRes, uint32_t ChnlNbr)
743 {
744 
745 #if 0
746   // BK: only do PLL clock configuration in sink
747   /* Configure PLL clock */
748   BSP_AUDIO_IN_ClockConfig(&hAudioInI2s, AudioFreq, NULL);
749 #endif
750 
751   /* Configure the PDM library */
752   PDMDecoder_Init(AudioFreq, ChnlNbr);
753 
754   /* Configure the I2S peripheral */
755   hAudioInI2s.Instance = I2S2;
756   if(HAL_I2S_GetState(&hAudioInI2s) == HAL_I2S_STATE_RESET)
757   {
758     /* Initialize the I2S Msp: this __weak function can be rewritten by the application */
759     BSP_AUDIO_IN_MspInit(&hAudioInI2s, NULL);
760   }
761 
762   /* Configure the I2S2 */
763   I2S2_Init(AudioFreq);
764 
765   /* Return AUDIO_OK when all operations are correctly done */
766   return AUDIO_OK;
767 }
768 
769 /**
770   * @brief  Starts audio recording.
771   * @param  pbuf: Main buffer pointer for the recorded data storing
772   * @param  size: Current size of the recorded buffer
773   * @retval AUDIO_OK if correct communication, else wrong communication
774   */
BSP_AUDIO_IN_Record(uint16_t * pbuf,uint32_t size)775 uint8_t BSP_AUDIO_IN_Record(uint16_t* pbuf, uint32_t size)
776 {
777   uint32_t ret = AUDIO_ERROR;
778 
779   /* Start the process receive DMA */
780   HAL_I2S_Receive_DMA(&hAudioInI2s, pbuf, size);
781 
782   /* Return AUDIO_OK when all operations are correctly done */
783   ret = AUDIO_OK;
784 
785   return ret;
786 }
787 
788 /**
789   * @brief  Stops audio recording.
790   * @retval AUDIO_OK if correct communication, else wrong communication
791   */
BSP_AUDIO_IN_Stop(void)792 uint8_t BSP_AUDIO_IN_Stop(void)
793 {
794   uint32_t ret = AUDIO_ERROR;
795 
796   /* Call the Media layer pause function */
797   HAL_I2S_DMAStop(&hAudioInI2s);
798 
799   /* Return AUDIO_OK when all operations are correctly done */
800   ret = AUDIO_OK;
801 
802   return ret;
803 }
804 
805 /**
806   * @brief  Pauses the audio file stream.
807   * @retval AUDIO_OK if correct communication, else wrong communication
808   */
BSP_AUDIO_IN_Pause(void)809 uint8_t BSP_AUDIO_IN_Pause(void)
810 {
811   /* Call the Media layer pause function */
812   HAL_I2S_DMAPause(&hAudioInI2s);
813 
814   /* Return AUDIO_OK when all operations are correctly done */
815   return AUDIO_OK;
816 }
817 
818 /**
819   * @brief  Resumes the audio file stream.
820   * @retval AUDIO_OK if correct communication, else wrong communication
821   */
BSP_AUDIO_IN_Resume(void)822 uint8_t BSP_AUDIO_IN_Resume(void)
823 {
824   /* Call the Media layer pause/resume function */
825   HAL_I2S_DMAResume(&hAudioInI2s);
826 
827   /* Return AUDIO_OK when all operations are correctly done */
828   return AUDIO_OK;
829 }
830 
831 /**
832   * @brief  Controls the audio in volume level.
833   * @param  Volume: Volume level to be set in percentage from 0% to 100% (0 for
834   *         Mute and 100 for Max volume level).
835   * @retval AUDIO_OK if correct communication, else wrong communication
836   */
BSP_AUDIO_IN_SetVolume(uint8_t Volume)837 uint8_t BSP_AUDIO_IN_SetVolume(uint8_t Volume)
838 {
839   /* Set the Global variable AudioInVolume */
840   AudioInVolume = Volume;
841 
842   /* Return AUDIO_OK when all operations are correctly done */
843   return AUDIO_OK;
844 }
845 
846 /**
847   * @brief  Converts audio format from PDM to PCM.
848   * @param  PDMBuf: Pointer to data PDM buffer
849   * @param  PCMBuf: Pointer to data PCM buffer
850   * @retval AUDIO_OK if correct communication, else wrong communication
851   */
BSP_AUDIO_IN_PDMToPCM(uint16_t * PDMBuf,uint16_t * PCMBuf)852 uint8_t BSP_AUDIO_IN_PDMToPCM(uint16_t *PDMBuf, uint16_t *PCMBuf)
853 {
854   uint16_t AppPDM[INTERNAL_BUFF_SIZE/2];
855   uint32_t index = 0;
856 
857   /* PDM Demux */
858   for(index = 0; index<INTERNAL_BUFF_SIZE/2; index++)
859   {
860     AppPDM[index] = HTONS(PDMBuf[index]);
861   }
862 
863   for(index = 0; index < DEFAULT_AUDIO_IN_CHANNEL_NBR; index++)
864   {
865     /* PDM to PCM filter */
866     PDM_Filter_64_LSB((uint8_t*)&AppPDM[index], (uint16_t*)&(PCMBuf[index]), AudioInVolume , (PDMFilter_InitStruct *)&Filter[index]);
867   }
868 #if 0
869   // BK - generate mono output
870 
871   /* Duplicate samples since a single microphone in mounted on STM32F4-Discovery */
872   for(index = 0; index < PCM_OUT_SIZE; index++)
873   {
874     PCMBuf[(index<<1)+1] = PCMBuf[index<<1];
875   }
876 #endif
877 
878   /* Return AUDIO_OK when all operations are correctly done */
879   return AUDIO_OK;
880 }
881 
882 /**
883   * @brief  Rx Transfer completed callbacks
884   * @param  hi2s: I2S handle
885   */
HAL_I2S_RxCpltCallback(I2S_HandleTypeDef * hi2s)886 void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
887 {
888   /* Call the record update function to get the next buffer to fill and its size (size is ignored) */
889   BSP_AUDIO_IN_TransferComplete_CallBack();
890 }
891 
892 /**
893   * @brief  Rx Half Transfer completed callbacks.
894   * @param  hi2s: I2S handle
895   */
HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef * hi2s)896 void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
897 {
898   /* Manage the remaining file size and new address offset: This function
899      should be coded by user (its prototype is already declared in stm32f4_discovery_audio.h) */
900   BSP_AUDIO_IN_HalfTransfer_CallBack();
901 }
902 
903 /**
904   * @brief  Retrive the audio frequency.
905   * @retval AudioFreq: Audio frequency used to play the audio stream.
906   * @note   This API should be called after the BSP_AUDIO_OUT_Init() to adjust the
907   *         audio frequency.
908   */
BSP_AUDIO_OUT_GetFrequency(uint32_t AudioFreq)909 uint32_t BSP_AUDIO_OUT_GetFrequency(uint32_t AudioFreq)
910 {
911   return bsp_audio_out_frequency;
912 }
913 
914 /**
915   * @brief  Audio In Clock Config.
916   * @param  hi2s: I2S handle
917   * @param  AudioFreq: Audio frequency used to record the audio stream.
918   * @param  Params : pointer on additional configuration parameters, can be NULL.
919   * @note   This API is called by BSP_AUDIO_IN_Init()
920   *         Being __weak it can be overwritten by the application
921   */
BSP_AUDIO_IN_ClockConfig(I2S_HandleTypeDef * hi2s,uint32_t AudioFreq,void * Params)922 __weak void BSP_AUDIO_IN_ClockConfig(I2S_HandleTypeDef *hi2s, uint32_t AudioFreq, void *Params)
923 {
924   RCC_PeriphCLKInitTypeDef rccclkinit;
925 
926   /*Enable PLLI2S clock*/
927   HAL_RCCEx_GetPeriphCLKConfig(&rccclkinit);
928   i2s_pll_entry_t const *pll_params = i2s_find_pll_params( AudioFreq );
929 
930   rccclkinit.PeriphClockSelection = RCC_PERIPHCLK_I2S;
931   rccclkinit.PLLI2S.PLLI2SN = pll_params->n;
932   rccclkinit.PLLI2S.PLLI2SR = pll_params->r;
933   HAL_RCCEx_PeriphCLKConfig(&rccclkinit);
934 }
935 
936 /**
937   * @brief  BSP AUDIO IN MSP Init.
938   * @param  hi2s: I2S handle
939   * @param  Params : pointer on additional configuration parameters, can be NULL.
940   */
BSP_AUDIO_IN_MspInit(I2S_HandleTypeDef * hi2s,void * Params)941 __weak void BSP_AUDIO_IN_MspInit(I2S_HandleTypeDef *hi2s, void *Params)
942 {
943   static DMA_HandleTypeDef hdma_i2sRx;
944   GPIO_InitTypeDef  GPIO_InitStruct;
945 
946   /* Enable the I2S2 peripheral clock */
947   I2S2_CLK_ENABLE();
948 
949   /* Enable I2S GPIO clocks */
950   I2S2_SCK_GPIO_CLK_ENABLE();
951   I2S2_MOSI_GPIO_CLK_ENABLE();
952 
953   /* I2S2 pins configuration: SCK and MOSI pins ------------------------------*/
954   GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
955   GPIO_InitStruct.Pull      = GPIO_NOPULL;
956   GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
957 
958   GPIO_InitStruct.Pin       = I2S2_SCK_PIN;
959   GPIO_InitStruct.Alternate = I2S2_SCK_AF;
960   HAL_GPIO_Init(I2S2_SCK_GPIO_PORT, &GPIO_InitStruct);
961 
962   GPIO_InitStruct.Pin       = I2S2_MOSI_PIN ;
963   GPIO_InitStruct.Alternate = I2S2_MOSI_AF;
964   HAL_GPIO_Init(I2S2_MOSI_GPIO_PORT, &GPIO_InitStruct);
965 
966   /* Enable the DMA clock */
967   I2S2_DMAx_CLK_ENABLE();
968 
969   if(hi2s->Instance == I2S2)
970   {
971     /* Configure the hdma_i2sRx handle parameters */
972     hdma_i2sRx.Init.Channel             = I2S2_DMAx_CHANNEL;
973     hdma_i2sRx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
974     hdma_i2sRx.Init.PeriphInc           = DMA_PINC_DISABLE;
975     hdma_i2sRx.Init.MemInc              = DMA_MINC_ENABLE;
976     hdma_i2sRx.Init.PeriphDataAlignment = I2S2_DMAx_PERIPH_DATA_SIZE;
977     hdma_i2sRx.Init.MemDataAlignment    = I2S2_DMAx_MEM_DATA_SIZE;
978     hdma_i2sRx.Init.Mode                = DMA_CIRCULAR;
979     hdma_i2sRx.Init.Priority            = DMA_PRIORITY_HIGH;
980     hdma_i2sRx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
981     hdma_i2sRx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
982     hdma_i2sRx.Init.MemBurst            = DMA_MBURST_SINGLE;
983     hdma_i2sRx.Init.PeriphBurst         = DMA_MBURST_SINGLE;
984 
985     hdma_i2sRx.Instance = I2S2_DMAx_STREAM;
986 
987     /* Associate the DMA handle */
988     __HAL_LINKDMA(hi2s, hdmarx, hdma_i2sRx);
989 
990     /* Deinitialize the Stream for new transfer */
991     HAL_DMA_DeInit(&hdma_i2sRx);
992 
993     /* Configure the DMA Stream */
994     HAL_DMA_Init(&hdma_i2sRx);
995   }
996 
997   /* I2S DMA IRQ Channel configuration */
998   HAL_NVIC_SetPriority(I2S2_DMAx_IRQ, AUDIO_IN_IRQ_PREPRIO, 0);
999   HAL_NVIC_EnableIRQ(I2S2_DMAx_IRQ);
1000 }
1001 
1002 /**
1003   * @brief  DeInitializes BSP_AUDIO_IN MSP.
1004   * @param  hi2s: I2S handle
1005   * @param  Params : pointer on additional configuration parameters, can be NULL.
1006   */
BSP_AUDIO_IN_MspDeInit(I2S_HandleTypeDef * hi2s,void * Params)1007 __weak void BSP_AUDIO_IN_MspDeInit(I2S_HandleTypeDef *hi2s, void *Params)
1008 {
1009   GPIO_InitTypeDef  gpio_init_structure;
1010 
1011   /* I2S DMA IRQ Channel deactivation */
1012   HAL_NVIC_DisableIRQ(I2S2_DMAx_IRQ);
1013 
1014   if(hi2s->Instance == I2S2)
1015   {
1016     /* Deinitialize the Stream for new transfer */
1017     HAL_DMA_DeInit(hi2s->hdmarx);
1018   }
1019 
1020  /* Disable I2S block */
1021   __HAL_I2S_DISABLE(hi2s);
1022 
1023   /* Disable pins: SCK and SD pins */
1024   gpio_init_structure.Pin = I2S2_SCK_PIN;
1025   HAL_GPIO_DeInit(I2S2_SCK_GPIO_PORT, gpio_init_structure.Pin);
1026   gpio_init_structure.Pin = I2S2_MOSI_PIN;
1027   HAL_GPIO_DeInit(I2S2_MOSI_GPIO_PORT, gpio_init_structure.Pin);
1028 
1029   /* Disable I2S clock */
1030   I2S2_CLK_DISABLE();
1031 
1032   /* GPIO pins clock and DMA clock can be shut down in the applic
1033      by surcgarging this __weak function */
1034 }
1035 
1036 /**
1037   * @brief  User callback when record buffer is filled.
1038   */
BSP_AUDIO_IN_TransferComplete_CallBack(void)1039 __weak void BSP_AUDIO_IN_TransferComplete_CallBack(void)
1040 {
1041   /* This function should be implemented by the user application.
1042      It is called into this driver when the current buffer is filled
1043      to prepare the next buffer pointer and its size. */
1044 }
1045 
1046 /**
1047   * @brief  Manages the DMA Half Transfer complete event.
1048   */
BSP_AUDIO_IN_HalfTransfer_CallBack(void)1049 __weak void BSP_AUDIO_IN_HalfTransfer_CallBack(void)
1050 {
1051   /* This function should be implemented by the user application.
1052      It is called into this driver when the current buffer is filled
1053      to prepare the next buffer pointer and its size. */
1054 }
1055 
1056 /**
1057   * @brief  Audio IN Error callback function.
1058   */
BSP_AUDIO_IN_Error_Callback(void)1059 __weak void BSP_AUDIO_IN_Error_Callback(void)
1060 {
1061   /* This function is called when an Interrupt due to transfer error on or peripheral
1062      error occurs. */
1063 }
1064 
1065 /*******************************************************************************
1066                             Static Functions
1067 *******************************************************************************/
1068 
1069 /**
1070   * @brief  Initialize the PDM library.
1071   * @param  AudioFreq: Audio sampling frequency
1072   * @param  ChnlNbr: Number of audio channels (1: mono; 2: stereo)
1073   */
PDMDecoder_Init(uint32_t AudioFreq,uint32_t ChnlNbr)1074 static void PDMDecoder_Init(uint32_t AudioFreq, uint32_t ChnlNbr)
1075 {
1076 #if 1
1077   uint32_t i = 0;
1078 
1079   /* Enable CRC peripheral to unlock the PDM library */
1080   __CRC_CLK_ENABLE();
1081 
1082   for(i = 0; i < ChnlNbr; i++)
1083   {
1084     /* Filter LP and HP Init */
1085     Filter[i].LP_HZ = AudioFreq / 2;
1086     Filter[i].HP_HZ = 10;
1087     Filter[i].Fs = AudioFreq;
1088     /* On STM32F4-Discovery a single microphone is mounted, samples are duplicated
1089        to make stereo audio streams */
1090     // BK - generate mono output
1091     // Filter[i].Out_MicChannels = 2;
1092     Filter[i].Out_MicChannels = 1;
1093     Filter[i].In_MicChannels = ChnlNbr;
1094     PDM_Filter_Init((PDMFilter_InitStruct *)&Filter[i]);
1095   }
1096 #endif
1097 }
1098 
1099 /**
1100   * @brief  Initializes the Audio Codec audio interface (I2S)
1101   * @note   This function assumes that the I2S input clock (through PLL_R in
1102   *         Devices RevA/Z and through dedicated PLLI2S_R in Devices RevB/Y)
1103   *         is already configured and ready to be used.
1104   * @param  AudioFreq: Audio frequency to be configured for the I2S peripheral.
1105   */
I2S2_Init(uint32_t AudioFreq)1106 static uint8_t I2S2_Init(uint32_t AudioFreq)
1107 {
1108   /* Initialize the hAudioInI2s Instance parameter */
1109   hAudioInI2s.Instance          = I2S2;
1110 
1111   /* Disable I2S block */
1112   __HAL_I2S_DISABLE(&hAudioInI2s);
1113 
1114   /* I2S2 peripheral configuration */
1115   hAudioInI2s.Init.AudioFreq    = 2 * AudioFreq;
1116   hAudioInI2s.Init.ClockSource  = I2S_CLOCK_PLL;
1117   hAudioInI2s.Init.CPOL         = I2S_CPOL_HIGH;
1118   hAudioInI2s.Init.DataFormat   = I2S_DATAFORMAT_16B;
1119   hAudioInI2s.Init.MCLKOutput   = I2S_MCLKOUTPUT_DISABLE;
1120   hAudioInI2s.Init.Mode         = I2S_MODE_MASTER_RX;
1121   hAudioInI2s.Init.Standard     = I2S_STANDARD_LSB;
1122 
1123   /* Initialize the I2S peripheral with the structure above */
1124   if(HAL_I2S_Init(&hAudioInI2s) != HAL_OK)
1125   {
1126     return AUDIO_ERROR;
1127   }
1128   else
1129   {
1130     return AUDIO_OK;
1131   }
1132 }
1133 
1134 /**
1135   * @}
1136   */
1137 
1138 /** @defgroup STM32F4_DISCOVERY_AUDIO_IN_OUT_Private_Functions STM32F4 DISCOVERY AUDIO IN OUT Private Functions
1139   * @{
1140   */
1141 
1142 /**
1143   * @brief  I2S error callbacks.
1144   * @param  hi2s: I2S handle
1145   */
HAL_I2S_ErrorCallback(I2S_HandleTypeDef * hi2s)1146 void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s)
1147 {
1148   /* Manage the error generated on DMA FIFO: This function
1149      should be coded by user (its prototype is already declared in stm32f4_discovery_audio.h) */
1150   if(hi2s->Instance == I2S3)
1151   {
1152     BSP_AUDIO_OUT_Error_CallBack();
1153   }
1154   if(hi2s->Instance == I2S2)
1155   {
1156     BSP_AUDIO_IN_Error_Callback();
1157   }
1158  }
1159 
1160 /**
1161   * @}
1162   */
1163 
1164 /**
1165   * @}
1166   */
1167 
1168 /**
1169   * @}
1170   */
1171 
1172 /**
1173   * @}
1174   */
1175 
1176 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
1177