/** ****************************************************************************** * @file cca02m2_audio.c * @author SRA * @version v1.1.1 * @date 18-Dec-2020 * @brief This file provides the Audio driver for the cca02m2 * board. ****************************************************************************** * @attention * *

© Copyright (c) 2020 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "cca02m2_audio.h" #include "cca02m2_conf.h" #include "audio.h" #ifndef USE_STM32L4XX_NUCLEO #include "arm_math.h" #endif /** @addtogroup BSP * @{ */ /** @addtogroup CCA02M2 * @{ */ /** @defgroup CCA02M2_AUDIO_ CCA02M2 AUDIO * @{ */ /** @defgroup CCA02M2_AUDIO_Private_Macros CCA02M2_AUDIO_ Private Macros * @{ */ /*### RECORD ###*/ #define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N))) #define DFSDM_OVER_SAMPLING(__FREQUENCY__) \ ((__FREQUENCY__) == (AUDIO_FREQUENCY_8K)) ? (128U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_11K)) ? (256U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_16K)) ? (128U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_22K)) ? (128U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_32K)) ? (64U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_44K)) ? (64U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_48K)) ? (64U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_96K)) ? (32U) : (32U) #define DFSDM_CLOCK_DIVIDER(__FREQUENCY__) \ ((__FREQUENCY__) == (AUDIO_FREQUENCY_8K)) ? (17U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_11K)) ? (4U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_16K)) ? (24U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_22K)) ? (4U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_32K)) ? (24U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_44K)) ? (4U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_48K)) ? (16U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_96K)) ? (16U) : (16U) #define DFSDM_FILTER_ORDER(__FREQUENCY__) \ ((__FREQUENCY__) == (AUDIO_FREQUENCY_8K)) ? (DFSDM_FILTER_SINC4_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_11K)) ? (DFSDM_FILTER_SINC5_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_16K)) ? (DFSDM_FILTER_SINC4_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_22K)) ? (DFSDM_FILTER_SINC4_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_32K)) ? (DFSDM_FILTER_SINC5_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_44K)) ? (DFSDM_FILTER_SINC5_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_48K)) ? (DFSDM_FILTER_SINC5_ORDER) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_96K)) ? (DFSDM_FILTER_SINC5_ORDER) : (DFSDM_FILTER_SINC5_ORDER) #define DFSDM_MIC_BIT_SHIFT(__FREQUENCY__) \ ((__FREQUENCY__) == (AUDIO_FREQUENCY_8K)) ? (8U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_11K)) ? (5U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_16K)) ? (8U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_22K)) ? (8U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_32K)) ? (10U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_44K)) ? (10U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_48K)) ? (10U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_96K)) ? (5U) : (5U) #ifdef USE_STM32WBXX_NUCLEO #define SAI_DIVIDER(__FREQUENCY__) \ ((__FREQUENCY__) == (AUDIO_FREQUENCY_8K)) ? (37U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_11K)) ? (4U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_16K)) ? (37U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_22K)) ? (4U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_32K)) ? (24U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_44K)) ? (4U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_48K)) ? (16U) \ : ((__FREQUENCY__) == (AUDIO_FREQUENCY_96K)) ? (16U) : (16U) #endif /** * @} */ /** @defgroup CCA02M2_AUDIO_Exported_Variables CCA02M2_AUDIO_ Exported Variables * @{ */ /* Recording context */ AUDIO_IN_Ctx_t AudioInCtx[AUDIO_IN_INSTANCES_NBR] = {0}; /** * @} */ /** @defgroup CCA02M2_AUDIO_Private_Variables CCA02M2_AUDIO_ Private Variables * @{ */ #ifdef USE_STM32L4XX_NUCLEO /* Recording handles */ static DFSDM_Channel_HandleTypeDef hAudioInDfsdmChannel[4]; DMA_HandleTypeDef hDmaDfsdm[4]; static DFSDM_Filter_HandleTypeDef hAudioInDfsdmFilter[4]; #else #define DECIMATOR_NUM_TAPS (16U) #define DECIMATOR_BLOCK_SIZE (16U*N_MS_PER_INTERRUPT) #define DECIMATOR_FACTOR 2U #define DECIMATOR_STATE_LENGTH (DECIMATOR_BLOCK_SIZE + (DECIMATOR_NUM_TAPS) -1U) static arm_fir_decimate_instance_q15 ARM_Decimator_State[4]; /* PDM filters params */ static PDM_Filter_Handler_t PDM_FilterHandler[4]; static PDM_Filter_Config_t PDM_FilterConfig[4]; #ifdef USE_STM32WBXX_NUCLEO SAI_HandleTypeDef hAudioInSai; #define PDM_INTERNAL_BUFFER_SIZE_SAI ((MAX_MIC_FREQ / 8) * MAX_AUDIO_IN_CHANNEL_NBR_TOTAL * N_MS_PER_INTERRUPT) static uint16_t SAI_InternalBuffer[PDM_INTERNAL_BUFFER_SIZE_SAI]; #else I2S_HandleTypeDef hAudioInI2s; static SPI_HandleTypeDef hAudioInSPI; static TIM_HandleTypeDef TimDividerHandle; static uint16_t I2S_InternalBuffer[PDM_INTERNAL_BUFFER_SIZE_I2S]; static uint16_t SPI_InternalBuffer[PDM_INTERNAL_BUFFER_SIZE_SPI]; static uint8_t Channel_Demux[128] = { 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b, 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f, 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b, 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f, 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f }; #endif #endif /* Recording Buffer Trigger */ static __IO uint32_t RecBuffTrigger = 0; static __IO uint32_t RecBuffHalf = 0; static __IO uint32_t MicBuffIndex[4]; #ifdef USE_STM32L4XX_NUCLEO static int32_t MicRecBuff[4][DEFAULT_AUDIO_IN_BUFFER_SIZE]; #endif /** * @} */ /** @defgroup CCA02M2_AUDIO_Private_Function_Prototypes CCA02M2_AUDIO_ Private Function Prototypes * @{ */ #ifdef USE_STM32L4XX_NUCLEO /* DFSDM Channel Msp config */ static void DFSDM_ChannelMspInit(DFSDM_Channel_HandleTypeDef *hDfsdmChannel); static void DFSDM_ChannelMspDeInit(DFSDM_Channel_HandleTypeDef *hDfsdmChannel); /* DFSDM Filter Msp config */ static void DFSDM_FilterMspInit(DFSDM_Filter_HandleTypeDef *hDfsdmFilter); static void DFSDM_FilterMspDeInit(DFSDM_Filter_HandleTypeDef *hDfsdmFilter); /* DFSDM Filter conversion callbacks */ #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) static void DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter); static void DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter); #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) */ #else #ifdef USE_STM32WBXX_NUCLEO static void SAI_MspInit(SAI_HandleTypeDef *hsai); #else static HAL_StatusTypeDef AUDIO_IN_Timer_Init(void); static HAL_StatusTypeDef AUDIO_IN_Timer_Start(void); static void I2S_MspInit(I2S_HandleTypeDef *hi2s); static void SPI_MspInit(SPI_HandleTypeDef *hspi); #endif #endif /** * @} */ /** @defgroup CCA02M2_AUDIO_IN_Exported_Functions CCA02M2_AUDIO_IN Exported Functions * @{ */ /** * @brief Initialize wave recording. * @param Instance AUDIO IN Instance. It can be: * - 0 when I2S is used * - 1 if DFSDM is used * - 2 if PDM is used * @param AudioInit Init structure * @retval BSP status */ __weak int32_t CCA02M2_AUDIO_IN_Init(uint32_t Instance, CCA02M2_AUDIO_Init_t* AudioInit) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Store the audio record context */ AudioInCtx[Instance].Device = AudioInit->Device; AudioInCtx[Instance].ChannelsNbr = AudioInit->ChannelsNbr; AudioInCtx[Instance].SampleRate = AudioInit->SampleRate; AudioInCtx[Instance].BitsPerSample = AudioInit->BitsPerSample; AudioInCtx[Instance].Volume = AudioInit->Volume; AudioInCtx[Instance].State = AUDIO_IN_STATE_RESET; if(Instance == 0U) { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else uint32_t PDM_Clock_Freq; switch (AudioInit->SampleRate) { case AUDIO_FREQUENCY_8K: PDM_Clock_Freq = 1280; break; case AUDIO_FREQUENCY_16K: PDM_Clock_Freq = PDM_FREQ_16K; break; case AUDIO_FREQUENCY_32K: PDM_Clock_Freq = 2048; break; case AUDIO_FREQUENCY_48K: PDM_Clock_Freq = 3072; break; default: PDM_Clock_Freq = 0; break; } if (PDM_Clock_Freq == 0U) { return BSP_ERROR_WRONG_PARAM; } AudioInCtx[Instance].DecimationFactor = (PDM_Clock_Freq * 1000U)/AudioInit->SampleRate; /* Double buffer for 1 microphone */ AudioInCtx[Instance].Size = (PDM_Clock_Freq/8U) * 2U * N_MS_PER_INTERRUPT; #ifdef USE_STM32WBXX_NUCLEO if (AudioInCtx[Instance].ChannelsNbr == 1U) { AudioInCtx[Instance].Size *= 2U; } /* Initialize SAI */ __HAL_SAI_RESET_HANDLE_STATE(&hAudioInSai); /* PLL clock is set depending by the AudioFreq */ if(MX_SAI_ClockConfig(&hAudioInSai, PDM_Clock_Freq) != HAL_OK) { return BSP_ERROR_CLOCK_FAILURE; } if(HAL_SAI_GetState(&hAudioInSai) == HAL_SAI_STATE_RESET) { SAI_MspInit(&hAudioInSai); } hAudioInSai.Instance = AUDIO_IN_SAI_INSTANCE; __HAL_SAI_DISABLE(&hAudioInSai); hAudioInSai.Init.Protocol = SAI_FREE_PROTOCOL; hAudioInSai.Init.AudioMode = SAI_MODEMASTER_RX; hAudioInSai.Init.DataSize = SAI_DATASIZE_16; hAudioInSai.Init.FirstBit = SAI_FIRSTBIT_MSB; hAudioInSai.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE; hAudioInSai.Init.Synchro = SAI_ASYNCHRONOUS; hAudioInSai.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE; hAudioInSai.Init.NoDivider = SAI_MASTERDIVIDER_DISABLE; hAudioInSai.Init.MckOverSampling = SAI_MCK_OVERSAMPLING_DISABLE; hAudioInSai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY; hAudioInSai.Init.SynchroExt = SAI_SYNCEXT_DISABLE; hAudioInSai.Init.MonoStereoMode = SAI_STEREOMODE; hAudioInSai.Init.CompandingMode = SAI_NOCOMPANDING; hAudioInSai.Init.PdmInit.Activation = ENABLE; hAudioInSai.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK2_ENABLE; hAudioInSai.FrameInit.FrameLength = 16; if ( AudioInCtx[Instance].ChannelsNbr <= 2U) { hAudioInSai.Init.PdmInit.MicPairsNbr = 1; hAudioInSai.Init.AudioFrequency = ((PDM_Clock_Freq * 1000U) / hAudioInSai.FrameInit.FrameLength ) * 2U; } else { hAudioInSai.Init.PdmInit.MicPairsNbr = 2; hAudioInSai.Init.AudioFrequency = ((PDM_Clock_Freq * 1000U) / hAudioInSai.FrameInit.FrameLength ) * 4U; } hAudioInSai.FrameInit.ActiveFrameLength = 1; hAudioInSai.FrameInit.FSDefinition = SAI_FS_STARTFRAME; hAudioInSai.FrameInit.FSPolarity = SAI_FS_ACTIVE_HIGH; hAudioInSai.FrameInit.FSOffset = SAI_FS_FIRSTBIT; hAudioInSai.SlotInit.FirstBitOffset = 0; hAudioInSai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; hAudioInSai.SlotInit.SlotNumber = 1; hAudioInSai.SlotInit.SlotActive = 0x00000003; if (HAL_SAI_Init(&hAudioInSai) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } /* Enable SAI to generate clock used by audio driver */ __HAL_SAI_ENABLE(&hAudioInSai); #else MX_I2S_IN_Config i2s_config; if(AudioInCtx[0].ChannelsNbr == 1U) { i2s_config.DataFormat = I2S_DATAFORMAT_16B; } else { i2s_config.DataFormat = I2S_DATAFORMAT_32B; } i2s_config.AudioFreq = ((PDM_Clock_Freq * 1000U) / 32U); i2s_config.CPOL = I2S_CPOL_HIGH; i2s_config.MCLKOutput = I2S_MCLKOUTPUT_DISABLE; i2s_config.Mode = I2S_MODE_MASTER_RX; i2s_config.Standard = I2S_STANDARD_MSB; #ifdef USE_STM32F4XX_NUCLEO i2s_config.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE; i2s_config.ClockSource = I2S_CLOCK_PLL; #else i2s_config.ClockSource = I2S_CLOCK_SYSCLK; #endif if (AudioInCtx[0].ChannelsNbr>1U) { PDM_Clock_Freq *=2U; if (AUDIO_IN_Timer_Init() != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } /* PLL clock is set depending by the AudioFreq */ if(MX_I2S_IN_ClockConfig(&hAudioInI2s, PDM_Clock_Freq) != HAL_OK) { return BSP_ERROR_CLOCK_FAILURE; } /* I2S Peripheral configuration */ hAudioInI2s.Instance = AUDIO_IN_I2S_INSTANCE; __HAL_I2S_DISABLE(&hAudioInI2s); I2S_MspInit(&hAudioInI2s); if (MX_I2S_IN_Init(&hAudioInI2s, &i2s_config)!= HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } if (HAL_I2S_Init(&hAudioInI2s) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } if (AudioInCtx[0].ChannelsNbr>2U) { /* Set the SPI parameters */ hAudioInSPI.Instance = AUDIO_IN_SPI_INSTANCE; __HAL_SPI_DISABLE(&hAudioInSPI); SPI_MspInit(&hAudioInSPI); MX_SPI_Config spi_config; spi_config.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; spi_config.Direction = SPI_DIRECTION_2LINES_RXONLY; spi_config.CLKPhase = SPI_PHASE_2EDGE; spi_config.CLKPolarity = SPI_POLARITY_HIGH; spi_config.CRCCalculation = SPI_CRCCALCULATION_DISABLED; spi_config.CRCPolynomial = 7; spi_config.DataSize = SPI_DATASIZE_16BIT; spi_config.FirstBit = SPI_FIRSTBIT_MSB; spi_config.NSS = SPI_NSS_SOFT; spi_config.TIMode = SPI_TIMODE_DISABLED; spi_config.Mode = SPI_MODE_SLAVE; if (MX_SPI_Init(&hAudioInSPI, &spi_config)!= HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } if (HAL_SPI_Init(&hAudioInSPI) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } #endif if (CCA02M2_AUDIO_IN_PDMToPCM_Init(Instance, AudioInCtx[0].SampleRate, AudioInCtx[0].ChannelsNbr, AudioInCtx[0].ChannelsNbr)!= BSP_ERROR_NONE) { return BSP_ERROR_NO_INIT; } #endif } else if(Instance == 1U) { #ifdef USE_STM32L4XX_NUCLEO int8_t i; DFSDM_Filter_TypeDef* FilterInstnace[4] = {AUDIO_DFSDMx_MIC1_FILTER, AUDIO_DFSDMx_MIC2_FILTER, AUDIO_DFSDMx_MIC3_FILTER, AUDIO_DFSDMx_MIC4_FILTER}; DFSDM_Channel_TypeDef* ChannelInstance[4] = {AUDIO_DFSDMx_MIC1_CHANNEL, AUDIO_DFSDMx_MIC2_CHANNEL, AUDIO_DFSDMx_MIC3_CHANNEL, AUDIO_DFSDMx_MIC4_CHANNEL}; uint32_t DigitalMicPins[4] = {DFSDM_CHANNEL_SAME_CHANNEL_PINS, DFSDM_CHANNEL_FOLLOWING_CHANNEL_PINS, DFSDM_CHANNEL_SAME_CHANNEL_PINS, DFSDM_CHANNEL_FOLLOWING_CHANNEL_PINS}; uint32_t DigitalMicType[4] = {DFSDM_CHANNEL_SPI_RISING, DFSDM_CHANNEL_SPI_FALLING, DFSDM_CHANNEL_SPI_RISING, DFSDM_CHANNEL_SPI_FALLING}; uint32_t Channel4Filter[4] = {AUDIO_DFSDMx_MIC1_CHANNEL_FOR_FILTER, AUDIO_DFSDMx_MIC2_CHANNEL_FOR_FILTER, AUDIO_DFSDMx_MIC3_CHANNEL_FOR_FILTER, AUDIO_DFSDMx_MIC4_CHANNEL_FOR_FILTER}; MX_DFSDM_Config dfsdm_config; /* PLL clock is set depending on the AudioFreq (44.1khz vs 48khz groups) */ if(MX_DFSDM1_ClockConfig(&hAudioInDfsdmChannel[0], AudioInit->SampleRate) != HAL_OK) { return BSP_ERROR_CLOCK_FAILURE; } #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) /* Register the default DFSDM MSP callbacks */ if(AudioInCtx[Instance].IsMspCallbacksValid == 0U) { if(CCA02M2_AUDIO_IN_RegisterDefaultMspCallbacks(Instance) != BSP_ERROR_NONE) { return BSP_ERROR_PERIPH_FAILURE; } } #else DFSDM_FilterMspInit(&hAudioInDfsdmFilter[1]); DFSDM_ChannelMspInit(&hAudioInDfsdmChannel[1]); #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) */ for(i = 0; i < DFSDM_MIC_NUMBER; i ++) { dfsdm_config.FilterInstance = FilterInstnace[i]; dfsdm_config.ChannelInstance = ChannelInstance[i]; dfsdm_config.DigitalMicPins = DigitalMicPins[i]; dfsdm_config.DigitalMicType = DigitalMicType[i]; dfsdm_config.Channel4Filter = Channel4Filter[i]; if((i == 0) && (AudioInCtx[Instance].Device == AUDIO_IN_DIGITAL_MIC)) { dfsdm_config.RegularTrigger = DFSDM_FILTER_SW_TRIGGER; } else { dfsdm_config.RegularTrigger = DFSDM_FILTER_SYNC_TRIGGER; } dfsdm_config.SincOrder = DFSDM_FILTER_ORDER(AudioInCtx[Instance].SampleRate); dfsdm_config.Oversampling = DFSDM_OVER_SAMPLING(AudioInCtx[Instance].SampleRate); dfsdm_config.ClockDivider = DFSDM_CLOCK_DIVIDER(AudioInCtx[Instance].SampleRate); dfsdm_config.RightBitShift = DFSDM_MIC_BIT_SHIFT(AudioInCtx[Instance].SampleRate); if(((AudioInit->Device >> (uint8_t)i) & AUDIO_IN_DIGITAL_MIC1) == AUDIO_IN_DIGITAL_MIC1) { /* Default configuration of DFSDM filters and channels */ if(MX_DFSDM1_Init(&hAudioInDfsdmFilter[i], &hAudioInDfsdmChannel[i], &dfsdm_config) != HAL_OK) { /* Return BSP_ERROR_PERIPH_FAILURE when operations are not correctly done */ return BSP_ERROR_PERIPH_FAILURE; } #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) /* Register filter regular conversion callbacks */ if(HAL_DFSDM_FILTER_RegisterCallback(&hAudioInDfsdmFilter[i], HAL_DFSDM_FILTER_REG_COMPLETE_CB_ID, DFSDM_FilterRegConvCpltCallback) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } if(HAL_DFSDM_FILTER_RegisterCallback(&hAudioInDfsdmFilter[i], HAL_DFSDM_FILTER_REG_HALFCOMPLETE_CB_ID, DFSDM_FilterRegConvHalfCpltCallback) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) */ } } #else return BSP_ERROR_WRONG_PARAM; #endif } else /* Instance = 2 */ { // PDM direttamente? } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_STOP; /* Return BSP status */ } return BSP_ERROR_NONE; } /** * @brief Deinit the audio IN peripherals. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @retval BSP status */ __weak int32_t CCA02M2_AUDIO_IN_DeInit(uint32_t Instance) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { if(Instance != 0U) { #ifdef USE_STM32L4XX_NUCLEO int8_t i; for(i = 0; i < DFSDM_MIC_NUMBER; i++) { /* De-initializes DFSDM Filter handle */ if(hAudioInDfsdmFilter[i].Instance != NULL) { #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 0U) DFSDM_FilterMspDeInit(&hAudioInDfsdmFilter[i]); #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 0U) */ if(HAL_OK != HAL_DFSDM_FilterDeInit(&hAudioInDfsdmFilter[i])) { return BSP_ERROR_PERIPH_FAILURE; } hAudioInDfsdmFilter[i].Instance = NULL; } /* De-initializes DFSDM Channel handle */ if(hAudioInDfsdmChannel[i].Instance != NULL) { #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 0U) DFSDM_ChannelMspDeInit(&hAudioInDfsdmChannel[i]); #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 0U) */ if(HAL_OK != HAL_DFSDM_ChannelDeInit(&hAudioInDfsdmChannel[i])) { return BSP_ERROR_PERIPH_FAILURE; } hAudioInDfsdmChannel[i].Instance = NULL; } } /* Reset AudioInCtx[1].IsMultiBuff if any */ AudioInCtx[1].IsMultiBuff = 0; #else return BSP_ERROR_WRONG_PARAM; #endif } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RESET; } /* Return BSP status */ return BSP_ERROR_NONE; } #ifdef USE_STM32L4XX_NUCLEO /** * @brief Clock Config. * @param hDfsdmChannel DFSDM Channel Handle * @param SampleRate Audio frequency to be configured for the DFSDM Channel. * @note This API is called by CCA02M2_AUDIO_IN_Init() * Being __weak it can be overwritten by the application * @retval HAL_status */ __weak HAL_StatusTypeDef MX_DFSDM1_ClockConfig(DFSDM_Channel_HandleTypeDef *hDfsdmChannel, uint32_t SampleRate) { /* Prevent unused argument(s) compilation warning */ UNUSED(hDfsdmChannel); HAL_StatusTypeDef ret = HAL_OK; RCC_PeriphCLKInitTypeDef RCC_ExCLKInitStruct; HAL_RCCEx_GetPeriphCLKConfig(&RCC_ExCLKInitStruct); switch (SampleRate) { case AUDIO_FREQUENCY_8K: { RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 37; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 17; break; } case AUDIO_FREQUENCY_16K: { RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 43; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 7; break; } case AUDIO_FREQUENCY_32K: { RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 43; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 7; break; } case AUDIO_FREQUENCY_48K: { RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 43; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 7; break; } case AUDIO_FREQUENCY_96K: { RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 43; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 7; break; } default: { RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 43; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 7; break; } } /* Configure PLLSAI prescalers */ /* Please note that some of these parameters must be consistent with the parameters of the main PLL */ RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK; RCC_ExCLKInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_MSI; RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1M = 6; if(HAL_RCCEx_PeriphCLKConfig(&RCC_ExCLKInitStruct) != HAL_OK) { ret = HAL_ERROR; } return ret; } /** * @brief Initializes the Audio instance (DFSDM). * @param hDfsdmFilter DFSDM Filter Handle * @param hDfsdmChannel DFSDM Channel Handle * @param SampleRate Audio frequency to be configured for the DFSDM Channel. * @note Being __weak it can be overwritten by the application * @note Channel output Clock Divider and Filter Oversampling are calculated as follow: * - Clock_Divider = CLK(input DFSDM)/CLK(micro) with * 1MHZ < CLK(micro) < 3.2MHZ (TYP 2.4MHZ for MP34DT01TR) * - Oversampling = CLK(input DFSDM)/(Clock_Divider * AudioFreq) * @retval HAL_status */ __weak HAL_StatusTypeDef MX_DFSDM1_Init(DFSDM_Filter_HandleTypeDef *hDfsdmFilter, DFSDM_Channel_HandleTypeDef *hDfsdmChannel, MX_DFSDM_Config *MXConfig) { HAL_StatusTypeDef ret = HAL_OK; /* MIC filters initialization */ __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(hDfsdmFilter); hDfsdmFilter->Instance = MXConfig->FilterInstance; hDfsdmFilter->Init.RegularParam.Trigger = MXConfig->RegularTrigger; hDfsdmFilter->Init.RegularParam.FastMode = ENABLE; hDfsdmFilter->Init.RegularParam.DmaMode = ENABLE; hDfsdmFilter->Init.InjectedParam.Trigger = DFSDM_FILTER_SW_TRIGGER; hDfsdmFilter->Init.InjectedParam.ScanMode = DISABLE; hDfsdmFilter->Init.InjectedParam.DmaMode = DISABLE; hDfsdmFilter->Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM8_TRGO; hDfsdmFilter->Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_BOTH_EDGES; hDfsdmFilter->Init.FilterParam.SincOrder = MXConfig->SincOrder; hDfsdmFilter->Init.FilterParam.Oversampling = MXConfig->Oversampling; hDfsdmFilter->Init.FilterParam.IntOversampling = 1; if(HAL_DFSDM_FilterInit(hDfsdmFilter) != HAL_OK) { ret = HAL_ERROR; } /* MIC channels initialization */ __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(hDfsdmChannel); hDfsdmChannel->Instance = MXConfig->ChannelInstance; hDfsdmChannel->Init.OutputClock.Activation = ENABLE; hDfsdmChannel->Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO; hDfsdmChannel->Init.OutputClock.Divider = MXConfig->ClockDivider; hDfsdmChannel->Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS; hDfsdmChannel->Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE; hDfsdmChannel->Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL; hDfsdmChannel->Init.Awd.FilterOrder = DFSDM_CHANNEL_SINC1_ORDER; hDfsdmChannel->Init.Awd.Oversampling = 10; hDfsdmChannel->Init.Offset = 0; hDfsdmChannel->Init.RightBitShift = MXConfig->RightBitShift; hDfsdmChannel->Init.Input.Pins = MXConfig->DigitalMicPins; hDfsdmChannel->Init.SerialInterface.Type = MXConfig->DigitalMicType; if(HAL_OK != HAL_DFSDM_ChannelInit(hDfsdmChannel)) { ret = HAL_ERROR; } /* Configure injected channel */ if(HAL_DFSDM_FilterConfigRegChannel(hDfsdmFilter, MXConfig->Channel4Filter, DFSDM_CONTINUOUS_CONV_ON) != HAL_OK) { ret = HAL_ERROR; } return ret; } #if ((USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) || (USE_HAL_SAI_REGISTER_CALLBACKS == 1U)) /** * @brief Default BSP AUDIO IN Msp Callbacks * @param Instance BSP AUDIO IN Instance * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_RegisterDefaultMspCallbacks (uint32_t Instance) { uint32_t i; if(Instance == 1U) { for(i = 0; i < DFSDM_MIC_NUMBER; i ++) { if(((AudioInCtx[Instance].Device >> i) & AUDIO_IN_DIGITAL_MIC1) == AUDIO_IN_DIGITAL_MIC1) { __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(&hAudioInDfsdmChannel[i]); __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hAudioInDfsdmFilter[i]); #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) /* Register MspInit/MspDeInit Callbacks */ if(HAL_DFSDM_CHANNEL_RegisterCallback(&hAudioInDfsdmChannel[i], HAL_DFSDM_CHANNEL_MSPINIT_CB_ID, DFSDM_ChannelMspInit) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else if(HAL_DFSDM_FILTER_RegisterCallback(&hAudioInDfsdmFilter[i], HAL_DFSDM_FILTER_MSPINIT_CB_ID, DFSDM_FilterMspInit) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else if(HAL_DFSDM_CHANNEL_RegisterCallback(&hAudioInDfsdmChannel[i], HAL_DFSDM_CHANNEL_MSPDEINIT_CB_ID, DFSDM_ChannelMspDeInit) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else if(HAL_DFSDM_FILTER_RegisterCallback(&hAudioInDfsdmFilter[i], HAL_DFSDM_FILTER_MSPDEINIT_CB_ID, DFSDM_FilterMspDeInit) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else { } #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) */ } } } else { return BSP_ERROR_WRONG_PARAM; } AudioInCtx[Instance].IsMspCallbacksValid = 1; /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief BSP AUDIO In Filter Msp Callback registering * @param Instance AUDIO IN Instance * @param CallBacks pointer to filter MspInit/MspDeInit functions * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_RegisterMspCallbacks (uint32_t Instance, CCA02M2_AUDIO_IN_Cb_t *CallBacks) { uint32_t i; if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { if(Instance == 1U) { #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) for(i = 0U; i < DFSDM_MIC_NUMBER; i ++) { __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hAudioInDfsdmFilter[i]); /* Register MspInit/MspDeInit Callback */ if(HAL_DFSDM_FILTER_RegisterCallback(&hAudioInDfsdmFilter[i], HAL_DFSDM_FILTER_MSPINIT_CB_ID, CallBacks->pMspFltrInitCb) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else if(HAL_DFSDM_FILTER_RegisterCallback(&hAudioInDfsdmFilter[i], HAL_DFSDM_FILTER_MSPDEINIT_CB_ID, CallBacks->pMspFltrDeInitCb) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else if(HAL_DFSDM_CHANNEL_RegisterCallback(&hAudioInDfsdmChannel[i], HAL_DFSDM_CHANNEL_MSPINIT_CB_ID, CallBacks->pMspChInitCb) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else if(HAL_DFSDM_CHANNEL_RegisterCallback(&hAudioInDfsdmChannel[i], HAL_DFSDM_CHANNEL_MSPDEINIT_CB_ID, CallBacks->pMspChDeInitCb) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else { return BSP_ERROR_WRONG_PARAM; } } #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) */ } else /* (Instance == 0U) */ { return BSP_ERROR_WRONG_PARAM; } AudioInCtx[Instance].IsMspCallbacksValid = 1; } /* Return BSP status */ return BSP_ERROR_NONE; } #endif /* ((USE_HAL_DFSDM_REGISTER_CALLBACKS == 1) || (USE_HAL_SAI_REGISTER_CALLBACKS == 1U)) */ #else #ifdef USE_STM32WBXX_NUCLEO /** * @brief Clock Config. * @param hSai: SAI handle if required * @param SampleRate: Audio frequency used to play the audio stream. * @note This API is called by CCA02M2_AUDIO_IN_Init() * Being __weak it can be overwritten by the application * @retval HAL_OK if no problem during execution, HAL_ERROR otherwise */ __weak HAL_StatusTypeDef MX_SAI_ClockConfig(SAI_HandleTypeDef *hSai, uint32_t PDM_rate) { UNUSED(hSai); HAL_StatusTypeDef ret = HAL_OK; /*SAI PLL Configuration*/ RCC_PeriphCLKInitTypeDef rccclkinit; HAL_RCCEx_GetPeriphCLKConfig(&rccclkinit); if ((PDM_rate % 1280U) == 0U) { rccclkinit.PLLSAI1.PLLN = 82; rccclkinit.PLLSAI1.PLLP = RCC_PLLP_DIV8; } else { rccclkinit.PLLSAI1.PLLN = 86; rccclkinit.PLLSAI1.PLLP = RCC_PLLP_DIV7; } rccclkinit.PeriphClockSelection = RCC_PERIPHCLK_SAI1; if(HAL_RCCEx_PeriphCLKConfig(&rccclkinit) != HAL_OK) { ret = HAL_ERROR; } return ret; } #else /** * @brief Clock Config. * @param hi2s: I2S handle if required * @param SampleRate: Audio frequency used to play the audio stream. * @note This API is called by CCA02M2_AUDIO_IN_Init() * Being __weak it can be overwritten by the application * @retval HAL_OK if no problem during execution, HAL_ERROR otherwise */ __weak HAL_StatusTypeDef MX_I2S_IN_ClockConfig(I2S_HandleTypeDef *hi2s, uint32_t PDM_rate) { UNUSED(hi2s); HAL_StatusTypeDef ret = HAL_OK; /*I2S PLL Configuration*/ RCC_PeriphCLKInitTypeDef rccclkinit; HAL_RCCEx_GetPeriphCLKConfig(&rccclkinit); #if defined(STM32F446xx) rccclkinit.PLLI2S.PLLI2SQ = 2; rccclkinit.PLLI2SDivQ = 1; #endif if ((PDM_rate % 1280U) == 0U) { #if defined(STM32F411xE) || defined (STM32F446xx) rccclkinit.PLLI2S.PLLI2SM = 10; rccclkinit.PLLI2S.PLLI2SN = 96; #else rccclkinit.PLLI2S.PLLI2SN = 192; #endif rccclkinit.PLLI2S.PLLI2SR = 5; } else { #if defined(STM32F411xE) || defined (STM32F446xx) rccclkinit.PLLI2S.PLLI2SM = 8; #endif rccclkinit.PLLI2S.PLLI2SN = 258; rccclkinit.PLLI2S.PLLI2SR = 3; } #if defined(STM32F446xx) rccclkinit.PeriphClockSelection = RCC_PERIPHCLK_I2S_APB2; #else rccclkinit.PeriphClockSelection = RCC_PERIPHCLK_I2S; #endif if(HAL_RCCEx_PeriphCLKConfig(&rccclkinit) != HAL_OK) { ret = HAL_ERROR; } return ret; } __weak HAL_StatusTypeDef MX_SPI_Init(SPI_HandleTypeDef* hspi, MX_SPI_Config *MXConfig) { static DMA_HandleTypeDef hdma_rx; HAL_StatusTypeDef ret = HAL_OK; hspi->Init.BaudRatePrescaler = MXConfig->BaudRatePrescaler; hspi->Init.Direction = MXConfig->Direction; hspi->Init.CLKPhase = MXConfig->CLKPhase; hspi->Init.CLKPolarity = MXConfig->CLKPolarity; hspi->Init.CRCCalculation = MXConfig->CRCCalculation; hspi->Init.CRCPolynomial = MXConfig->CRCPolynomial; hspi->Init.DataSize = MXConfig->DataSize; hspi->Init.FirstBit = MXConfig->FirstBit; hspi->Init.NSS = MXConfig->NSS; hspi->Init.TIMode = MXConfig->TIMode; hspi->Init.Mode = MXConfig->Mode; /* Configure the DMA handler for Transmission process */ hdma_rx.Instance = AUDIO_IN_SPI_RX_DMA_STREAM; hdma_rx.Init.Channel = AUDIO_IN_SPI_RX_DMA_CHANNEL; hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_rx.Init.Mode = DMA_CIRCULAR; hdma_rx.Init.Priority = DMA_PRIORITY_HIGH; hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_rx.Init.MemBurst = DMA_MBURST_INC4; hdma_rx.Init.PeriphBurst = DMA_PBURST_INC4; /* Configure the DMA Stream */ if (HAL_DMA_Init(&hdma_rx) != HAL_OK) { ret = HAL_ERROR; } /* Associate the initialized DMA handle to the the SPI handle */ __HAL_LINKDMA(hspi, hdmarx, hdma_rx); return ret; } __weak HAL_StatusTypeDef MX_I2S_IN_Init(I2S_HandleTypeDef* hi2s, MX_I2S_IN_Config *MXConfig) { static DMA_HandleTypeDef hdma_i2sRx; HAL_StatusTypeDef ret = HAL_OK; hi2s->Init.DataFormat = MXConfig->DataFormat; hi2s->Init.AudioFreq = MXConfig->AudioFreq; hi2s->Init.ClockSource = MXConfig->ClockSource; hi2s->Init.CPOL = MXConfig->CPOL; hi2s->Init.MCLKOutput = MXConfig->MCLKOutput; hi2s->Init.Mode = MXConfig->Mode; hi2s->Init.Standard = MXConfig->Standard; #ifdef USE_STM32F4XX_NUCLEO hi2s->Init.FullDuplexMode = MXConfig->FullDuplexMode; #endif /* Enable the DMA clock */ AUDIO_IN_I2S_DMAx_CLK_ENABLE(); if(hi2s->Instance == AUDIO_IN_I2S_INSTANCE) { /* Configure the hdma_i2sRx handle parameters */ hdma_i2sRx.Init.Channel = AUDIO_IN_I2S_DMAx_CHANNEL; hdma_i2sRx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_i2sRx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_i2sRx.Init.MemInc = DMA_MINC_ENABLE; hdma_i2sRx.Init.PeriphDataAlignment = AUDIO_IN_I2S_DMAx_PERIPH_DATA_SIZE; hdma_i2sRx.Init.MemDataAlignment = AUDIO_IN_I2S_DMAx_MEM_DATA_SIZE; hdma_i2sRx.Init.Mode = DMA_CIRCULAR; hdma_i2sRx.Init.Priority = DMA_PRIORITY_HIGH; hdma_i2sRx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_i2sRx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_i2sRx.Init.MemBurst = DMA_MBURST_SINGLE; hdma_i2sRx.Init.PeriphBurst = DMA_MBURST_SINGLE; hdma_i2sRx.Instance = AUDIO_IN_I2S_DMAx_STREAM; /* Associate the DMA handle */ __HAL_LINKDMA(hi2s, hdmarx, hdma_i2sRx); /* Deinitialize the Stream for new transfer */ if (HAL_DMA_DeInit(&hdma_i2sRx) != HAL_OK) { ret = HAL_ERROR; } /* Configure the DMA Stream */ if (HAL_DMA_Init(&hdma_i2sRx) != HAL_OK) { ret = HAL_ERROR; } } else { ret = HAL_ERROR; } /* I2S DMA IRQ Channel configuration */ HAL_NVIC_SetPriority(AUDIO_IN_I2S_DMAx_IRQ, CCA02M2_AUDIO_IN_IT_PRIORITY, CCA02M2_AUDIO_IN_IT_PRIORITY); HAL_NVIC_EnableIRQ(AUDIO_IN_I2S_DMAx_IRQ); return ret; } #endif #endif #ifndef USE_STM32L4XX_NUCLEO /** * @brief Initialize the PDM library. * @param Instance AUDIO IN Instance * @param AudioFreq Audio sampling frequency * @param ChnlNbrIn Number of input audio channels in the PDM buffer * @param ChnlNbrOut Number of desired output audio channels in the resulting PCM buffer * @retval BSP status */ __weak int32_t CCA02M2_AUDIO_IN_PDMToPCM_Init(uint32_t Instance, uint32_t AudioFreq, uint32_t ChnlNbrIn, uint32_t ChnlNbrOut) { if(Instance != 0U) { return BSP_ERROR_WRONG_PARAM; } else { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else uint32_t index; #if (ENABLE_HIGH_PERFORMANCE_MODE == 0U) static int16_t aState_ARM[4][DECIMATOR_STATE_LENGTH]; static int16_t aCoeffs[] = { -1406, 1634, -1943, 2386, -3080, 4325, -7223, 21690, 21690, -7223, 4325, -3080, 2386, -1943, 1634, -1406, }; #endif /* Enable CRC peripheral to unlock the PDM library */ __HAL_RCC_CRC_CLK_ENABLE(); for(index = 0; index < ChnlNbrIn; index++) { volatile uint32_t error = 0; /* Init PDM filters */ PDM_FilterHandler[index].bit_order = PDM_FILTER_BIT_ORDER_LSB; if (ChnlNbrIn == 1U) { PDM_FilterHandler[index].endianness = PDM_FILTER_ENDIANNESS_BE; /* For WB this should be LE, TODO after bugfix in PDMlib */ } else { PDM_FilterHandler[index].endianness = PDM_FILTER_ENDIANNESS_LE; } PDM_FilterHandler[index].high_pass_tap = 2122358088; PDM_FilterHandler[index].out_ptr_channels = (uint16_t)ChnlNbrOut; PDM_FilterHandler[index].in_ptr_channels = (uint16_t)ChnlNbrIn; /* PDM lib config phase */ PDM_FilterConfig[index].output_samples_number = (uint16_t) ((AudioFreq/1000U) * N_MS_PER_INTERRUPT); PDM_FilterConfig[index].mic_gain = 24; switch (AudioInCtx[0].DecimationFactor) { #if (ENABLE_HIGH_PERFORMANCE_MODE == 1U) case 64: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_64_HI_PERF; break; case 96: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_96_HI_PERF; break; #else case 16: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_16; break; case 24: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_24; break; case 32: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_32; break; case 48: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_48; break; case 64: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_64; break; case 80: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_80; break; case 128: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_128; break; case 160: PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_80; PDM_FilterConfig[index].output_samples_number *= 2U; PDM_FilterHandler[index].out_ptr_channels = 1; (void)arm_fir_decimate_init_q15 (&ARM_Decimator_State[index], DECIMATOR_NUM_TAPS, DECIMATOR_FACTOR, aCoeffs, aState_ARM[index], DECIMATOR_BLOCK_SIZE); break; #endif default: break; } #if (ENABLE_HIGH_PERFORMANCE_MODE == 1U) switch(AudioInCtx[0].BitsPerSample) { case AUDIO_RESOLUTION_16b: PDM_FilterConfig[index].bit_depth = PDM_FILTER_BITDEPTH_16; break; case AUDIO_RESOLUTION_24b: PDM_FilterConfig[index].bit_depth = PDM_FILTER_BITDEPTH_24; break; case AUDIO_RESOLUTION_32b: PDM_FilterConfig[index].bit_depth = PDM_FILTER_BITDEPTH_24IN32; break; default: break; } #endif error = PDM_Filter_Init((PDM_Filter_Handler_t *)(&PDM_FilterHandler[index])); if (error!=0U) { return BSP_ERROR_NO_INIT; } error = PDM_Filter_setConfig((PDM_Filter_Handler_t *)&PDM_FilterHandler[index], &PDM_FilterConfig[index]); if (error!=0U) { return BSP_ERROR_NO_INIT; } } #endif } return BSP_ERROR_NONE; } /** * @brief Converts audio format from PDM to PCM. * @param Instance AUDIO IN Instance * @param PDMBuf Pointer to PDM buffer data * @param PCMBuf Pointer to PCM buffer data * @retval BSP status */ __weak int32_t CCA02M2_AUDIO_IN_PDMToPCM(uint32_t Instance, uint16_t *PDMBuf, uint16_t *PCMBuf) { if(Instance != 0U) { return BSP_ERROR_WRONG_PARAM; } else { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else uint32_t index; for(index = 0; index < AudioInCtx[Instance].ChannelsNbr; index++) { if (AudioInCtx[Instance].SampleRate == 8000U) { uint16_t Decimate_Out[8U*N_MS_PER_INTERRUPT]; uint32_t ii; uint16_t PDM_Filter_Out[16U*N_MS_PER_INTERRUPT]; (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], PDM_Filter_Out, &PDM_FilterHandler[index]); (void)arm_fir_decimate_q15 (&ARM_Decimator_State[index], (q15_t *)&(PDM_Filter_Out), (q15_t*)&(Decimate_Out), DECIMATOR_BLOCK_SIZE); for (ii=0; ii<(8U*N_MS_PER_INTERRUPT); ii++) { PCMBuf[(ii * AudioInCtx[Instance].ChannelsNbr) + index] = Decimate_Out[ii]; } } else { switch(AudioInCtx[Instance].BitsPerSample) { case AUDIO_RESOLUTION_16b: (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], (uint16_t*)&(PCMBuf[index]), &PDM_FilterHandler[index]); break; case AUDIO_RESOLUTION_24b: (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], &((uint8_t*)(PCMBuf))[3U*index], &PDM_FilterHandler[index]); break; case AUDIO_RESOLUTION_32b: (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], (uint32_t*)&(PCMBuf[index]), &PDM_FilterHandler[index]); break; default: break; } } } #endif } return BSP_ERROR_NONE; } #endif /** * @brief Start audio recording. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param pbuf Main buffer pointer for the recorded data storing * @param NbrOfBytes Size of the record buffer. Parameter not used when Instance is 0 * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_Record(uint32_t Instance, uint8_t* pBuf, uint32_t NbrOfBytes) { if(Instance >= (AUDIO_IN_INSTANCES_NBR - 1U) ) { return BSP_ERROR_WRONG_PARAM; } else { AudioInCtx[Instance].pBuff = (uint16_t*)pBuf; if(Instance == 0U) { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else UNUSED(NbrOfBytes); #ifdef USE_STM32WBXX_NUCLEO if(HAL_SAI_Receive_DMA(&hAudioInSai, (uint8_t *)SAI_InternalBuffer, (uint16_t)(AudioInCtx[Instance].Size/2U * AudioInCtx[Instance].ChannelsNbr)) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } #else if(AudioInCtx[Instance].ChannelsNbr > 2U) { if(HAL_SPI_Receive_DMA(&hAudioInSPI, (uint8_t *)SPI_InternalBuffer, (uint16_t)AudioInCtx[Instance].Size) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } if(AudioInCtx[Instance].ChannelsNbr != 1U) { if(AUDIO_IN_Timer_Start() != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } if(HAL_I2S_Receive_DMA(&hAudioInI2s, I2S_InternalBuffer, (uint16_t)AudioInCtx[Instance].Size/2U) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } #endif /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RECORDING; #endif } else { #ifdef USE_STM32L4XX_NUCLEO int32_t counter; for (counter = (int32_t)(AudioInCtx[Instance].ChannelsNbr); counter > 0; counter --) { if(HAL_DFSDM_FilterRegularStart_DMA(&hAudioInDfsdmFilter[counter-1], MicRecBuff[counter-1], NbrOfBytes) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RECORDING; #else return BSP_ERROR_WRONG_PARAM; #endif } } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Stop audio recording. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_Stop(uint32_t Instance) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { if(Instance == 0U) { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else #ifdef USE_STM32WBXX_NUCLEO if(HAL_SAI_DMAStop(&hAudioInSai) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } #else if(AudioInCtx[Instance].ChannelsNbr > 2U) { if(HAL_SPI_DMAStop(&hAudioInSPI)!= HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } if(HAL_I2S_DMAStop(&hAudioInI2s) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } #endif #endif } else /*(Instance == 1U) */ { #ifdef USE_STM32L4XX_NUCLEO int32_t counter; for (counter = (int32_t)(AudioInCtx[Instance].ChannelsNbr); counter > 0; counter --) { if(HAL_DFSDM_FilterRegularStop_DMA(&hAudioInDfsdmFilter[counter-1]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } #else return BSP_ERROR_WRONG_PARAM; #endif } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_STOP; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Pause the audio file stream. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_Pause(uint32_t Instance) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { if(Instance == 0U) { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else #ifdef USE_STM32WBXX_NUCLEO if(HAL_SAI_DMAPause(&hAudioInSai)!= HAL_OK) { return BSP_ERROR_WRONG_PARAM; } #else if(HAL_I2S_DMAPause(&hAudioInI2s)!= HAL_OK) { return BSP_ERROR_WRONG_PARAM; } #endif #endif } else /* (Instance == 1U) */ { #ifdef USE_STM32L4XX_NUCLEO int32_t counter; for (counter = (int32_t)(AudioInCtx[Instance].ChannelsNbr); counter > 0; counter --) { if(HAL_DFSDM_FilterRegularStop_DMA(&hAudioInDfsdmFilter[counter-1]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } #else return BSP_ERROR_WRONG_PARAM; #endif } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_PAUSE; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Resume the audio file stream. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_Resume(uint32_t Instance) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { if(Instance == 0U) { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else #ifdef USE_STM32WBXX_NUCLEO if(HAL_SAI_DMAResume(&hAudioInSai)!= HAL_OK) { return BSP_ERROR_WRONG_PARAM; } #else if(HAL_I2S_DMAResume(&hAudioInI2s)!= HAL_OK) { return BSP_ERROR_WRONG_PARAM; } #endif #endif } else /* (Instance == 1U) */ { #ifdef USE_STM32L4XX_NUCLEO int32_t counter; for (counter = (int32_t)(AudioInCtx[Instance].ChannelsNbr); counter > 0; counter --) { if(HAL_DFSDM_FilterRegularStart_DMA(&hAudioInDfsdmFilter[counter-1], MicRecBuff[counter-1], DEFAULT_AUDIO_IN_BUFFER_SIZE) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } #else return BSP_ERROR_WRONG_PARAM; #endif } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RECORDING; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Starts audio recording. * @param Instance AUDIO IN Instance. It can be 1(DFSDM used) * @param pBuf Main buffer pointer for the recorded data storing * @param NbrOfBytes Size of the recorded buffer. Parameter not used when Instance is 0 * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_RecordChannels(uint32_t Instance, uint8_t **pBuf, uint32_t NbrOfBytes) { if(Instance != 1U) { return BSP_ERROR_WRONG_PARAM; } else { #ifdef USE_STM32L4XX_NUCLEO int8_t i; uint32_t mic_init[4] = {0}; uint32_t audio_in_digital_mic = AUDIO_IN_DIGITAL_MIC1, pbuf_index = 0; uint32_t enabled_mic=0; /* Get the number of activated microphones */ for(i = 0; i < DFSDM_MIC_NUMBER; i++) { if((AudioInCtx[Instance].Device & audio_in_digital_mic) == audio_in_digital_mic) { enabled_mic++; } audio_in_digital_mic = audio_in_digital_mic << 1; } AudioInCtx[Instance].pMultiBuff = pBuf; AudioInCtx[Instance].Size = NbrOfBytes; AudioInCtx[Instance].IsMultiBuff = 1; audio_in_digital_mic = AUDIO_IN_DIGITAL_MIC_LAST; for(i = 0; i < DFSDM_MIC_NUMBER; i++) { if( (mic_init[POS_VAL(audio_in_digital_mic)] != 1U) && ( (AudioInCtx[Instance].Device & audio_in_digital_mic) == audio_in_digital_mic) ) { /* Call the Media layer start function for MICx channel */ if(HAL_DFSDM_FilterRegularMsbStart_DMA(&hAudioInDfsdmFilter[POS_VAL(audio_in_digital_mic)], (int16_t*)pBuf[enabled_mic - 1U - pbuf_index], NbrOfBytes) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } else { mic_init[POS_VAL(audio_in_digital_mic)] = 1; pbuf_index++; } } audio_in_digital_mic = audio_in_digital_mic >> 1; } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RECORDING; /* Return BSP status */ return BSP_ERROR_NONE; #else UNUSED(pBuf); UNUSED(NbrOfBytes); return BSP_ERROR_WRONG_PARAM; #endif } } /** * @brief Stop audio recording. * @param Instance AUDIO IN Instance. It can be 1(DFSDM used) * @param Device Digital input device to be stopped * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_StopChannels(uint32_t Instance, uint32_t Device) { /* Stop selected devices */ int32_t ret = CCA02M2_AUDIO_IN_PauseChannels(Instance, Device); /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_STOP; /* Return BSP status */ return ret; } /** * @brief Pause the audio file stream. * @param Instance AUDIO IN Instance. It can be 1(DFSDM used) * @param Device Digital mic to be paused * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_PauseChannels(uint32_t Instance, uint32_t Device) { if((Instance != 1U) || ((Device < AUDIO_IN_DIGITAL_MIC1) && (Device > AUDIO_IN_DIGITAL_MIC_LAST))) { return BSP_ERROR_WRONG_PARAM; } else { #ifdef USE_STM32L4XX_NUCLEO uint32_t audio_in_digital_mic = AUDIO_IN_DIGITAL_MIC1; int8_t i; for(i = 0; i < DFSDM_MIC_NUMBER; i++) { if((Device & audio_in_digital_mic) == audio_in_digital_mic) { /* Call the Media layer stop function */ if(HAL_DFSDM_FilterRegularStop_DMA(&hAudioInDfsdmFilter[POS_VAL(audio_in_digital_mic)]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } audio_in_digital_mic = audio_in_digital_mic << 1; } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_PAUSE; /* Return BSP status */ return BSP_ERROR_NONE; #else return BSP_ERROR_WRONG_PARAM; #endif } } /** * @brief Resume the audio file stream * @param Instance AUDIO IN Instance. It can be 1(DFSDM used) * @param Device Digital mic to be resumed * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_ResumeChannels(uint32_t Instance, uint32_t Device) { if((Instance != 1U) || ((Device < AUDIO_IN_DIGITAL_MIC1) && (Device > AUDIO_IN_DIGITAL_MIC_LAST))) { return BSP_ERROR_WRONG_PARAM; } else { #ifdef USE_STM32L4XX_NUCLEO uint32_t audio_in_digital_mic = AUDIO_IN_DIGITAL_MIC_LAST; int8_t i; for(i = 0; i < DFSDM_MIC_NUMBER; i++) { if((Device & audio_in_digital_mic) == audio_in_digital_mic) { /* Start selected device channel */ if(HAL_DFSDM_FilterRegularMsbStart_DMA(&hAudioInDfsdmFilter[POS_VAL(audio_in_digital_mic)],\ (int16_t*)AudioInCtx[Instance].pMultiBuff[POS_VAL(audio_in_digital_mic)], AudioInCtx[Instance].Size) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } audio_in_digital_mic = audio_in_digital_mic >> 1; } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RECORDING; /* Return BSP status */ return BSP_ERROR_NONE; #else return BSP_ERROR_WRONG_PARAM; #endif } } /** * @brief Start audio recording. * @param Instance AUDIO IN SAI PDM Instance. It can be only 2 * @param pbuf Main buffer pointer for the recorded data storing * @param NbrOfBytes Size of the record buffer. Parameter not used when Instance is 0 * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_RecordPDM(uint32_t Instance, uint8_t* pBuf, uint32_t NbrOfBytes) { if(Instance != 2U) { return BSP_ERROR_WRONG_PARAM; } else { #ifdef USE_STM32L4XX_NUCLEO static SAI_HandleTypeDef hAudioInSai; /* Start the process receive DMA */ if(HAL_SAI_Receive_DMA(&hAudioInSai, (uint8_t*)pBuf, (uint16_t)(NbrOfBytes/(AudioInCtx[Instance].BitsPerSample/8U))) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } /* Update BSP AUDIO IN state */ AudioInCtx[Instance].State = AUDIO_IN_STATE_RECORDING; /* Return BSP status */ return BSP_ERROR_NONE; #else UNUSED(pBuf); UNUSED(NbrOfBytes); return BSP_ERROR_WRONG_PARAM; #endif } } /** * @brief Set Audio In device * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param Device The audio input device to be used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_SetDevice(uint32_t Instance, uint32_t Device) { CCA02M2_AUDIO_Init_t audio_init; if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else if(AudioInCtx[Instance].State == AUDIO_IN_STATE_STOP) { if(Instance == 1U) { #ifdef USE_STM32L4XX_NUCLEO int8_t i; for(i = 0; i < DFSDM_MIC_NUMBER; i ++) { if(((Device >> (uint8_t)i) & AUDIO_IN_DIGITAL_MIC1) == AUDIO_IN_DIGITAL_MIC1) { if(HAL_DFSDM_ChannelDeInit(&hAudioInDfsdmChannel[i]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } } #else return BSP_ERROR_WRONG_PARAM; #endif } audio_init.Device = Device; audio_init.ChannelsNbr = AudioInCtx[Instance].ChannelsNbr; audio_init.SampleRate = AudioInCtx[Instance].SampleRate; audio_init.BitsPerSample = AudioInCtx[Instance].BitsPerSample; audio_init.Volume = AudioInCtx[Instance].Volume; if(CCA02M2_AUDIO_IN_Init(Instance, &audio_init) != BSP_ERROR_NONE) { return BSP_ERROR_NO_INIT; } } else { return BSP_ERROR_BUSY; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Get Audio In device * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param Device The audio input device used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_GetDevice(uint32_t Instance, uint32_t *Device) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Return audio Input Device */ *Device = AudioInCtx[Instance].Device; } return BSP_ERROR_NONE; } /** * @brief Set Audio In frequency * @param Instance Audio IN instance * @param SampleRate Input frequency to be set * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_SetSampleRate(uint32_t Instance, uint32_t SampleRate) { CCA02M2_AUDIO_Init_t audio_init; if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else if(AudioInCtx[Instance].State == AUDIO_IN_STATE_STOP) { if(Instance == 1U) { #ifdef USE_STM32L4XX_NUCLEO int8_t i; for(i = 0; i < DFSDM_MIC_NUMBER; i ++) { if(((AudioInCtx[Instance].Device >> (uint8_t)i) & AUDIO_IN_DIGITAL_MIC1) == AUDIO_IN_DIGITAL_MIC1) { if(HAL_DFSDM_ChannelDeInit(&hAudioInDfsdmChannel[i]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } if(HAL_DFSDM_FilterDeInit(&hAudioInDfsdmFilter[i]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } } #else return BSP_ERROR_WRONG_PARAM; #endif } audio_init.Device = AudioInCtx[Instance].Device; audio_init.ChannelsNbr = AudioInCtx[Instance].ChannelsNbr; audio_init.SampleRate = SampleRate; audio_init.BitsPerSample = AudioInCtx[Instance].BitsPerSample; audio_init.Volume = AudioInCtx[Instance].Volume; if(CCA02M2_AUDIO_IN_Init(Instance, &audio_init) != BSP_ERROR_NONE) { return BSP_ERROR_NO_INIT; } } else { return BSP_ERROR_BUSY; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Get Audio In frequency * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param SampleRate Audio Input frequency to be returned * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_GetSampleRate(uint32_t Instance, uint32_t *SampleRate) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Return audio in frequency */ *SampleRate = AudioInCtx[Instance].SampleRate; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Set Audio In Resolution * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param BitsPerSample Input resolution to be set * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_SetBitsPerSample(uint32_t Instance, uint32_t BitsPerSample) { CCA02M2_AUDIO_Init_t audio_init; if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else if(AudioInCtx[Instance].State == AUDIO_IN_STATE_STOP) { if(Instance == 1U) { #ifdef USE_STM32L4XX_NUCLEO int8_t i; for(i = 0; i < DFSDM_MIC_NUMBER; i ++) { if(((AudioInCtx[Instance].Device >> (uint8_t)i) & AUDIO_IN_DIGITAL_MIC1) == AUDIO_IN_DIGITAL_MIC1) { if(HAL_DFSDM_ChannelDeInit(&hAudioInDfsdmChannel[i]) != HAL_OK) { return BSP_ERROR_PERIPH_FAILURE; } } } #else return BSP_ERROR_WRONG_PARAM; #endif } audio_init.Device = AudioInCtx[Instance].Device; audio_init.ChannelsNbr = AudioInCtx[Instance].ChannelsNbr; audio_init.SampleRate = AudioInCtx[Instance].SampleRate; audio_init.BitsPerSample = BitsPerSample; audio_init.Volume = AudioInCtx[Instance].Volume; if(CCA02M2_AUDIO_IN_Init(Instance, &audio_init) != BSP_ERROR_NONE) { return BSP_ERROR_NO_INIT; } } else { return BSP_ERROR_BUSY; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Get Audio In Resolution * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param BitsPerSample Input resolution to be returned * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_GetBitsPerSample(uint32_t Instance, uint32_t *BitsPerSample) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Return audio in resolution */ *BitsPerSample = AudioInCtx[Instance].BitsPerSample; } return BSP_ERROR_NONE; } /** * @brief Set Audio In Channel number * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param ChannelNbr Channel number to be used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_SetChannelsNbr(uint32_t Instance, uint32_t ChannelNbr) { if((Instance >= AUDIO_IN_INSTANCES_NBR) || (ChannelNbr > 2U)) { return BSP_ERROR_WRONG_PARAM; } else { /* Update AudioIn Context */ AudioInCtx[Instance].ChannelsNbr = ChannelNbr; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Get Audio In Channel number * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param ChannelNbr Channel number to be used * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_GetChannelsNbr(uint32_t Instance, uint32_t *ChannelNbr) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Channel number to be returned */ *ChannelNbr = AudioInCtx[Instance].ChannelsNbr; } return BSP_ERROR_NONE; } /** * @brief Set the current audio in volume level. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param Volume Volume level to be returnd * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_SetVolume(uint32_t Instance, uint32_t Volume) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else if (Instance == 0U) { #ifdef USE_STM32L4XX_NUCLEO return BSP_ERROR_WRONG_PARAM; #else uint32_t index; static int16_t VolumeGain[] = { -12,-12,-6,-3,0,2,3,5,6,7,8,9,9,10,11,11,12,12,13,13,14,14,15,15,15, 16,16,17,17,17,17,18,18,18,19,19,19,19,19,20,20,20,20,21,21,21,21,21, 22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24,24,24,24,25,25,25, 25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27, 27,27,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29, 29,29,29,29,30,30,30,30,30,30,30,31 }; for (index = 0; index < AudioInCtx[Instance].ChannelsNbr; index++) { if (PDM_FilterConfig[index].mic_gain != VolumeGain[Volume]) { PDM_FilterConfig[index].mic_gain = VolumeGain[Volume]; (void)PDM_Filter_setConfig((PDM_Filter_Handler_t *)&PDM_FilterHandler[index], &PDM_FilterConfig[index]); } } #endif } else { /* Update AudioIn Context */ AudioInCtx[Instance].Volume = Volume; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Get the current audio in volume level. * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param Volume Volume level to be returnd * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_GetVolume(uint32_t Instance, uint32_t *Volume) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Input Volume to be returned */ *Volume = AudioInCtx[Instance].Volume; } /* Return BSP status */ return BSP_ERROR_NONE; } /** * @brief Get Audio In device * @param Instance AUDIO IN Instance. It can be 0 when I2S / SPI is used or 1 if DFSDM is used * @param State Audio Out state * @retval BSP status */ int32_t CCA02M2_AUDIO_IN_GetState(uint32_t Instance, uint32_t *State) { if(Instance >= AUDIO_IN_INSTANCES_NBR) { return BSP_ERROR_WRONG_PARAM; } else { /* Input State to be returned */ *State = AudioInCtx[Instance].State; } return BSP_ERROR_NONE; } #ifdef USE_STM32L4XX_NUCLEO /** * @brief CCA02M2 AUDIO IN interrupt handler. * @param Instance Audio in instance. * @param Device Device of the audio in stream. * @retval None. */ void CCA02M2_AUDIO_IN_IRQHandler(uint32_t Instance, uint32_t Device) { /* Prevent unused argument(s) compilation warning */ UNUSED(Instance); if (Device == AUDIO_IN_DIGITAL_MIC1) { HAL_DMA_IRQHandler(hAudioInDfsdmFilter[0].hdmaReg); } else if (Device == AUDIO_IN_DIGITAL_MIC2) { HAL_DMA_IRQHandler(hAudioInDfsdmFilter[1].hdmaReg); } else if (Device == AUDIO_IN_DIGITAL_MIC3) { HAL_DMA_IRQHandler(hAudioInDfsdmFilter[2].hdmaReg); } else { HAL_DMA_IRQHandler(hAudioInDfsdmFilter[3].hdmaReg); } } #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 0U) /** * @brief Regular conversion complete callback. * @note In interrupt mode, user has to read conversion value in this function using HAL_DFSDM_FilterGetRegularValue. * @param hdfsdm_filter DFSDM filter handle. * @retval None */ void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { uint32_t i, j; if(AudioInCtx[1].IsMultiBuff == 1U) { /* Call the record update function to get the second half */ CCA02M2_AUDIO_IN_TransferComplete_CallBack(1); } else { if(hdfsdm_filter == &hAudioInDfsdmFilter[POS_VAL(AUDIO_IN_DIGITAL_MIC1)]) { for(j=0; j < AudioInCtx[1].ChannelsNbr; j ++) { for (i = 0; i < ((AudioInCtx[1].SampleRate / (uint32_t)1000) * N_MS_PER_INTERRUPT); i++) { AudioInCtx[1].HP_Filters[j].Z = ((MicRecBuff[j][i + ((AudioInCtx[1].SampleRate / (uint32_t)1000) * N_MS_PER_INTERRUPT)] /256) * (int32_t)(AudioInCtx[1].Volume)) /128; AudioInCtx[1].HP_Filters[j].oldOut = (0xFC * (AudioInCtx[1].HP_Filters[j].oldOut + AudioInCtx[1].HP_Filters[j].Z - AudioInCtx[1].HP_Filters[j].oldIn)) / 256; AudioInCtx[1].HP_Filters[j].oldIn = AudioInCtx[1].HP_Filters[j].Z; AudioInCtx[1].pBuff[(i * AudioInCtx[1].ChannelsNbr) + j] = (uint16_t) (SaturaLH(AudioInCtx[1].HP_Filters[j].oldOut, -32760, 32760)); } } CCA02M2_AUDIO_IN_TransferComplete_CallBack(1); } } } /** * @brief Half regular conversion complete callback. * @param hdfsdm_filter DFSDM filter handle. * @retval None */ void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { uint32_t i, j; if(AudioInCtx[1].IsMultiBuff == 1U) { /* Call the record update function to get the first half */ CCA02M2_AUDIO_IN_HalfTransfer_CallBack(1); } else { if(hdfsdm_filter == &hAudioInDfsdmFilter[POS_VAL(AUDIO_IN_DIGITAL_MIC1)]) { for(j=0; j < AudioInCtx[1].ChannelsNbr; j ++) { for (i = 0; i < ((AudioInCtx[1].SampleRate / (uint32_t)1000)* N_MS_PER_INTERRUPT); i++) { AudioInCtx[1].HP_Filters[j].Z = ((MicRecBuff[j][i] /256) * (int32_t)(AudioInCtx[1].Volume)) /128; AudioInCtx[1].HP_Filters[j].oldOut = (0xFC * (AudioInCtx[1].HP_Filters[j].oldOut + AudioInCtx[1].HP_Filters[j].Z - AudioInCtx[1].HP_Filters[j].oldIn)) / 256; AudioInCtx[1].HP_Filters[j].oldIn = AudioInCtx[1].HP_Filters[j].Z; AudioInCtx[1].pBuff[(i * AudioInCtx[1].ChannelsNbr) + j] = (uint16_t) (SaturaLH(AudioInCtx[1].HP_Filters[j].oldOut, -32760, 32760)); } } CCA02M2_AUDIO_IN_HalfTransfer_CallBack(1); } } } #endif #else #ifdef USE_STM32WBXX_NUCLEO /** * @brief Rx Transfer completed callbacks. It performs demuxing of the bit-interleaved PDM streams into byte-interleaved data suitable for PDM to PCM conversion. 1 ms of data for each microphone is written into the buffer that the user indicates when calling the CCA02M2_AUDIO_IN_Start(...) function. * @param hSai: SAI handle. Not used * @retval None */ void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hSai) { UNUSED(hSai); uint32_t index; switch(AudioInCtx[0].ChannelsNbr) { case 1: { uint8_t * DataTempSAI = &(((uint8_t *)SAI_InternalBuffer)[AudioInCtx[0].Size/2U]) ; for(index = 0; index < (AudioInCtx[0].Size/4U) ; index++) { ((uint8_t *)(AudioInCtx[0].pBuff))[index] = (DataTempSAI[2U*index]); } /* Remove after bugfix in PDMlib */ for(index = 0; index < (AudioInCtx[0].Size/8U) ; index++) { ((uint16_t *)(AudioInCtx[0].pBuff))[index] = HTONS(((uint16_t *)(AudioInCtx[0].pBuff))[index]); } break; } case 2: { uint8_t * DataTempSAI = &(((uint8_t *)SAI_InternalBuffer)[AudioInCtx[0].Size]) ; for(index = 0; index < (AudioInCtx[0].Size) ; index++) { ((uint8_t *)(AudioInCtx[0].pBuff))[index] = (DataTempSAI[index]); } break; } case 4: { uint8_t * DataTempSAI = &(((uint8_t *)SAI_InternalBuffer)[AudioInCtx[0].Size * 2U]) ; for(index = 0; index < (AudioInCtx[0].Size * 2U) ; index++) { ((uint8_t *)(AudioInCtx[0].pBuff))[index] = (DataTempSAI[index]); } break; } default: { break; } } CCA02M2_AUDIO_IN_TransferComplete_CallBack(0); } /** * @brief Rx Transfer completed callbacks. It performs demuxing of the bit-interleaved PDM streams into byte-interleaved data suitable for PDM to PCM conversion. 1 ms of data for each microphone is written into the buffer that the user indicates when calling the CCA02M2_AUDIO_IN_Start(...) function. * @param hSai: SAI handle. Not used * @retval None */ void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hSai) { UNUSED(hSai); uint32_t index; switch(AudioInCtx[0].ChannelsNbr) { case 1: { uint8_t * DataTempSAI = (uint8_t *)SAI_InternalBuffer; for(index = 0; index < (AudioInCtx[0].Size/4U) ; index++) { ((uint8_t *)(AudioInCtx[0].pBuff))[index] = (DataTempSAI[2U*index]); } /* Remove after bugfix in PDMlib */ for(index = 0; index < (AudioInCtx[0].Size/8U) ; index++) { ((uint16_t *)(AudioInCtx[0].pBuff))[index] = HTONS(((uint16_t *)(AudioInCtx[0].pBuff))[index]); } break; } case 2: { uint8_t * DataTempSAI = (uint8_t *)SAI_InternalBuffer; for(index = 0; index < (AudioInCtx[0].Size); index++) { ((uint8_t *)(AudioInCtx[0].pBuff))[index] = (DataTempSAI[index]); } break; } case 4: { uint8_t * DataTempSAI = (uint8_t *)SAI_InternalBuffer; for(index = 0; index < (AudioInCtx[0].Size * 2U); index++) { ((uint8_t *)(AudioInCtx[0].pBuff))[index] = (DataTempSAI[index]); } break; } default: { break; } } CCA02M2_AUDIO_IN_HalfTransfer_CallBack(0); } #else /** * @brief Rx Transfer completed callbacks. It performs demuxing of the bit-interleaved PDM streams into byte-interleaved data suitable for PDM to PCM conversion. 1 ms of data for each microphone is written into the buffer that the user indicates when calling the CCA02M2_AUDIO_IN_Start(...) function. * @param hi2s: I2S handle * @retval None */ void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { UNUSED(hi2s); uint32_t index; switch(AudioInCtx[0].ChannelsNbr) { case 1: { uint16_t * DataTempI2S = &I2S_InternalBuffer[AudioInCtx[0].Size/4U] ; for(index = 0; index < (AudioInCtx[0].Size/4U); index++) { AudioInCtx[0].pBuff[index] = (DataTempI2S[index]); } break; } case 2: { uint16_t * DataTempI2S = &(I2S_InternalBuffer[AudioInCtx[0].Size/2U]); uint8_t a,b; for(index=0; index<(AudioInCtx[0].Size/2U); index++) { a = ((uint8_t *)(DataTempI2S))[(index*2U)]; b = ((uint8_t *)(DataTempI2S))[(index*2U)+1U]; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*2U)] = Channel_Demux[a & CHANNEL_DEMUX_MASK] | (Channel_Demux[b & CHANNEL_DEMUX_MASK] << 4);; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*2U)+1U] = Channel_Demux[(a>>1) & CHANNEL_DEMUX_MASK] | (Channel_Demux[(b>>1) & CHANNEL_DEMUX_MASK] << 4); } break; } case 4: { uint16_t * DataTempI2S = &(I2S_InternalBuffer[AudioInCtx[0].Size/2U]); uint16_t * DataTempSPI = &(SPI_InternalBuffer[AudioInCtx[0].Size/2U]); uint8_t a,b; for(index=0; index<(AudioInCtx[0].Size/2U); index++) { a = ((uint8_t *)(DataTempI2S))[(index*2U)]; b = ((uint8_t *)(DataTempI2S))[(index*2U)+1U]; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)] = Channel_Demux[a & CHANNEL_DEMUX_MASK] | (Channel_Demux[b & CHANNEL_DEMUX_MASK] << 4);; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)+1U] = Channel_Demux[(a>>1) & CHANNEL_DEMUX_MASK] | (Channel_Demux[(b>>1) & CHANNEL_DEMUX_MASK] << 4); a = ((uint8_t *)(DataTempSPI))[(index*2U)]; b = ((uint8_t *)(DataTempSPI))[(index*2U)+1U]; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)+2U] = Channel_Demux[a & CHANNEL_DEMUX_MASK] | (Channel_Demux[b & CHANNEL_DEMUX_MASK] << 4);; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)+3U] = Channel_Demux[(a>>1) & CHANNEL_DEMUX_MASK] | (Channel_Demux[(b>>1) & CHANNEL_DEMUX_MASK] << 4); } break; } default: { break; } } CCA02M2_AUDIO_IN_TransferComplete_CallBack(0); } /** * @brief Rx Transfer completed callbacks. It performs demuxing of the bit-interleaved PDM streams into byte-interleaved data suitable for PDM to PCM conversion. 1 ms of data for each microphone is written into the buffer that the user indicates when calling the CCA02M2_AUDIO_IN_Start(...) function. * @param hi2s: I2S handle * @retval None */ void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { UNUSED(hi2s); uint32_t index; switch(AudioInCtx[0].ChannelsNbr) { case 1: { uint16_t * DataTempI2S = I2S_InternalBuffer; for(index = 0; index < (AudioInCtx[0].Size/4U); index++) { AudioInCtx[0].pBuff[index] = (DataTempI2S[index]); } break; } case 2: { uint16_t * DataTempI2S = I2S_InternalBuffer; uint8_t a,b; for(index=0; index<(AudioInCtx[0].Size/2U); index++) { a = ((uint8_t *)(DataTempI2S))[(index*2U)]; b = ((uint8_t *)(DataTempI2S))[(index*2U)+1U]; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*2U)] = Channel_Demux[a & CHANNEL_DEMUX_MASK] | (Channel_Demux[b & CHANNEL_DEMUX_MASK] << 4);; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*2U)+1U] = Channel_Demux[(a>>1) & CHANNEL_DEMUX_MASK] | (Channel_Demux[(b>>1) & CHANNEL_DEMUX_MASK] << 4); } break; } case 4: { uint16_t * DataTempI2S = I2S_InternalBuffer; uint16_t * DataTempSPI = SPI_InternalBuffer; uint8_t a,b; for(index=0; index<(AudioInCtx[0].Size/2U); index++) { a = ((uint8_t *)(DataTempI2S))[(index*2U)]; b = ((uint8_t *)(DataTempI2S))[(index*2U)+1U]; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)] = Channel_Demux[a & CHANNEL_DEMUX_MASK] | (Channel_Demux[b & CHANNEL_DEMUX_MASK] << 4);; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)+1U] = Channel_Demux[(a>>1) & CHANNEL_DEMUX_MASK] | (Channel_Demux[(b>>1) & CHANNEL_DEMUX_MASK] << 4); a = ((uint8_t *)(DataTempSPI))[(index*2U)]; b = ((uint8_t *)(DataTempSPI))[(index*2U)+1U]; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)+2U] = Channel_Demux[a & CHANNEL_DEMUX_MASK] | (Channel_Demux[b & CHANNEL_DEMUX_MASK] << 4);; ((uint8_t *)(AudioInCtx[0].pBuff))[(index*4U)+3U] = Channel_Demux[(a>>1) & CHANNEL_DEMUX_MASK] | (Channel_Demux[(b>>1) & CHANNEL_DEMUX_MASK] << 4); } break; } default: { break; } } CCA02M2_AUDIO_IN_HalfTransfer_CallBack(0); } #endif #endif /** * @brief User callback when record buffer is filled. * @retval None */ __weak void CCA02M2_AUDIO_IN_TransferComplete_CallBack(uint32_t Instance) { /* Prevent unused argument(s) compilation warning */ UNUSED(Instance); /* This function should be implemented by the user application. It is called into this driver when the current buffer is filled to prepare the next buffer pointer and its size. */ } /** * @brief Manages the DMA Half Transfer complete event. * @retval None */ __weak void CCA02M2_AUDIO_IN_HalfTransfer_CallBack(uint32_t Instance) { /* Prevent unused argument(s) compilation warning */ UNUSED(Instance); /* This function should be implemented by the user application. It is called into this driver when the current buffer is filled to prepare the next buffer pointer and its size. */ } /** * @brief Audio IN Error callback function. * @retval None */ __weak void CCA02M2_AUDIO_IN_Error_CallBack(uint32_t Instance) { /* Prevent unused argument(s) compilation warning */ UNUSED(Instance); /* This function is called when an Interrupt due to transfer error on or peripheral error occurs. */ } /** * @} */ /** @defgroup CCA02M2_AUDIO_IN_Private_Functions CCA02M2_AUDIO_IN Private Functions * @{ */ /******************************************************************************* Static Functions *******************************************************************************/ #if (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) /** * @brief Regular conversion complete callback. * @note In interrupt mode, user has to read conversion value in this function using HAL_DFSDM_FilterGetRegularValue. * @param hdfsdm_filter DFSDM filter handle. * @retval None */ static void DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { uint32_t i, j = 0; uint32_t index; if(AudioInCtx[1].IsMultiBuff == 1U) { /* Call the record update function to get the second half */ CCA02M2_AUDIO_IN_TransferComplete_CallBack(1); } else { if(hdfsdm_filter == &hAudioInDfsdmFilter[POS_VAL(AUDIO_IN_DIGITAL_MIC1)]) { for(j=0; j < AudioInCtx[1].ChannelsNbr; j ++) { for (i = 0; i < (AudioInCtx[1].SampleRate / 1000); i++) { AudioInCtx[1].HP_Filters[j].Z = ((MicRecBuff[j][i + (AudioInCtx[1].SampleRate / 1000) ] >> 8) * AudioInCtx[1].Volume) >> 7; AudioInCtx[1].HP_Filters[j].oldOut = (0xFC * (AudioInCtx[1].HP_Filters[j].oldOut + AudioInCtx[1].HP_Filters[j].Z - AudioInCtx[1].HP_Filters[j].oldIn)) / 256; AudioInCtx[1].HP_Filters[j].oldIn = AudioInCtx[1].HP_Filters[j].Z; AudioInCtx[1].pBuff[i * AudioInCtx[1].ChannelsNbr + j] = SaturaLH(AudioInCtx[1].HP_Filters[j].oldOut, -32760, 32760); RecBuffTrigger +=1U; } } } /* Call Half Transfer Complete callback */ if(RecBuffTrigger == (AudioInCtx[1].Size/2U)) { if(RecBuffHalf == 0U) { RecBuffHalf = 1; CCA02M2_AUDIO_IN_HalfTransfer_CallBack(1); } } /* Call Transfer Complete callback */ if(RecBuffTrigger == AudioInCtx[1].Size) { /* Reset Application Buffer Trigger */ RecBuffTrigger = 0; RecBuffHalf = 0; /* Call the record update function to get the next buffer to fill and its size (size is ignored) */ CCA02M2_AUDIO_IN_TransferComplete_CallBack(1); } } } /** * @brief Half regular conversion complete callback. * @param hdfsdm_filter DFSDM filter handle. * @retval None */ static void DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { uint32_t i, j = 0; uint32_t index; if(AudioInCtx[1].IsMultiBuff == 1U) { /* Call the record update function to get the first half */ CCA02M2_AUDIO_IN_HalfTransfer_CallBack(1); } else if(hdfsdm_filter == &hAudioInDfsdmFilter[POS_VAL(AUDIO_IN_DIGITAL_MIC1)]) { for(j=0; j < AudioInCtx[1].ChannelsNbr; j ++) { for (i = 0; i < (AudioInCtx[1].SampleRate / 1000); i++) { AudioInCtx[1].HP_Filters[j].Z = ((MicRecBuff[j][i] >> 8) * AudioInCtx[1].Volume) >> 7; AudioInCtx[1].HP_Filters[j].oldOut = (0xFC * (AudioInCtx[1].HP_Filters[j].oldOut + AudioInCtx[1].HP_Filters[j].Z - AudioInCtx[1].HP_Filters[j].oldIn)) / 256; AudioInCtx[1].HP_Filters[j].oldIn = AudioInCtx[1].HP_Filters[j].Z; AudioInCtx[1].pBuff[i * AudioInCtx[1].ChannelsNbr + j] = SaturaLH(AudioInCtx[1].HP_Filters[j].oldOut, -32760, 32760); RecBuffTrigger +=1U; } } } /* Call Half Transfer Complete callback */ if(RecBuffTrigger == (AudioInCtx[1].Size/2U)) { if(RecBuffHalf == 0U) { RecBuffHalf = 1; CCA02M2_AUDIO_IN_HalfTransfer_CallBack(1); } } /* Call Transfer Complete callback */ if(RecBuffTrigger == AudioInCtx[1].Size) { /* Reset Application Buffer Trigger */ RecBuffTrigger = 0; RecBuffHalf = 0; /* Call the record update function to get the next buffer to fill and its size (size is ignored) */ CCA02M2_AUDIO_IN_TransferComplete_CallBack(1); } } #endif /* (USE_HAL_DFSDM_REGISTER_CALLBACKS == 1U) */ #ifdef USE_STM32L4XX_NUCLEO /** * @brief Initialize the DFSDM channel MSP. * @param hDfsdmChannel DFSDM Channel handle * @retval None */ static void DFSDM_ChannelMspInit(DFSDM_Channel_HandleTypeDef *hDfsdmChannel) { GPIO_InitTypeDef GPIO_InitStruct; /* Prevent unused argument(s) compilation warning */ UNUSED(hDfsdmChannel); /* Enable DFSDM clock */ AUDIO_DFSDMx_CLK_ENABLE(); /* Enable GPIO clock */ AUDIO_DFSDMx_CKOUT_GPIO_CLK_ENABLE(); AUDIO_DFSDMx_DATIN_MIC1_GPIO_CLK_ENABLE(); AUDIO_DFSDMx_DATIN_MIC2_GPIO_CLK_ENABLE(); /* DFSDM pins configuration: DFSDM_CKOUT, DMIC_DATIN pins ------------------*/ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Pin = AUDIO_DFSDMx_CKOUT_PIN; GPIO_InitStruct.Alternate = AUDIO_DFSDMx_CKOUT_AF; HAL_GPIO_Init(AUDIO_DFSDMx_CKOUT_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC1_PIN; GPIO_InitStruct.Alternate = AUDIO_DFSDMx_DATIN_MIC1_AF; HAL_GPIO_Init(AUDIO_DFSDMx_DATIN_MIC1_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC2_PIN; GPIO_InitStruct.Alternate = AUDIO_DFSDMx_DATIN_MIC2_AF; HAL_GPIO_Init(AUDIO_DFSDMx_DATIN_MIC2_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC3_PIN; GPIO_InitStruct.Alternate = AUDIO_DFSDMx_DATIN_MIC3_AF; HAL_GPIO_Init(AUDIO_DFSDMx_DATIN_MIC3_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC4_PIN; GPIO_InitStruct.Alternate = AUDIO_DFSDMx_DATIN_MIC4_AF; HAL_GPIO_Init(AUDIO_DFSDMx_DATIN_MIC4_GPIO_PORT, &GPIO_InitStruct); } /** * @brief DeInitialize the DFSDM channel MSP. * @param hDfsdmChannel DFSDM Channel handle * @retval None */ static void DFSDM_ChannelMspDeInit(DFSDM_Channel_HandleTypeDef *hDfsdmChannel) { /* Prevent unused argument(s) compilation warning */ UNUSED(hDfsdmChannel); GPIO_InitTypeDef GPIO_InitStruct; /* DFSDM pins configuration: DFSDM_CKOUT, DMIC_DATIN pins ------------------*/ GPIO_InitStruct.Pin = AUDIO_DFSDMx_CKOUT_PIN; HAL_GPIO_DeInit(AUDIO_DFSDMx_CKOUT_GPIO_PORT, GPIO_InitStruct.Pin); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC1_PIN; HAL_GPIO_DeInit(AUDIO_DFSDMx_DATIN_MIC1_GPIO_PORT, GPIO_InitStruct.Pin); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC2_PIN; HAL_GPIO_DeInit(AUDIO_DFSDMx_DATIN_MIC2_GPIO_PORT, GPIO_InitStruct.Pin); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC3_PIN; HAL_GPIO_DeInit(AUDIO_DFSDMx_DATIN_MIC3_GPIO_PORT, GPIO_InitStruct.Pin); GPIO_InitStruct.Pin = AUDIO_DFSDMx_DATIN_MIC4_PIN; HAL_GPIO_DeInit(AUDIO_DFSDMx_DATIN_MIC4_GPIO_PORT, GPIO_InitStruct.Pin); } /** * @brief Initialize the DFSDM filter MSP. * @param hDfsdmFilter DFSDM Filter handle * @retval None */ static void DFSDM_FilterMspInit(DFSDM_Filter_HandleTypeDef *hDfsdmFilter) { /* Prevent unused argument(s) compilation warning */ UNUSED(hDfsdmFilter); uint32_t mic_init[4] = {0}; uint32_t mic_num; int8_t i; DMA_Channel_TypeDef* AUDIO_DFSDMx_DMAx_MIC_STREAM[4] = {AUDIO_DFSDMx_DMAx_MIC1_STREAM, AUDIO_DFSDMx_DMAx_MIC2_STREAM, AUDIO_DFSDMx_DMAx_MIC3_STREAM, AUDIO_DFSDMx_DMAx_MIC4_STREAM}; /* Enable DFSDM clock */ AUDIO_DFSDMx_CLK_ENABLE(); /* Enable the DMA clock */ AUDIO_DFSDMx_DMAx_CLK_ENABLE(); for(i = 0; i < DFSDM_MIC_NUMBER; i++) { if((mic_init[POS_VAL(AUDIO_IN_DIGITAL_MIC1)] != 1U) && ((AudioInCtx[1].Device & AUDIO_IN_DIGITAL_MIC1) == AUDIO_IN_DIGITAL_MIC1) ) { mic_num = POS_VAL(AUDIO_IN_DIGITAL_MIC1); mic_init[mic_num] = 1; } else if((mic_init[POS_VAL(AUDIO_IN_DIGITAL_MIC2)] != 1U) && ((AudioInCtx[1].Device & AUDIO_IN_DIGITAL_MIC2) == AUDIO_IN_DIGITAL_MIC2) ) { mic_num = POS_VAL(AUDIO_IN_DIGITAL_MIC2); mic_init[mic_num] = 1; } else if((mic_init[POS_VAL(AUDIO_IN_DIGITAL_MIC3)] != 1U) && ((AudioInCtx[1].Device & AUDIO_IN_DIGITAL_MIC3) == AUDIO_IN_DIGITAL_MIC3) ) { mic_num = POS_VAL(AUDIO_IN_DIGITAL_MIC3); mic_init[mic_num] = 1; } else if((mic_init[POS_VAL(AUDIO_IN_DIGITAL_MIC4)] != 1U) && ((AudioInCtx[1].Device & AUDIO_IN_DIGITAL_MIC4) == AUDIO_IN_DIGITAL_MIC4) ) { mic_num = POS_VAL(AUDIO_IN_DIGITAL_MIC4); mic_init[mic_num] = 1; } else { break; } /* Configure the hDmaDfsdm[i] handle parameters */ hDmaDfsdm[mic_num].Init.Request = DMA_REQUEST_0; hDmaDfsdm[mic_num].Instance = AUDIO_DFSDMx_DMAx_MIC_STREAM[mic_num]; hDmaDfsdm[mic_num].Init.Direction = DMA_PERIPH_TO_MEMORY; hDmaDfsdm[mic_num].Init.PeriphInc = DMA_PINC_DISABLE; hDmaDfsdm[mic_num].Init.MemInc = DMA_MINC_ENABLE; hDmaDfsdm[mic_num].Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hDmaDfsdm[mic_num].Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hDmaDfsdm[mic_num].Init.Mode = DMA_CIRCULAR; hDmaDfsdm[mic_num].Init.Priority = DMA_PRIORITY_HIGH; hDmaDfsdm[mic_num].State = HAL_DMA_STATE_RESET; /* Associate the DMA handle */ __HAL_LINKDMA(&hAudioInDfsdmFilter[mic_num], hdmaReg, hDmaDfsdm[mic_num]); /* Reset DMA handle state */ __HAL_DMA_RESET_HANDLE_STATE(&hDmaDfsdm[mic_num]); /* Configure the DMA Channel */ (void)HAL_DMA_Init(&hDmaDfsdm[mic_num]); if (hDmaDfsdm[mic_num].Instance == AUDIO_DFSDMx_DMAx_MIC1_STREAM) { /* DMA IRQ Channel configuration */ HAL_NVIC_SetPriority(AUDIO_DFSDMx_DMAx_MIC1_IRQ, CCA02M2_AUDIO_IN_IT_PRIORITY, CCA02M2_AUDIO_IN_IT_PRIORITY); HAL_NVIC_EnableIRQ(AUDIO_DFSDMx_DMAx_MIC1_IRQ); } } } /** * @brief DeInitialize the DFSDM filter MSP. * @param hDfsdmFilter DFSDM Filter handle * @retval None */ static void DFSDM_FilterMspDeInit(DFSDM_Filter_HandleTypeDef *hDfsdmFilter) { /* Prevent unused argument(s) compilation warning */ UNUSED(hDfsdmFilter); int8_t i; /* Configure the DMA Channel */ for(i = 0; i < DFSDM_MIC_NUMBER; i++) { if(hDmaDfsdm[i].Instance != NULL) { (void)HAL_DMA_DeInit(&hDmaDfsdm[i]); } } } #else #ifdef USE_STM32WBXX_NUCLEO /** * @brief AUDIO IN SAI MSP Init * @param None * @retval None */ void SAI_MspInit(SAI_HandleTypeDef *hsai) { static DMA_HandleTypeDef hSaiDma; GPIO_InitTypeDef GPIO_InitStruct; /* Enable the SAI peripheral clock */ AUDIO_IN_SAI_CLK_ENABLE(); /* Enable SAI GPIO clocks */ AUDIO_IN_SAI_SCK_GPIO_CLK_ENABLE(); AUDIO_IN_SAI_SD_GPIO_CLK_ENABLE(); /* SAI pins configuration: SCK and SD pins ------------------------------*/ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Pin = AUDIO_IN_SAI_SCK_PIN; GPIO_InitStruct.Alternate = AUDIO_IN_SAI_SCK_AF; HAL_GPIO_Init(AUDIO_IN_SAI_SCK_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_IN_SAI_SD_PIN ; GPIO_InitStruct.Alternate = AUDIO_IN_SAI_SD_AF; HAL_GPIO_Init(AUDIO_IN_SAI_SD_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_IN_SAI_SD2_PIN ; GPIO_InitStruct.Alternate = AUDIO_IN_SAI_SD2_AF; HAL_GPIO_Init(AUDIO_IN_SAI_SD_GPIO_PORT, &GPIO_InitStruct); /* Enable the DMA clock */ /* DMA controller clock enable */ __HAL_RCC_DMAMUX1_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); /* Configure the hSaiDma handle parameters */ hSaiDma.Instance = DMA1_Channel1; /*tODO DEFINES */ hSaiDma.Init.Request = DMA_REQUEST_SAI1_A; hSaiDma.Init.Direction = DMA_PERIPH_TO_MEMORY; hSaiDma.Init.PeriphInc = DMA_PINC_DISABLE; hSaiDma.Init.MemInc = DMA_MINC_ENABLE; hSaiDma.Init.PeriphDataAlignment = AUDIO_IN_SAI_DMAx_PERIPH_DATA_SIZE; hSaiDma.Init.MemDataAlignment = AUDIO_IN_SAI_DMAx_MEM_DATA_SIZE; hSaiDma.Init.Mode = DMA_CIRCULAR; hSaiDma.Init.Priority = DMA_PRIORITY_HIGH; /* Associate the DMA handle */ __HAL_LINKDMA(hsai, hdmarx, hSaiDma); /* Deinitialize the Stream for new transfer */ (void)HAL_DMA_DeInit(&hSaiDma); /* Configure the DMA Stream */ (void)HAL_DMA_Init(&hSaiDma); /* I2S DMA IRQ Channel configuration */ HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, CCA02M2_AUDIO_IN_IT_PRIORITY, CCA02M2_AUDIO_IN_IT_PRIORITY); HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); } #else static void I2S_MspInit(I2S_HandleTypeDef *hi2s) { UNUSED(hi2s); GPIO_InitTypeDef GPIO_InitStruct; /* Enable the I2S2 peripheral clock */ AUDIO_IN_I2S_CLK_ENABLE(); /* Enable I2S GPIO clocks */ AUDIO_IN_I2S_SCK_GPIO_CLK_ENABLE(); AUDIO_IN_I2S_MOSI_GPIO_CLK_ENABLE(); /* I2S2 pins configuration: SCK and MOSI pins ------------------------------*/ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Pin = AUDIO_IN_I2S_SCK_PIN; GPIO_InitStruct.Alternate = AUDIO_IN_I2S_SCK_AF; HAL_GPIO_Init(AUDIO_IN_I2S_SCK_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = AUDIO_IN_I2S_MOSI_PIN ; GPIO_InitStruct.Alternate = AUDIO_IN_I2S_MOSI_AF; HAL_GPIO_Init(AUDIO_IN_I2S_MOSI_GPIO_PORT, &GPIO_InitStruct); } static void SPI_MspInit(SPI_HandleTypeDef *hspi) { UNUSED(hspi); GPIO_InitTypeDef GPIO_InitStruct; /* Enable GPIO TX/RX clock */ AUDIO_IN_SPI_SCK_GPIO_CLK_ENABLE(); AUDIO_IN_SPI_MISO_GPIO_CLK_ENABLE(); AUDIO_IN_SPI_MOSI_GPIO_CLK_ENABLE(); /* Enable SPI3 clock */ AUDIO_IN_SPI_CLK_ENABLE(); /* Enable DMA1 clock */ AUDIO_IN_SPI_DMAx_CLK_ENABLE(); /*##-2- Configure peripheral GPIO ##########################################*/ /* SPI SCK GPIO pin configuration */ GPIO_InitStruct.Pin = AUDIO_IN_SPI_SCK_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Alternate = AUDIO_IN_SPI_SCK_AF; HAL_GPIO_Init(AUDIO_IN_SPI_SCK_GPIO_PORT, &GPIO_InitStruct); /* SPI MOSI GPIO pin configuration */ GPIO_InitStruct.Pin = AUDIO_IN_SPI_MOSI_PIN; GPIO_InitStruct.Alternate = AUDIO_IN_SPI_MOSI_AF; HAL_GPIO_Init(AUDIO_IN_SPI_MOSI_GPIO_PORT, &GPIO_InitStruct); } /** * @brief Audio Timer Init * @param None * @retval None */ static HAL_StatusTypeDef AUDIO_IN_Timer_Init(void) { HAL_StatusTypeDef ret = HAL_OK; static TIM_SlaveConfigTypeDef sSlaveConfig; static TIM_IC_InitTypeDef sICConfig; static TIM_OC_InitTypeDef sOCConfig; GPIO_InitTypeDef GPIO_InitStruct; /* Enable AUDIO_TIMER clock*/ AUDIO_IN_TIMER_CLK_ENABLE(); AUDIO_IN_TIMER_CHOUT_GPIO_PORT_CLK_ENABLE(); AUDIO_IN_TIMER_CHIN_GPIO_PORT_CLK_ENABLE(); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = AUDIO_IN_TIMER_CHIN_AF; GPIO_InitStruct.Pin = AUDIO_IN_TIMER_CHIN_PIN; HAL_GPIO_Init(AUDIO_IN_TIMER_CHIN_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Alternate = AUDIO_IN_TIMER_CHOUT_AF; GPIO_InitStruct.Pin = AUDIO_IN_TIMER_CHOUT_PIN; HAL_GPIO_Init(AUDIO_IN_TIMER_CHOUT_GPIO_PORT, &GPIO_InitStruct); TimDividerHandle.Instance = AUDIO_IN_TIMER; /* Configure the Input: channel_1 */ sICConfig.ICPolarity = TIM_ICPOLARITY_RISING; sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI; sICConfig.ICPrescaler = TIM_ICPSC_DIV1; sICConfig.ICFilter = 0; if(HAL_TIM_IC_ConfigChannel(&TimDividerHandle, &sICConfig, TIM_CHANNEL_1) != HAL_OK) { ret = HAL_ERROR; } /* Configure TIM1 in Gated Slave mode for the external trigger (Filtered Timer Input 1) */ sSlaveConfig.InputTrigger = TIM_TS_TI1FP1; sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1; if( HAL_TIM_SlaveConfigSynchronization(&TimDividerHandle, &sSlaveConfig) != HAL_OK) { ret = HAL_ERROR; } /* Initialize TIM3 peripheral in PWM mode*/ TimDividerHandle.Init.Period = 1; TimDividerHandle.Init.Prescaler = 0; TimDividerHandle.Init.ClockDivision = 0; TimDividerHandle.Init.CounterMode = TIM_COUNTERMODE_UP; TimDividerHandle.Init.RepetitionCounter = 0; if(HAL_TIM_PWM_Init(&TimDividerHandle) != HAL_OK) { ret = HAL_ERROR; } /* Configure the PWM_channel_1 */ sOCConfig.OCMode = TIM_OCMODE_PWM1; sOCConfig.OCPolarity = TIM_OCPOLARITY_HIGH; sOCConfig.Pulse = 1; if(HAL_TIM_PWM_ConfigChannel(&TimDividerHandle, &sOCConfig, TIM_CHANNEL_2) != HAL_OK) { ret = HAL_ERROR; } return ret; } /** * @brief Audio Timer Start * @param None * @retval None */ static HAL_StatusTypeDef AUDIO_IN_Timer_Start(void) { HAL_StatusTypeDef ret = HAL_OK; if(HAL_TIM_IC_Start(&TimDividerHandle, TIM_CHANNEL_1) != HAL_OK) { ret = HAL_ERROR; } /* Start the Output Compare */ if(HAL_TIM_OC_Start(&TimDividerHandle, TIM_CHANNEL_2) != HAL_OK) { ret = HAL_ERROR; } return ret; } #endif #endif /** * @} */ /** * @} */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/