/* * Copyright (c) 2018, Freescale Semiconductor, Inc. * Copyright 2019-2020 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_pdm.h" /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.pdm" #endif /******************************************************************************* * Definitations ******************************************************************************/ /*! @brief Typedef for pdm rx interrupt handler. */ typedef void (*pdm_isr_t)(PDM_Type *base, pdm_handle_t *pdmHandle); /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief Get the instance number for PDM. * * @param channelMask enabled channel. * @param qualitymode selected quality mode. * @param osr oversample rate. * @param regdiv register divider. */ static status_t PDM_ValidateSrcClockRate(uint32_t channelMask, pdm_df_quality_mode_t qualityMode, uint8_t osr, uint32_t regDiv); /******************************************************************************* * Variables ******************************************************************************/ /* Base pointer array */ static PDM_Type *const s_pdmBases[] = PDM_BASE_PTRS; /*!@brief PDM handle pointer */ static pdm_handle_t *s_pdmHandle[ARRAY_SIZE(s_pdmBases)]; #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Clock name array */ static const clock_ip_name_t s_pdmClock[] = PDM_CLOCKS; #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE /* Clock name array */ static const clock_ip_name_t s_pdmFilterClock[] = PDM_FILTER_CLOCKS; #endif #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ /*! @brief Pointer to tx IRQ handler for each instance. */ static pdm_isr_t s_pdmIsr; /*! @brief callback for hwvad. */ static pdm_hwvad_notification_t s_pdm_hwvad_notification[ARRAY_SIZE(s_pdmBases)]; /******************************************************************************* * Code ******************************************************************************/ uint32_t PDM_GetInstance(PDM_Type *base) { uint32_t instance; /* Find the instance index from base address mappings. */ for (instance = 0; instance < ARRAY_SIZE(s_pdmBases); instance++) { if (s_pdmBases[instance] == base) { break; } } assert(instance < ARRAY_SIZE(s_pdmBases)); return instance; } /*! * brief PDM read fifo. * Note: This function support 16 bit only for IP version that only supports 16bit. * * param base PDM base pointer. * param startChannel start channel number. * param channelNums total enabled channelnums. * param buffer received buffer address. * param size number of samples to read. * param dataWidth sample width. */ void PDM_ReadFifo( PDM_Type *base, uint32_t startChannel, uint32_t channelNums, void *buffer, size_t size, uint32_t dataWidth) { uint32_t i = 0, j = 0U; uint32_t *dataAddr = (uint32_t *)buffer; for (i = 0U; i < size; i++) { for (j = 0; j < channelNums; j++) { #if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH != 2U) *dataAddr = base->DATACH[startChannel + j] >> (dataWidth == 4U ? 0U : 8U); dataAddr = (uint32_t *)((uint32_t)dataAddr + dataWidth); #else *dataAddr = base->DATACH[startChannel + j]; dataAddr = (uint32_t *)((uint32_t)dataAddr + 2U); #endif } } } #if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH == 2U) /*! * brief PDM read data non blocking, only support 16bit data read. * So the actually read data byte size in this function is (size * 2 * channelNums). * param base PDM base pointer. * param startChannel start channel number. * param channelNums total enabled channelnums. * param buffer received buffer address. * param size number of 16bit data to read. */ void PDM_ReadNonBlocking(PDM_Type *base, uint32_t startChannel, uint32_t channelNums, int16_t *buffer, size_t size) { uint32_t i = 0, j = 0U; for (i = 0U; i < size; i++) { for (j = 0; j < channelNums; j++) { *buffer++ = (int16_t)base->DATACH[startChannel + j]; } } } #endif static status_t PDM_ValidateSrcClockRate(uint32_t channelMask, pdm_df_quality_mode_t qualityMode, uint8_t osr, uint32_t regDiv) { uint32_t enabledChannel = 0U, i = 0U, factor = 0U, k = 0U; for (i = 0U; i < (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM; i++) { if (channelMask >> i != 0U) { enabledChannel++; } } switch (qualityMode) { case kPDM_QualityModeMedium: factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR; k = 2U; break; case kPDM_QualityModeHigh: factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR; k = 1U; break; case kPDM_QualityModeLow: factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR; k = 4U; break; case kPDM_QualityModeVeryLow0: factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR; k = 2U; break; case kPDM_QualityModeVeryLow1: factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR; k = 4U; break; case kPDM_QualityModeVeryLow2: factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR; k = 8U; break; default: assert(false); break; } /* validate the minimum clock divider */ if (((regDiv * k) / 2U) < (((10U + factor * enabledChannel) / (8U * osr)) * k / 2U)) { return kStatus_Fail; } return kStatus_Success; } /*! * brief PDM set sample rate. * * note This function is depend on the configuration of the PDM and PDM channel, so the correct call sequence is * code * PDM_Init(base, pdmConfig) * PDM_SetChannelConfig(base, channel, &channelConfig) * PDM_SetSampleRateConfig(base, source, sampleRate) * endcode * param base PDM base pointer * param sourceClock_HZ PDM source clock frequency. * param sampleRate_HZ PDM sample rate. */ status_t PDM_SetSampleRateConfig(PDM_Type *base, uint32_t sourceClock_HZ, uint32_t sampleRate_HZ) { uint32_t osr = (base->CTRL_2 & PDM_CTRL_2_CICOSR_MASK) >> PDM_CTRL_2_CICOSR_SHIFT; pdm_df_quality_mode_t qualityMode = (pdm_df_quality_mode_t)(uint32_t)((base->CTRL_2 & PDM_CTRL_2_QSEL_MASK) >> PDM_CTRL_2_QSEL_SHIFT); uint32_t pdmClockRate = 0U; uint32_t enabledChannelMask = base->CTRL_1 & (uint32_t)kPDM_EnableChannelAll, regDiv = 0U; /* get divider */ osr = 16U - osr; pdmClockRate = sampleRate_HZ * osr * 8U; regDiv = sourceClock_HZ / pdmClockRate; if (regDiv > PDM_CTRL_2_CLKDIV_MASK) { return kStatus_Fail; } if (PDM_ValidateSrcClockRate(enabledChannelMask, qualityMode, (uint8_t)osr, regDiv) == kStatus_Fail) { return kStatus_Fail; } base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv); return kStatus_Success; } /*! * brief PDM set sample rate. * * deprecated Do not use this function. It has been superceded by @ref PDM_SetSampleRateConfig * param base PDM base pointer * param enableChannelMask PDM channel enable mask. * param qualityMode quality mode. * param osr cic oversample rate * param clkDiv clock divider */ status_t PDM_SetSampleRate( PDM_Type *base, uint32_t enableChannelMask, pdm_df_quality_mode_t qualityMode, uint8_t osr, uint32_t clkDiv) { uint8_t realOsr = 16U - (osr & (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT)); uint32_t regDiv = clkDiv >> 1U; switch (qualityMode) { case kPDM_QualityModeHigh: regDiv <<= 1U; break; case kPDM_QualityModeLow: case kPDM_QualityModeVeryLow1: regDiv >>= 1U; break; case kPDM_QualityModeVeryLow2: regDiv >>= 2U; break; default: assert(false); break; } if (PDM_ValidateSrcClockRate(enableChannelMask, qualityMode, realOsr, regDiv) == kStatus_Fail) { return kStatus_Fail; } assert(regDiv <= PDM_CTRL_2_CLKDIV_MASK); base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv); return kStatus_Success; } /*! * brief Initializes the PDM peripheral. * * Ungates the PDM clock, resets the module, and configures PDM with a configuration structure. * The configuration structure can be custom filled or set with default values by * PDM_GetDefaultConfig(). * * note This API should be called at the beginning of the application to use * the PDM driver. Otherwise, accessing the PDM module can cause a hard fault * because the clock is not enabled. * * param base PDM base pointer * param config PDM configuration structure. */ void PDM_Init(PDM_Type *base, const pdm_config_t *config) { assert(config != NULL); assert(config->fifoWatermark <= PDM_FIFO_CTRL_FIFOWMK_MASK); #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Enable the PDM clock */ CLOCK_EnableClock(s_pdmClock[PDM_GetInstance(base)]); #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE CLOCK_EnableClock(s_pdmFilterClock[PDM_GetInstance(base)]); #endif #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ /* Enable the module and disable the interface/all channel */ base->CTRL_1 &= ~(PDM_CTRL_1_MDIS_MASK | PDM_CTRL_1_PDMIEN_MASK | PDM_CTRL_1_ERREN_MASK | (uint32_t)kPDM_EnableChannelAll); /* wait all filter stopped */ while ((base->STAT & PDM_STAT_BSY_FIL_MASK) != 0U) { } /* software reset */ base->CTRL_1 |= PDM_CTRL_1_SRES_MASK; /* Set the configure settings */ base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DOZEN_MASK)) | PDM_CTRL_1_DOZEN(config->enableDoze); base->CTRL_2 = (base->CTRL_2 & (~(PDM_CTRL_2_CICOSR_MASK | PDM_CTRL_2_QSEL_MASK))) | PDM_CTRL_2_CICOSR(config->cicOverSampleRate) | PDM_CTRL_2_QSEL(config->qualityMode); /* Set the watermark */ base->FIFO_CTRL = PDM_FIFO_CTRL_FIFOWMK(config->fifoWatermark); } /*! * brief De-initializes the PDM peripheral. * * This API gates the PDM clock. The PDM module can't operate unless PDM_Init * is called to enable the clock. * * param base PDM base pointer */ void PDM_Deinit(PDM_Type *base) { /* disable PDM interface */ PDM_Enable(base, false); #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) CLOCK_DisableClock(s_pdmClock[PDM_GetInstance(base)]); #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE CLOCK_DisableClock(s_pdmFilterClock[PDM_GetInstance(base)]); #endif #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } /*! * brief Enables the PDM interrupt requests. * * param base PDM base pointer * param mask interrupt source * The parameter can be a combination of the following sources if defined. * arg kPDM_ErrorInterruptEnable * arg kPDM_FIFOInterruptEnable */ void PDM_EnableInterrupts(PDM_Type *base, uint32_t mask) { if ((mask & (uint32_t)kPDM_FIFOInterruptEnable) != 0U) { base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DISEL_MASK)) | (uint32_t)kPDM_FIFOInterruptEnable; } if ((mask & (uint32_t)kPDM_ErrorInterruptEnable) != 0U) { base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_ERREN_MASK)) | (uint32_t)kPDM_ErrorInterruptEnable; } } /*! * brief PDM one channel configurations. * * param base PDM base pointer * param config PDM channel configurations. * param channel channel number. * after completing the current frame in debug mode. */ void PDM_SetChannelConfig(PDM_Type *base, uint32_t channel, const pdm_channel_config_t *config) { assert(config != NULL); assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM); uint32_t dcCtrl = 0U; #if (defined(FSL_FEATURE_PDM_HAS_DC_OUT_CTRL) && (FSL_FEATURE_PDM_HAS_DC_OUT_CTRL)) dcCtrl = base->DC_OUT_CTRL; /* configure gain and cut off freq */ dcCtrl &= ~((uint32_t)PDM_DC_OUT_CTRL_DCCONFIG0_MASK << (channel << 1U)); dcCtrl |= (uint32_t)config->outputCutOffFreq << (channel << 1U); base->DC_OUT_CTRL = dcCtrl; #endif #if !(defined(FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED) && (FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED)) dcCtrl = base->DC_CTRL; /* configure gain and cut off freq */ dcCtrl &= ~((uint32_t)PDM_DC_CTRL_DCCONFIG0_MASK << (channel << 1U)); dcCtrl |= (uint32_t)config->cutOffFreq << (channel << 1U); base->DC_CTRL = dcCtrl; #endif PDM_SetChannelGain(base, channel, config->gain); /* enable channel */ base->CTRL_1 |= 1UL << channel; } /*! * brief Set the PDM channel gain. * * Please note for different quality mode, the valid gain value is different, reference RM for detail. * param base PDM base pointer. * param channel PDM channel index. * param gain channel gain, the register gain value range is 0 - 15. */ void PDM_SetChannelGain(PDM_Type *base, uint32_t channel, pdm_df_output_gain_t gain) { assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM); #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL uint32_t outCtrl = base->RANGE_CTRL; #else uint32_t outCtrl = base->OUT_CTRL; #endif #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL outCtrl &= ~((uint32_t)PDM_RANGE_CTRL_RANGEADJ0_MASK << (channel << 2U)); #else outCtrl &= ~((uint32_t)PDM_OUT_CTRL_OUTGAIN0_MASK << (channel << 2U)); #endif outCtrl |= (uint32_t)gain << (channel << 2U); #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL base->RANGE_CTRL = outCtrl; #else base->OUT_CTRL = outCtrl; #endif } /*! * brief PDM set channel transfer config. * * param base PDM base pointer. * param handle PDM handle pointer. * param channel PDM channel. * param config channel config. * param format data format. */ status_t PDM_TransferSetChannelConfig( PDM_Type *base, pdm_handle_t *handle, uint32_t channel, const pdm_channel_config_t *config, uint32_t format) { assert(handle != NULL); PDM_SetChannelConfig(base, channel, config); handle->format = format; if (handle->channelNums == 0U) { handle->startChannel = (uint8_t)channel; } handle->channelNums++; if (handle->channelNums > (uint8_t)FSL_FEATURE_PDM_CHANNEL_NUM) { return kStatus_PDM_ChannelConfig_Failed; } return kStatus_Success; } /*! * brief Initializes the PDM handle. * * This function initializes the handle for the PDM transactional APIs. Call * this function once to get the handle initialized. * * param base PDM base pointer. * param handle PDM handle pointer. * param callback Pointer to the user callback function. * param userData User parameter passed to the callback function. */ void PDM_TransferCreateHandle(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_callback_t callback, void *userData) { assert(handle != NULL); /* Zero the handle */ (void)memset(handle, 0, sizeof(*handle)); s_pdmHandle[PDM_GetInstance(base)] = handle; handle->callback = callback; handle->userData = userData; handle->watermark = (uint8_t)(base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK); /* Set the isr pointer */ s_pdmIsr = PDM_TransferHandleIRQ; /* Enable RX event IRQ */ (void)EnableIRQ(PDM_EVENT_IRQn); #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ) /* Enable FIFO error IRQ */ (void)EnableIRQ(PDM_ERROR_IRQn); #endif } /*! * brief Performs an interrupt non-blocking receive transfer on PDM. * * note This API returns immediately after the transfer initiates. * Call the PDM_RxGetTransferStatusIRQ to poll the transfer status and check whether * the transfer is finished. If the return status is not kStatus_PDM_Busy, the transfer * is finished. * * param base PDM base pointer * param handle Pointer to the pdm_handle_t structure which stores the transfer state. * param xfer Pointer to the pdm_transfer_t structure. * retval kStatus_Success Successfully started the data receive. * retval kStatus_PDM_Busy Previous receive still not finished. */ status_t PDM_TransferReceiveNonBlocking(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_t *xfer) { assert(handle != NULL); /* Check if the queue is full */ if (handle->pdmQueue[handle->queueUser].data != NULL) { return kStatus_PDM_QueueFull; } /* Add into queue */ handle->transferSize[handle->queueUser] = xfer->dataSize; handle->pdmQueue[handle->queueUser].data = xfer->data; handle->pdmQueue[handle->queueUser].dataSize = xfer->dataSize; handle->queueUser = (handle->queueUser + 1U) % PDM_XFER_QUEUE_SIZE; /* Set state to busy */ handle->state = kStatus_PDM_Busy; /* Enable interrupt */ PDM_EnableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable); PDM_Enable(base, true); return kStatus_Success; } /*! * brief Aborts the current IRQ receive. * * note This API can be called when an interrupt non-blocking transfer initiates * to abort the transfer early. * * param base PDM base pointer * param handle Pointer to the pdm_handle_t structure which stores the transfer state. */ void PDM_TransferAbortReceive(PDM_Type *base, pdm_handle_t *handle) { assert(handle != NULL); /* Use FIFO request interrupt and fifo error */ PDM_DisableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable | (uint32_t)kPDM_ErrorInterruptEnable); PDM_Enable(base, false); handle->state = kStatus_PDM_Idle; /* Clear the queue */ (void)memset(handle->pdmQueue, 0, sizeof(pdm_transfer_t) * PDM_XFER_QUEUE_SIZE); handle->queueDriver = 0; handle->queueUser = 0; } /*! * brief Tx interrupt handler. * * param base PDM base pointer. * param handle Pointer to the pdm_handle_t structure. */ void PDM_TransferHandleIRQ(PDM_Type *base, pdm_handle_t *handle) { assert(handle != NULL); #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ) uint32_t status = 0U; #if (defined(FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ) && (FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ == 1U)) if (PDM_GetStatus(base) & PDM_STAT_LOWFREQF_MASK) { PDM_ClearStatus(base, PDM_STAT_LOWFREQF_MASK); if (handle->callback != NULL) { (handle->callback)(base, handle, kStatus_PDM_CLK_LOW, handle->userData); } } #endif status = PDM_GetFifoStatus(base); if (status != 0U) { PDM_ClearFIFOStatus(base, status); if (handle->callback != NULL) { (handle->callback)(base, handle, kStatus_PDM_FIFO_ERROR, handle->userData); } } #if !(defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL) status = PDM_GetOutputStatus(base); if (status != 0U) { PDM_ClearOutputStatus(base, status); if (handle->callback != NULL) { (handle->callback)(base, handle, kStatus_PDM_Output_ERROR, handle->userData); } } #endif #endif /* Handle transfer */ if (((base->STAT & 0xFFU) != 0U) && (handle->channelNums != 0U) && ((base->CTRL_1 & PDM_CTRL_1_DISEL_MASK) == (0x2UL << PDM_CTRL_1_DISEL_SHIFT))) { PDM_ClearStatus(base, 0xFFU); /* Judge if the data need to transmit is less than space */ uint8_t size = (uint8_t)MIN((handle->pdmQueue[handle->queueDriver].dataSize), ((uint32_t)handle->watermark * handle->channelNums * handle->format)); PDM_ReadFifo(base, handle->startChannel, handle->channelNums, (uint8_t *)(uint32_t)handle->pdmQueue[handle->queueDriver].data, ((size_t)size / handle->channelNums / handle->format), handle->format); /* Update the internal counter */ handle->pdmQueue[handle->queueDriver].dataSize -= size; handle->pdmQueue[handle->queueDriver].data = &(handle->pdmQueue[handle->queueDriver].data[size]); } /* If finished a block, call the callback function */ if (handle->pdmQueue[handle->queueDriver].dataSize == 0U) { handle->pdmQueue[handle->queueDriver].data = NULL; handle->queueDriver = (handle->queueDriver + 1U) % PDM_XFER_QUEUE_SIZE; if (handle->callback != NULL) { (handle->callback)(base, handle, kStatus_PDM_Idle, handle->userData); } } /* If all data finished, just stop the transfer */ if (handle->pdmQueue[handle->queueDriver].data == NULL) { PDM_TransferAbortReceive(base, handle); } } /*! * brief set HWVAD in envelope based mode . * Recommand configurations, * code * static const pdm_hwvad_config_t hwvadConfig = { * .channel = 0, * .initializeTime = 10U, * .cicOverSampleRate = 0U, * .inputGain = 0U, * .frameTime = 10U, * .cutOffFreq = kPDM_HwvadHpfBypassed, * .enableFrameEnergy = false, * .enablePreFilter = true, }; * static const pdm_hwvad_noise_filter_t noiseFilterConfig = { * .enableAutoNoiseFilter = false, * .enableNoiseMin = true, * .enableNoiseDecimation = true, * .noiseFilterAdjustment = 0U, * .noiseGain = 7U, * .enableNoiseDetectOR = true, * }; * code * param base PDM base pointer. * param hwvadConfig internal filter status. * param noiseConfig Voice activity detector noise filter configure structure pointer. * param zcdConfig Voice activity detector zero cross detector configure structure pointer . * param signalGain signal gain value. */ void PDM_SetHwvadInEnvelopeBasedMode(PDM_Type *base, const pdm_hwvad_config_t *hwvadConfig, const pdm_hwvad_noise_filter_t *noiseConfig, const pdm_hwvad_zero_cross_detector_t *zcdConfig, uint32_t signalGain) { assert(hwvadConfig != NULL); assert(noiseConfig != NULL); uint32_t i = 0U; PDM_SetHwvadConfig(base, hwvadConfig); PDM_SetHwvadSignalFilterConfig(base, true, signalGain); PDM_SetHwvadNoiseFilterConfig(base, noiseConfig); PDM_EnableHwvad(base, true); if (NULL != zcdConfig) { PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig); } PDM_Enable(base, true); while (PDM_GetHwvadInitialFlag(base) != 0U) { } for (i = 0; i < 3U; i++) { /* set HWVAD interal filter stauts initial */ PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterInitial); } PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterNormalOperation); } /*! * brief set HWVAD in energy based mode . * Recommand configurations, * code * static const pdm_hwvad_config_t hwvadConfig = { * .channel = 0, * .initializeTime = 10U, * .cicOverSampleRate = 0U, * .inputGain = 0U, * .frameTime = 10U, * .cutOffFreq = kPDM_HwvadHpfBypassed, * .enableFrameEnergy = true, * .enablePreFilter = true, }; * static const pdm_hwvad_noise_filter_t noiseFilterConfig = { * .enableAutoNoiseFilter = true, * .enableNoiseMin = false, * .enableNoiseDecimation = false, * .noiseFilterAdjustment = 0U, * .noiseGain = 7U, * .enableNoiseDetectOR = false, * }; * code * param base PDM base pointer. * param hwvadConfig internal filter status. * param noiseConfig Voice activity detector noise filter configure structure pointer. * param zcdConfig Voice activity detector zero cross detector configure structure pointer . * param signalGain signal gain value, signal gain value should be properly according to application. */ void PDM_SetHwvadInEnergyBasedMode(PDM_Type *base, const pdm_hwvad_config_t *hwvadConfig, const pdm_hwvad_noise_filter_t *noiseConfig, const pdm_hwvad_zero_cross_detector_t *zcdConfig, uint32_t signalGain) { assert(hwvadConfig != NULL); assert(noiseConfig != NULL); PDM_SetHwvadConfig(base, hwvadConfig); /* signal filter need to disable, but signal gain value should be set */ base->VAD0_SCONFIG = PDM_VAD0_SCONFIG_VADSGAIN(signalGain); PDM_SetHwvadNoiseFilterConfig(base, noiseConfig); PDM_EnableHwvad(base, true); if (NULL != zcdConfig) { PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig); } PDM_Enable(base, true); } /*! * brief Configure voice activity detector. * * param base PDM base pointer * param config Voice activity detector configure structure pointer . */ void PDM_SetHwvadConfig(PDM_Type *base, const pdm_hwvad_config_t *config) { assert(config != NULL); uint32_t ctrl1 = base->VAD0_CTRL_1; /* Configure VAD0_CTRL_1 register */ ctrl1 &= ~(PDM_VAD0_CTRL_1_VADCHSEL_MASK | PDM_VAD0_CTRL_1_VADCICOSR_MASK | PDM_VAD0_CTRL_1_VADINITT_MASK); ctrl1 |= (PDM_VAD0_CTRL_1_VADCHSEL(config->channel) | PDM_VAD0_CTRL_1_VADCICOSR(config->cicOverSampleRate) | PDM_VAD0_CTRL_1_VADINITT(config->initializeTime)); base->VAD0_CTRL_1 = ctrl1; /* Configure VAD0_CTRL_2 register */ base->VAD0_CTRL_2 = (PDM_VAD0_CTRL_2_VADFRENDIS((config->enableFrameEnergy == true) ? 0U : 1U) | PDM_VAD0_CTRL_2_VADPREFEN(config->enablePreFilter) | PDM_VAD0_CTRL_2_VADFRAMET(config->frameTime) | PDM_VAD0_CTRL_2_VADINPGAIN(config->inputGain) | PDM_VAD0_CTRL_2_VADHPF(config->cutOffFreq)); } /*! * brief Configure voice activity detector signal filter. * * param base PDM base pointer * param enableMaxBlock If signal maximum block enabled. * param signalGain Gain value for the signal energy. */ void PDM_SetHwvadSignalFilterConfig(PDM_Type *base, bool enableMaxBlock, uint32_t signalGain) { uint32_t signalConfig = base->VAD0_SCONFIG; signalConfig &= ~(PDM_VAD0_SCONFIG_VADSMAXEN_MASK | PDM_VAD0_SCONFIG_VADSGAIN_MASK); signalConfig |= (PDM_VAD0_SCONFIG_VADSMAXEN(enableMaxBlock) | PDM_VAD0_SCONFIG_VADSGAIN(signalGain)) | PDM_VAD0_SCONFIG_VADSFILEN_MASK; base->VAD0_SCONFIG = signalConfig; } /*! * brief Configure voice activity detector noise filter. * * param base PDM base pointer * param config Voice activity detector noise filter configure structure pointer . */ void PDM_SetHwvadNoiseFilterConfig(PDM_Type *base, const pdm_hwvad_noise_filter_t *config) { assert(config != NULL); base->VAD0_NCONFIG = (PDM_VAD0_NCONFIG_VADNFILAUTO(config->enableAutoNoiseFilter) | PDM_VAD0_NCONFIG_VADNOREN(config->enableNoiseDetectOR) | PDM_VAD0_NCONFIG_VADNMINEN(config->enableNoiseMin) | PDM_VAD0_NCONFIG_VADNDECEN(config->enableNoiseDecimation) | PDM_VAD0_NCONFIG_VADNFILADJ(config->noiseFilterAdjustment) | PDM_VAD0_NCONFIG_VADNGAIN(config->noiseGain)); } /*! * brief Configure voice activity detector zero cross detector. * * param base PDM base pointer * param config Voice activity detector zero cross detector configure structure pointer . */ void PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type *base, const pdm_hwvad_zero_cross_detector_t *config) { assert(config != NULL); uint32_t zcd = (base->VAD0_ZCD & (~(PDM_VAD0_ZCD_VADZCDTH_MASK | PDM_VAD0_ZCD_VADZCDADJ_MASK | PDM_VAD0_ZCD_VADZCDAUTO_MASK | PDM_VAD0_ZCD_VADZCDAND_MASK))); zcd |= (PDM_VAD0_ZCD_VADZCDTH(config->threshold) | PDM_VAD0_ZCD_VADZCDADJ(config->adjustmentThreshold) | PDM_VAD0_ZCD_VADZCDAUTO(config->enableAutoThreshold) | PDM_VAD0_ZCD_VADZCDAND(config->zcdAnd)) | PDM_VAD0_ZCD_VADZCDEN_MASK; base->VAD0_ZCD = zcd; } /*! * brief Enable/Disable hwvad callback. * This function enable/disable the hwvad interrupt for the selected PDM peripheral. * * param base Base address of the PDM peripheral. * param vadCallback callback Pointer to store callback function, should be NULL when disable. * param userData user data. * param enable true is enable, false is disable. * retval None. */ void PDM_EnableHwvadInterruptCallback(PDM_Type *base, pdm_hwvad_callback_t vadCallback, void *userData, bool enable) { uint32_t instance = PDM_GetInstance(base); if (enable) { PDM_EnableHwvadInterrupts(base, (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable); NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn); (void)EnableIRQ(PDM_HWVAD_EVENT_IRQn); #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ) NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn); (void)EnableIRQ(PDM_HWVAD_ERROR_IRQn); #endif s_pdm_hwvad_notification[instance].callback = vadCallback; s_pdm_hwvad_notification[instance].userData = userData; } else { PDM_DisableHwvadInterrupts(base, (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable); (void)DisableIRQ(PDM_HWVAD_EVENT_IRQn); #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ) (void)DisableIRQ(PDM_HWVAD_ERROR_IRQn); NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn); #endif s_pdm_hwvad_notification[instance].callback = NULL; s_pdm_hwvad_notification[instance].userData = NULL; NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn); } } #if defined(PDM) void PDM_EVENT_DriverIRQHandler(void); void PDM_EVENT_DriverIRQHandler(void) { assert(s_pdmHandle[0] != NULL); s_pdmIsr(PDM, s_pdmHandle[0]); SDK_ISR_EXIT_BARRIER; } #endif #if (defined PDM) void PDM_HWVAD_EVENT_DriverIRQHandler(void); void PDM_HWVAD_EVENT_DriverIRQHandler(void) { if ((PDM_GetHwvadInterruptStatusFlags(PDM) & (uint32_t)kPDM_HwvadStatusVoiceDetectFlag) != 0U) { PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusVoiceDetectFlag); if (s_pdm_hwvad_notification[0].callback != NULL) { s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_VoiceDetected, s_pdm_hwvad_notification[0].userData); } } #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ) else { PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation); if (s_pdm_hwvad_notification[0].callback != NULL) { s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData); } } #endif SDK_ISR_EXIT_BARRIER; } #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ) void PDM_HWVAD_ERROR_DriverIRQHandler(void); void PDM_HWVAD_ERROR_DriverIRQHandler(void) { PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation); if (s_pdm_hwvad_notification[0].callback != NULL) { s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData); } SDK_ISR_EXIT_BARRIER; } #endif #endif