diff options
Diffstat (limited to 'bsps/arm/imxrt/mcux-sdk/drivers/pdm')
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.c | 963 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.h | 1213 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.c | 459 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.h | 254 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_sdma.h | 140 |
5 files changed, 3029 insertions, 0 deletions
diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.c b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.c new file mode 100644 index 0000000000..bfdc89384c --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.c @@ -0,0 +1,963 @@ +/* + * 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 + ******************************************************************************/ +#if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV) +/*! + * @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); +#endif + +/******************************************************************************* + * 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; +#if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD) +/*! @brief callback for hwvad. */ +static pdm_hwvad_notification_t s_pdm_hwvad_notification[ARRAY_SIZE(s_pdmBases)]; +#endif +/******************************************************************************* + * 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 + +#if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV) +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) & 0x01U) != 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 */ + /* 2U is for canculating k, 100U is for determing the specific float number of clock divider */ + if (((regDiv * k) / 2U * 100U) < (((10U + factor * enabledChannel) * 100U / (8U * osr)) * k / 2U)) + { + return kStatus_Fail; + } + + return kStatus_Success; +} +#endif + +/*! + * 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; +#if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV) + 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 enabledChannelMask = base->CTRL_1 & (uint32_t)kPDM_EnableChannelAll; +#endif + + uint32_t pdmClockRate = 0U; + uint32_t 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 !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV) + if (PDM_ValidateSrcClockRate(enabledChannelMask, qualityMode, (uint8_t)osr, regDiv) == kStatus_Fail) + { + return kStatus_Fail; + } +#endif + + 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) +{ +#if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV) + uint8_t realOsr = 16U - (osr & (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT)); +#endif + 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 !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV) + if (PDM_ValidateSrcClockRate(enableChannelMask, qualityMode, realOsr, regDiv) == kStatus_Fail) + { + return kStatus_Fail; + } +#endif + + 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); + } +} + +#if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD) +/*! + * 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_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 +#endif + +#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 diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.h b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.h new file mode 100644 index 0000000000..f7c263a188 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm.h @@ -0,0 +1,1213 @@ +/* + * Copyright (c) 2018, Freescale Semiconductor, Inc. + * Copyright 2019-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_PDM_H_ +#define _FSL_PDM_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup pdm_driver PDM Driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +#define FSL_PDM_DRIVER_VERSION (MAKE_VERSION(2, 8, 0)) /*!< Version 2.8.0 */ +/*@}*/ + +/*! @brief PDM XFER QUEUE SIZE */ +#define PDM_XFER_QUEUE_SIZE (4U) + +/*! @brief PDM return status*/ +enum +{ + kStatus_PDM_Busy = MAKE_STATUS(kStatusGroup_PDM, 0), /*!< PDM is busy. */ +#if (defined(FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ) && (FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ == 1U)) + kStatus_PDM_CLK_LOW = MAKE_STATUS(kStatusGroup_PDM, 1), /*!< PDM clock frequency low */ +#endif + kStatus_PDM_FIFO_ERROR = MAKE_STATUS(kStatusGroup_PDM, 2), /*!< PDM FIFO underrun or overflow */ + kStatus_PDM_QueueFull = MAKE_STATUS(kStatusGroup_PDM, 3), /*!< PDM FIFO underrun or overflow */ + kStatus_PDM_Idle = MAKE_STATUS(kStatusGroup_PDM, 4), /*!< PDM is idle */ + kStatus_PDM_Output_ERROR = MAKE_STATUS(kStatusGroup_PDM, 5), /*!< PDM is output error */ + kStatus_PDM_ChannelConfig_Failed = MAKE_STATUS(kStatusGroup_PDM, 6), /*!< PDM channel config failed */ +#if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD) + kStatus_PDM_HWVAD_VoiceDetected = MAKE_STATUS(kStatusGroup_PDM, 7), /*!< PDM hwvad voice detected */ + kStatus_PDM_HWVAD_Error = MAKE_STATUS(kStatusGroup_PDM, 8), /*!< PDM hwvad error */ +#endif +}; + +/*! @brief The PDM interrupt enable flag */ +enum _pdm_interrupt_enable +{ + kPDM_ErrorInterruptEnable = PDM_CTRL_1_ERREN_MASK, /*!< PDM channel error interrupt enable. */ + kPDM_FIFOInterruptEnable = PDM_CTRL_1_DISEL(2U), /*!< PDM channel FIFO interrupt */ +}; + +/*! @brief The PDM status */ +enum _pdm_internal_status +{ + kPDM_StatusDfBusyFlag = (int)PDM_STAT_BSY_FIL_MASK, /*!< Decimation filter is busy processing data */ +#if !(defined(FSL_FEATURE_PDM_HAS_NO_FIR_RDY) && FSL_FEATURE_PDM_HAS_NO_FIR_RDY) + kPDM_StatusFIRFilterReady = PDM_STAT_FIR_RDY_MASK, /*!< FIR filter data is ready */ +#endif +#if (defined(FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ) && (FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ == 1U)) + kPDM_StatusFrequencyLow = PDM_STAT_LOWFREQF_MASK, /*!< Mic app clock frequency not high enough */ +#endif + kPDM_StatusCh0FifoDataAvaliable = PDM_STAT_CH0F_MASK, /*!< channel 0 fifo data reached watermark level */ + kPDM_StatusCh1FifoDataAvaliable = PDM_STAT_CH1F_MASK, /*!< channel 1 fifo data reached watermark level */ + kPDM_StatusCh2FifoDataAvaliable = PDM_STAT_CH2F_MASK, /*!< channel 2 fifo data reached watermark level */ + kPDM_StatusCh3FifoDataAvaliable = PDM_STAT_CH3F_MASK, /*!< channel 3 fifo data reached watermark level */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_StatusCh4FifoDataAvaliable = PDM_STAT_CH4F_MASK, /*!< channel 4 fifo data reached watermark level */ + kPDM_StatusCh5FifoDataAvaliable = PDM_STAT_CH5F_MASK, /*!< channel 5 fifo data reached watermark level */ + kPDM_StatusCh6FifoDataAvaliable = PDM_STAT_CH6F_MASK, /*!< channel 6 fifo data reached watermark level */ + kPDM_StatusCh7FifoDataAvaliable = PDM_STAT_CH7F_MASK, /*!< channel 7 fifo data reached watermark level */ +#endif +}; + +/*! @brief PDM channel enable mask */ +enum _pdm_channel_enable_mask +{ + kPDM_EnableChannel0 = PDM_STAT_CH0F_MASK, /*!< channgel 0 enable mask */ + kPDM_EnableChannel1 = PDM_STAT_CH1F_MASK, /*!< channgel 1 enable mask */ + kPDM_EnableChannel2 = PDM_STAT_CH2F_MASK, /*!< channgel 2 enable mask */ + kPDM_EnableChannel3 = PDM_STAT_CH3F_MASK, /*!< channgel 3 enable mask */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_EnableChannel4 = PDM_STAT_CH4F_MASK, /*!< channgel 4 enable mask */ + kPDM_EnableChannel5 = PDM_STAT_CH5F_MASK, /*!< channgel 5 enable mask */ + kPDM_EnableChannel6 = PDM_STAT_CH6F_MASK, /*!< channgel 6 enable mask */ + kPDM_EnableChannel7 = PDM_STAT_CH7F_MASK, /*!< channgel 7 enable mask */ + + kPDM_EnableChannelAll = kPDM_EnableChannel0 | kPDM_EnableChannel1 | kPDM_EnableChannel2 | kPDM_EnableChannel3 | + kPDM_EnableChannel4 | kPDM_EnableChannel5 | kPDM_EnableChannel6 | kPDM_EnableChannel7, +#else + kPDM_EnableChannelAll = kPDM_EnableChannel0 | kPDM_EnableChannel1 | kPDM_EnableChannel2 | kPDM_EnableChannel3, +#endif +}; + +/*! @brief The PDM fifo status */ +enum _pdm_fifo_status +{ + kPDM_FifoStatusUnderflowCh0 = PDM_FIFO_STAT_FIFOUND0_MASK, /*!< channel0 fifo status underflow */ + kPDM_FifoStatusUnderflowCh1 = PDM_FIFO_STAT_FIFOUND1_MASK, /*!< channel1 fifo status underflow */ + kPDM_FifoStatusUnderflowCh2 = PDM_FIFO_STAT_FIFOUND2_MASK, /*!< channel2 fifo status underflow */ + kPDM_FifoStatusUnderflowCh3 = PDM_FIFO_STAT_FIFOUND3_MASK, /*!< channel3 fifo status underflow */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_FifoStatusUnderflowCh4 = PDM_FIFO_STAT_FIFOUND4_MASK, /*!< channel4 fifo status underflow */ + kPDM_FifoStatusUnderflowCh5 = PDM_FIFO_STAT_FIFOUND5_MASK, /*!< channel5 fifo status underflow */ + kPDM_FifoStatusUnderflowCh6 = PDM_FIFO_STAT_FIFOUND6_MASK, /*!< channel6 fifo status underflow */ + kPDM_FifoStatusUnderflowCh7 = PDM_FIFO_STAT_FIFOUND6_MASK, /*!< channel7 fifo status underflow */ +#endif + + kPDM_FifoStatusOverflowCh0 = PDM_FIFO_STAT_FIFOOVF0_MASK, /*!< channel0 fifo status overflow */ + kPDM_FifoStatusOverflowCh1 = PDM_FIFO_STAT_FIFOOVF1_MASK, /*!< channel1 fifo status overflow */ + kPDM_FifoStatusOverflowCh2 = PDM_FIFO_STAT_FIFOOVF2_MASK, /*!< channel2 fifo status overflow */ + kPDM_FifoStatusOverflowCh3 = PDM_FIFO_STAT_FIFOOVF3_MASK, /*!< channel3 fifo status overflow */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_FifoStatusOverflowCh4 = PDM_FIFO_STAT_FIFOOVF4_MASK, /*!< channel4 fifo status overflow */ + kPDM_FifoStatusOverflowCh5 = PDM_FIFO_STAT_FIFOOVF5_MASK, /*!< channel5 fifo status overflow */ + kPDM_FifoStatusOverflowCh6 = PDM_FIFO_STAT_FIFOOVF6_MASK, /*!< channel6 fifo status overflow */ + kPDM_FifoStatusOverflowCh7 = PDM_FIFO_STAT_FIFOOVF7_MASK, /*!< channel7 fifo status overflow */ +#endif +}; + +#if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL +/*! @brief The PDM output status */ +enum _pdm_range_status +{ + kPDM_RangeStatusUnderFlowCh0 = PDM_RANGE_STAT_RANGEUNF0_MASK, /*!< channel0 range status underflow */ + kPDM_RangeStatusUnderFlowCh1 = PDM_RANGE_STAT_RANGEUNF1_MASK, /*!< channel1 range status underflow */ + kPDM_RangeStatusUnderFlowCh2 = PDM_RANGE_STAT_RANGEUNF2_MASK, /*!< channel2 range status underflow */ + kPDM_RangeStatusUnderFlowCh3 = PDM_RANGE_STAT_RANGEUNF3_MASK, /*!< channel3 range status underflow */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_RangeStatusUnderFlowCh4 = PDM_RANGE_STAT_RANGEUNF4_MASK, /*!< channel4 range status underflow */ + kPDM_RangeStatusUnderFlowCh5 = PDM_RANGE_STAT_RANGEUNF5_MASK, /*!< channel5 range status underflow */ + kPDM_RangeStatusUnderFlowCh6 = PDM_RANGE_STAT_RANGEUNF6_MASK, /*!< channel6 range status underflow */ + kPDM_RangeStatusUnderFlowCh7 = PDM_RANGE_STAT_RANGEUNF7_MASK, /*!< channel7 range status underflow */ +#endif + kPDM_RangeStatusOverFlowCh0 = PDM_RANGE_STAT_RANGEOVF0_MASK, /*!< channel0 range status overflow */ + kPDM_RangeStatusOverFlowCh1 = PDM_RANGE_STAT_RANGEOVF1_MASK, /*!< channel1 range status overflow */ + kPDM_RangeStatusOverFlowCh2 = PDM_RANGE_STAT_RANGEOVF2_MASK, /*!< channel2 range status overflow */ + kPDM_RangeStatusOverFlowCh3 = PDM_RANGE_STAT_RANGEOVF3_MASK, /*!< channel3 range status overflow */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_RangeStatusOverFlowCh4 = PDM_RANGE_STAT_RANGEOVF4_MASK, /*!< channel4 range status overflow */ + kPDM_RangeStatusOverFlowCh5 = PDM_RANGE_STAT_RANGEOVF5_MASK, /*!< channel5 range status overflow */ + kPDM_RangeStatusOverFlowCh6 = PDM_RANGE_STAT_RANGEOVF6_MASK, /*!< channel6 range status overflow */ + kPDM_RangeStatusOverFlowCh7 = PDM_RANGE_STAT_RANGEOVF7_MASK, /*!< channel7 range status overflow */ +#endif +}; +#else +/*! @brief The PDM output status */ +enum _pdm_output_status +{ + kPDM_OutputStatusUnderFlowCh0 = PDM_OUT_STAT_OUTUNF0_MASK, /*!< channel0 output status underflow */ + kPDM_OutputStatusUnderFlowCh1 = PDM_OUT_STAT_OUTUNF1_MASK, /*!< channel1 output status underflow */ + kPDM_OutputStatusUnderFlowCh2 = PDM_OUT_STAT_OUTUNF2_MASK, /*!< channel2 output status underflow */ + kPDM_OutputStatusUnderFlowCh3 = PDM_OUT_STAT_OUTUNF3_MASK, /*!< channel3 output status underflow */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_OutputStatusUnderFlowCh4 = PDM_OUT_STAT_OUTUNF4_MASK, /*!< channel4 output status underflow */ + kPDM_OutputStatusUnderFlowCh5 = PDM_OUT_STAT_OUTUNF5_MASK, /*!< channel5 output status underflow */ + kPDM_OutputStatusUnderFlowCh6 = PDM_OUT_STAT_OUTUNF6_MASK, /*!< channel6 output status underflow */ + kPDM_OutputStatusUnderFlowCh7 = PDM_OUT_STAT_OUTUNF7_MASK, /*!< channel7 output status underflow */ +#endif + kPDM_OutputStatusOverFlowCh0 = PDM_OUT_STAT_OUTOVF0_MASK, /*!< channel0 output status overflow */ + kPDM_OutputStatusOverFlowCh1 = PDM_OUT_STAT_OUTOVF1_MASK, /*!< channel1 output status overflow */ + kPDM_OutputStatusOverFlowCh2 = PDM_OUT_STAT_OUTOVF2_MASK, /*!< channel2 output status overflow */ + kPDM_OutputStatusOverFlowCh3 = PDM_OUT_STAT_OUTOVF3_MASK, /*!< channel3 output status overflow */ +#if !defined(FSL_FEATURE_PDM_CHANNEL_NUM) || (FSL_FEATURE_PDM_CHANNEL_NUM == 8U) + kPDM_OutputStatusOverFlowCh4 = PDM_OUT_STAT_OUTOVF4_MASK, /*!< channel4 output status overflow */ + kPDM_OutputStatusOverFlowCh5 = PDM_OUT_STAT_OUTOVF5_MASK, /*!< channel5 output status overflow */ + kPDM_OutputStatusOverFlowCh6 = PDM_OUT_STAT_OUTOVF6_MASK, /*!< channel6 output status overflow */ + kPDM_OutputStatusOverFlowCh7 = PDM_OUT_STAT_OUTOVF7_MASK, /*!< channel7 output status overflow */ +#endif +}; +#endif + +#if (defined(FSL_FEATURE_PDM_HAS_DC_OUT_CTRL) && (FSL_FEATURE_PDM_HAS_DC_OUT_CTRL)) +/*! @brief PDM DC remover configurations */ +typedef enum _pdm_dc_remover +{ + kPDM_DcRemoverCutOff20Hz = 0U, /*!< DC remover cut off 20HZ */ + kPDM_DcRemoverCutOff13Hz = 1U, /*!< DC remover cut off 13.3HZ */ + kPDM_DcRemoverCutOff40Hz = 2U, /*!< DC remover cut off 40HZ */ + kPDM_DcRemoverBypass = 3U, /*!< DC remover bypass */ +} pdm_dc_remover_t; +#else +/*! @brief PDM DC remover configurations */ +typedef enum _pdm_dc_remover +{ + kPDM_DcRemoverCutOff21Hz = 0U, /*!< DC remover cut off 21HZ */ + kPDM_DcRemoverCutOff83Hz = 1U, /*!< DC remover cut off 83HZ */ + kPDM_DcRemoverCutOff152Hz = 2U, /*!< DC remover cut off 152HZ */ + kPDM_DcRemoverBypass = 3U, /*!< DC remover bypass */ +} pdm_dc_remover_t; +#endif + +/*! @brief PDM decimation filter quality mode */ +typedef enum _pdm_df_quality_mode +{ + kPDM_QualityModeMedium = 0U, /*!< quality mode memdium */ + kPDM_QualityModeHigh = 1U, /*!< quality mode high */ + kPDM_QualityModeLow = 7U, /*!< quality mode low */ + kPDM_QualityModeVeryLow0 = 6U, /*!< quality mode very low0 */ + kPDM_QualityModeVeryLow1 = 5U, /*!< quality mode very low1 */ + kPDM_QualityModeVeryLow2 = 4U, /*!< quality mode very low2 */ +} pdm_df_quality_mode_t; + +/*! @brief PDM quality mode K factor */ +enum _pdm_qulaity_mode_k_factor +{ + kPDM_QualityModeHighKFactor = 1U, /*!< high quality mode K factor = 1 / 2 */ + kPDM_QualityModeMediumKFactor = 2U, /*!< medium/very low0 quality mode K factor = 2 / 2 */ + kPDM_QualityModeLowKFactor = 4U, /*!< low/very low1 quality mode K factor = 4 / 2 */ + kPDM_QualityModeVeryLow2KFactor = 8U, /*!< very low2 quality mode K factor = 8 / 2 */ +}; + +/*! @brief PDM decimation filter output gain */ +typedef enum _pdm_df_output_gain +{ + kPDM_DfOutputGain0 = 0U, /*!< Decimation filter output gain 0 */ + kPDM_DfOutputGain1 = 1U, /*!< Decimation filter output gain 1 */ + kPDM_DfOutputGain2 = 2U, /*!< Decimation filter output gain 2 */ + kPDM_DfOutputGain3 = 3U, /*!< Decimation filter output gain 3 */ + kPDM_DfOutputGain4 = 4U, /*!< Decimation filter output gain 4 */ + kPDM_DfOutputGain5 = 5U, /*!< Decimation filter output gain 5 */ + kPDM_DfOutputGain6 = 6U, /*!< Decimation filter output gain 6 */ + kPDM_DfOutputGain7 = 7U, /*!< Decimation filter output gain 7 */ + kPDM_DfOutputGain8 = 8U, /*!< Decimation filter output gain 8 */ + kPDM_DfOutputGain9 = 9U, /*!< Decimation filter output gain 9 */ + kPDM_DfOutputGain10 = 0xAU, /*!< Decimation filter output gain 10 */ + kPDM_DfOutputGain11 = 0xBU, /*!< Decimation filter output gain 11 */ + kPDM_DfOutputGain12 = 0xCU, /*!< Decimation filter output gain 12 */ + kPDM_DfOutputGain13 = 0xDU, /*!< Decimation filter output gain 13 */ + kPDM_DfOutputGain14 = 0xEU, /*!< Decimation filter output gain 14 */ + kPDM_DfOutputGain15 = 0xFU, /*!< Decimation filter output gain 15 */ +} pdm_df_output_gain_t; + +/*! @brief PDM data width */ +enum _pdm_data_width +{ +#if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH != 2U) + kPDM_DataWwidth24 = 3U, /*!< PDM data width 24bit */ + kPDM_DataWwidth32 = 4U, /*!< PDM data width 32bit */ +#else + kPDM_DataWdith16 = 2U, /*!< PDM data width 16bit */ +#endif +}; + +/*! @brief PDM channel configurations */ +typedef struct _pdm_channel_config +{ +#if (defined(FSL_FEATURE_PDM_HAS_DC_OUT_CTRL) && (FSL_FEATURE_PDM_HAS_DC_OUT_CTRL)) + pdm_dc_remover_t outputCutOffFreq; /*!< PDM output DC remover cut off frequency */ +#endif + +#if !(defined(FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED) && (FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED)) + pdm_dc_remover_t cutOffFreq; /*!< DC remover cut off frequency */ +#endif + + pdm_df_output_gain_t gain; /*!< Decimation Filter Output Gain */ +} pdm_channel_config_t; + +/*! @brief PDM user configuration structure */ +typedef struct _pdm_config +{ + bool + enableDoze; /*!< This module will enter disable/low leakage mode if DOZEN is active with ipg_doze is asserted */ + uint8_t fifoWatermark; /*!< Watermark value for FIFO */ + pdm_df_quality_mode_t qualityMode; /*!< Quality mode */ + uint8_t cicOverSampleRate; /*!< CIC filter over sampling rate */ +} pdm_config_t; + +#if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD) +/*! @brief PDM voice activity detector interrupt type */ +enum _pdm_hwvad_interrupt_enable +{ + kPDM_HwvadErrorInterruptEnable = PDM_VAD0_CTRL_1_VADERIE_MASK, /*!< PDM channel HWVAD error interrupt enable. */ + kPDM_HwvadInterruptEnable = PDM_VAD0_CTRL_1_VADIE_MASK, /*!< PDM channel HWVAD interrupt */ +}; + +/*! @brief The PDM hwvad interrupt status flag */ +enum _pdm_hwvad_int_status +{ + kPDM_HwvadStatusInputSaturation = PDM_VAD0_STAT_VADINSATF_MASK, /*!< HWVAD saturation condition */ + kPDM_HwvadStatusVoiceDetectFlag = PDM_VAD0_STAT_VADIF_MASK, /*!< HWVAD voice detect interrupt triggered */ +}; + +/*! @brief High pass filter configure cut-off frequency*/ +typedef enum _pdm_hwvad_hpf_config +{ + kPDM_HwvadHpfBypassed = 0x0U, /*!< High-pass filter bypass */ + kPDM_HwvadHpfCutOffFreq1750Hz = 0x1U, /*!< High-pass filter cut off frequency 1750HZ */ + kPDM_HwvadHpfCutOffFreq215Hz = 0x2U, /*!< High-pass filter cut off frequency 215HZ */ + kPDM_HwvadHpfCutOffFreq102Hz = 0x3U, /*!< High-pass filter cut off frequency 102HZ */ +} pdm_hwvad_hpf_config_t; + +/*! @brief HWVAD internal filter status */ +typedef enum _pdm_hwvad_filter_status +{ + kPDM_HwvadInternalFilterNormalOperation = 0U, /*!< internal filter ready for normal operation */ + kPDM_HwvadInternalFilterInitial = PDM_VAD0_CTRL_1_VADST10_MASK, /*!< interla filter are initial */ +} pdm_hwvad_filter_status_t; + +/*! @brief PDM voice activity detector user configuration structure */ +typedef struct _pdm_hwvad_config +{ + uint8_t channel; /*!< Which channel uses voice activity detector */ + uint8_t initializeTime; /*!< Number of frames or samples to initialize voice activity detector. */ + uint8_t cicOverSampleRate; /*!< CIC filter over sampling rate */ + + uint8_t inputGain; /*!< Voice activity detector input gain */ + uint32_t frameTime; /*!< Voice activity frame time */ + pdm_hwvad_hpf_config_t cutOffFreq; /*!< High pass filter cut off frequency */ + bool enableFrameEnergy; /*!< If frame energy enabled, true means enable */ + bool enablePreFilter; /*!< If pre-filter enabled */ +} pdm_hwvad_config_t; + +/*! @brief PDM voice activity detector noise filter user configuration structure */ +typedef struct _pdm_hwvad_noise_filter +{ + bool enableAutoNoiseFilter; /*!< If noise fileter automatically activated, true means enable */ + bool enableNoiseMin; /*!< If Noise minimum block enabled, true means enabled */ + bool enableNoiseDecimation; /*!< If enable noise input decimation */ + bool enableNoiseDetectOR; /*!< Enables a OR logic in the output of minimum noise estimator block */ + uint32_t noiseFilterAdjustment; /*!< The adjustment value of the noise filter */ + uint32_t noiseGain; /*!< Gain value for the noise energy or envelope estimated */ +} pdm_hwvad_noise_filter_t; + +/*! @brief PDM voice activity detector zero cross detector result */ +typedef enum _pdm_hwvad_zcd_result +{ + kPDM_HwvadResultOREnergyBasedDetection = + 0U, /*!< zero cross detector result will be OR with energy based detection */ + kPDM_HwvadResultANDEnergyBasedDetection = + 1U, /*!< zero cross detector result will be AND with energy based detection */ +} pdm_hwvad_zcd_result_t; + +/*! @brief PDM voice activity detector zero cross detector configuration structure */ +typedef struct _pdm_hwvad_zero_cross_detector +{ + bool enableAutoThreshold; /*!< If ZCD auto-threshold enabled, true means enabled. */ + pdm_hwvad_zcd_result_t zcdAnd; /*!< Is ZCD result is AND'ed with energy-based detection, false means OR'ed */ + uint32_t threshold; /*!< The adjustment value of the noise filter */ + uint32_t adjustmentThreshold; /*!< Gain value for the noise energy or envelope estimated */ +} pdm_hwvad_zero_cross_detector_t; +#endif + +/*! @brief PDM SDMA transfer structure */ +typedef struct _pdm_transfer +{ + volatile uint8_t *data; /*!< Data start address to transfer. */ + volatile size_t dataSize; /*!< Total Transfer bytes size. */ +} pdm_transfer_t; + +/*! @brief PDM handle */ +typedef struct _pdm_handle pdm_handle_t; + +/*! @brief PDM transfer callback prototype */ +typedef void (*pdm_transfer_callback_t)(PDM_Type *base, pdm_handle_t *handle, status_t status, void *userData); + +#if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD) +/*! @brief PDM HWVAD callback prototype */ +typedef void (*pdm_hwvad_callback_t)(status_t status, void *userData); +/*! @brief PDM HWVAD notification structure */ +typedef struct _pdm_hwvad_notification +{ + pdm_hwvad_callback_t callback; + void *userData; +} pdm_hwvad_notification_t; +#endif + +/*! @brief PDM handle structure */ +struct _pdm_handle +{ + uint32_t state; /*!< Transfer status */ + pdm_transfer_callback_t callback; /*!< Callback function called at transfer event*/ + void *userData; /*!< Callback parameter passed to callback function*/ + + pdm_transfer_t pdmQueue[PDM_XFER_QUEUE_SIZE]; /*!< Transfer queue storing queued transfer */ + size_t transferSize[PDM_XFER_QUEUE_SIZE]; /*!< Data bytes need to transfer */ + volatile uint8_t queueUser; /*!< Index for user to queue transfer */ + volatile uint8_t queueDriver; /*!< Index for driver to get the transfer data and size */ + + uint32_t format; /*!< data format */ + uint8_t watermark; /*!< Watermark value */ + uint8_t startChannel; /*!< end channel */ + uint8_t channelNums; /*!< Enabled channel number */ +}; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /*_cplusplus*/ + +/*! + * @name Initialization and deinitialization + * @{ + */ + +/*! + * @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); + +/*! + * @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); + +/*! + * @brief Resets the PDM module. + * + * @param base PDM base pointer + */ +static inline void PDM_Reset(PDM_Type *base) +{ + base->CTRL_1 |= PDM_CTRL_1_SRES_MASK; +} + +/*! + * @brief Enables/disables PDM interface. + * + * @param base PDM base pointer + * @param enable True means PDM interface is enabled, false means PDM interface is disabled. + */ +static inline void PDM_Enable(PDM_Type *base, bool enable) +{ + if (enable) + { + base->CTRL_1 |= PDM_CTRL_1_PDMIEN_MASK; + } + else + { + base->CTRL_1 &= ~PDM_CTRL_1_PDMIEN_MASK; + } +} + +/*! + * @brief Enables/disables DOZE. + * + * @param base PDM base pointer + * @param enable True means the module will enter Disable/Low Leakage mode when ipg_doze is asserted, false means the + * module will not enter Disable/Low Leakage mode when ipg_doze is asserted. + */ +static inline void PDM_EnableDoze(PDM_Type *base, bool enable) +{ + if (enable) + { + base->CTRL_1 |= PDM_CTRL_1_DOZEN_MASK; + } + else + { + base->CTRL_1 &= ~PDM_CTRL_1_DOZEN_MASK; + } +} + +/*! + * @brief Enables/disables debug mode for PDM. + * The PDM interface cannot enter debug mode once in Disable/Low Leakage or Low Power mode. + * @param base PDM base pointer + * @param enable True means PDM interface enter debug mode, false means PDM interface in normal mode. + */ +static inline void PDM_EnableDebugMode(PDM_Type *base, bool enable) +{ + if (enable) + { + base->CTRL_1 |= PDM_CTRL_1_DBG_MASK; + } + else + { + base->CTRL_1 &= ~PDM_CTRL_1_DBG_MASK; + } +} + +/*! + * @brief Enables/disables PDM interface in debug mode. + * + * @param base PDM base pointer + * @param enable True means PDM interface is enabled debug mode, false means PDM interface is disabled after + * after completing the current frame in debug mode. + */ +static inline void PDM_EnableInDebugMode(PDM_Type *base, bool enable) +{ + if (enable) + { + base->CTRL_1 |= PDM_CTRL_1_DBGE_MASK; + } + else + { + base->CTRL_1 &= ~PDM_CTRL_1_DBGE_MASK; + } +} + +/*! + * @brief Enables/disables PDM interface disable/Low Leakage mode. + * + * @param base PDM base pointer + * @param enable True means PDM interface is in disable/low leakage mode, False means PDM interface is in normal mode. + */ +static inline void PDM_EnterLowLeakageMode(PDM_Type *base, bool enable) +{ + if (enable) + { + base->CTRL_1 |= PDM_CTRL_1_MDIS_MASK; + } + else + { + base->CTRL_1 &= ~PDM_CTRL_1_MDIS_MASK; + } +} + +/*! + * @brief Enables/disables the PDM channel. + * + * @param base PDM base pointer + * @param channel PDM channel number need to enable or disable. + * @param enable True means enable PDM channel, false means disable. + */ +static inline void PDM_EnableChannel(PDM_Type *base, uint8_t channel, bool enable) +{ + if (enable) + { + base->CTRL_1 |= (1UL << channel); + } + else + { + base->CTRL_1 &= ~(1UL << channel); + } +} + +/*! + * @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); + +/*! + * @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); + +/*! + * @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); + +/*! + * @brief Get the instance number for PDM. + * + * @param base PDM base pointer. + */ +uint32_t PDM_GetInstance(PDM_Type *base); +/*! @} */ + +/*! + * @name Status + * @{ + */ + +/*! + * @brief Gets the PDM internal status flag. + * Use the Status Mask in _pdm_internal_status to get the status value needed + * @param base PDM base pointer + * @return PDM status flag value. + */ +static inline uint32_t PDM_GetStatus(PDM_Type *base) +{ + return base->STAT; +} + +/*! + * @brief Gets the PDM FIFO status flag. + * Use the Status Mask in _pdm_fifo_status to get the status value needed + * @param base PDM base pointer + * @return FIFO status. + */ +static inline uint32_t PDM_GetFifoStatus(PDM_Type *base) +{ + return base->FIFO_STAT; +} + +#if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL +/*! + * @brief Gets the PDM Range status flag. + * Use the Status Mask in _pdm_range_status to get the status value needed + * @param base PDM base pointer + * @return output status. + */ +static inline uint32_t PDM_GetRangeStatus(PDM_Type *base) +{ + return base->RANGE_STAT; +} +#else +/*! + * @brief Gets the PDM output status flag. + * Use the Status Mask in _pdm_output_status to get the status value needed + * @param base PDM base pointer + * @return output status. + */ +static inline uint32_t PDM_GetOutputStatus(PDM_Type *base) +{ + return base->OUT_STAT; +} +#endif + +/*! + * @brief Clears the PDM Tx status. + * + * @param base PDM base pointer + * @param mask State mask. It can be a combination of the status between kPDM_StatusFrequencyLow and + * kPDM_StatusCh7FifoDataAvaliable. + */ +static inline void PDM_ClearStatus(PDM_Type *base, uint32_t mask) +{ + base->STAT = mask; +} + +/*! + * @brief Clears the PDM Tx status. + * + * @param base PDM base pointer + * @param mask State mask.It can be a combination of the status in _pdm_fifo_status. + */ +static inline void PDM_ClearFIFOStatus(PDM_Type *base, uint32_t mask) +{ + base->FIFO_STAT = mask; +} + +#if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL +/*! + * @brief Clears the PDM range status. + * + * @param base PDM base pointer + * @param mask State mask. It can be a combination of the status in _pdm_range_status. + */ +static inline void PDM_ClearRangeStatus(PDM_Type *base, uint32_t mask) +{ + base->RANGE_STAT = mask; +} +#else +/*! + * @brief Clears the PDM output status. + * + * @param base PDM base pointer + * @param mask State mask. It can be a combination of the status in _pdm_output_status. + */ +static inline void PDM_ClearOutputStatus(PDM_Type *base, uint32_t mask) +{ + base->OUT_STAT = mask; +} +#endif + +/*! @} */ + +/*! + * @name Interrupts + * @{ + */ + +/*! + * @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); + +/*! + * @brief Disables 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 + */ +static inline void PDM_DisableInterrupts(PDM_Type *base, uint32_t mask) +{ + base->CTRL_1 &= ~mask; +} + +/*! @} */ + +/*! + * @name DMA Control + * @{ + */ + +/*! + * @brief Enables/disables the PDM DMA requests. + * + * @param base PDM base pointer + * @param enable True means enable DMA, false means disable DMA. + */ +static inline void PDM_EnableDMA(PDM_Type *base, bool enable) +{ + if (enable) + { + base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DISEL_MASK)) | PDM_CTRL_1_DISEL(0x1U); + } + else + { + base->CTRL_1 &= ~PDM_CTRL_1_DISEL_MASK; + } +} + +/*! + * @brief Gets the PDM data register address. + * + * This API is used to provide a transfer address for the PDM DMA transfer configuration. + * + * @param base PDM base pointer. + * @param channel Which data channel used. + * @return data register address. + */ +static inline uint32_t PDM_GetDataRegisterAddress(PDM_Type *base, uint32_t channel) +{ + return (uint32_t)(&(base->DATACH)[channel]); +} + +/*! @} */ + +/*! + * @name Bus Operations + * @{ + */ +#if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH == 2U) +/*! + * @brief Reads data from the PDM FIFO. + * + * @param base PDM base pointer. + * @param channel Data channel used. + * @return Data in PDM FIFO. + */ +static inline int16_t PDM_ReadData(PDM_Type *base, uint32_t channel) +{ + return (int16_t)(base->DATACH[channel]); +} + +/*! + * @brief PDM read data non blocking. + * 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); +#endif + +/*! + * @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); + +#if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH == 4U) +/*! + * @brief Reads data from the PDM FIFO. + * + * @param base PDM base pointer. + * @param channel Data channel used. + * @return Data in PDM FIFO. + */ +static inline uint32_t PDM_ReadData(PDM_Type *base, uint32_t channel) +{ + return base->DATACH[channel]; +} +#endif + +/*! + * @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); + +#if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD) +/*! @} */ + +/*! + * @name Voice Activity Detector + * @{ + */ + +/*! + * @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); + +/*! + * @brief PDM hwvad force output disable. + * + * @param base PDM base pointer + * @param enable true is output force disable, false is output not force. + */ +static inline void PDM_ForceHwvadOutputDisable(PDM_Type *base, bool enable) +{ + if (enable) + { + base->VAD0_CTRL_2 &= ~PDM_VAD0_CTRL_2_VADFOUTDIS_MASK; + } + else + { + base->VAD0_CTRL_2 |= PDM_VAD0_CTRL_2_VADFOUTDIS_MASK; + } +} + +/*! + * @brief PDM hwvad reset. + * It will reset VADNDATA register and will clean all internal buffers, should be called when the PDM isn't running. + * + * @param base PDM base pointer + */ +static inline void PDM_ResetHwvad(PDM_Type *base) +{ + base->VAD0_CTRL_1 |= PDM_VAD0_CTRL_1_VADRST_MASK; +} +/*! + * @brief Enable/Disable Voice activity detector. + * Should be called when the PDM isn't running. + * @param base PDM base pointer. + * @param enable True means enable voice activity detector, false means disable. + */ +static inline void PDM_EnableHwvad(PDM_Type *base, bool enable) +{ + if (enable) + { + base->VAD0_CTRL_1 |= PDM_VAD0_CTRL_1_VADEN_MASK; + } + else + { + base->VAD0_CTRL_1 &= ~PDM_VAD0_CTRL_1_VADEN_MASK; + } +} + +/*! + * @brief Enables the PDM Voice Detector 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_HWVADErrorInterruptEnable + * @arg kPDM_HWVADInterruptEnable + */ +static inline void PDM_EnableHwvadInterrupts(PDM_Type *base, uint32_t mask) +{ + base->VAD0_CTRL_1 |= mask; +} + +/*! + * @brief Disables the PDM Voice Detector 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_HWVADErrorInterruptEnable + * @arg kPDM_HWVADInterruptEnable + */ +static inline void PDM_DisableHwvadInterrupts(PDM_Type *base, uint32_t mask) +{ + base->VAD0_CTRL_1 &= ~mask; +} + +/*! + * @brief Clears the PDM voice activity detector status flags. + * + * @param base PDM base pointer + * @param mask State mask,reference _pdm_hwvad_int_status. + */ +static inline void PDM_ClearHwvadInterruptStatusFlags(PDM_Type *base, uint32_t mask) +{ + base->VAD0_STAT = mask; +} + +/*! + * @brief Clears the PDM voice activity detector status flags. + * + * @param base PDM base pointer + * @return status, reference _pdm_hwvad_int_status + */ +static inline uint32_t PDM_GetHwvadInterruptStatusFlags(PDM_Type *base) +{ + return base->VAD0_STAT & (PDM_VAD0_STAT_VADIF_MASK | PDM_VAD0_STAT_VADINSATF_MASK); +} + +/*! + * @brief Get the PDM voice activity detector initial flags. + * + * @param base PDM base pointer + * @return initial flag. + */ +static inline uint32_t PDM_GetHwvadInitialFlag(PDM_Type *base) +{ + return base->VAD0_STAT & PDM_VAD0_STAT_VADINITF_MASK; +} + +#if !(defined(FSL_FEATURE_PDM_HAS_NO_VADEF) && (FSL_FEATURE_PDM_HAS_NO_VADEF)) +/*! + * @brief Get the PDM voice activity detector voice detected flags. + * NOte: this flag is auto cleared when voice gone. + * @param base PDM base pointer + * @return voice detected flag. + */ +static inline uint32_t PDM_GetHwvadVoiceDetectedFlag(PDM_Type *base) +{ + return base->VAD0_STAT & PDM_VAD0_STAT_VADEF_MASK; +} +#endif + +/*! + * @brief Enables/disables voice activity detector signal filter. + * + * @param base PDM base pointer + * @param enable True means enable signal filter, false means disable. + */ +static inline void PDM_EnableHwvadSignalFilter(PDM_Type *base, bool enable) +{ + if (enable) + { + base->VAD0_SCONFIG |= PDM_VAD0_SCONFIG_VADSFILEN_MASK; + } + else + { + base->VAD0_SCONFIG &= ~PDM_VAD0_SCONFIG_VADSFILEN_MASK; + } +} + +/*! + * @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); + +/*! + * @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); + +/*! + * @brief Enables/disables voice activity detector zero cross detector. + * + * @param base PDM base pointer + * @param enable True means enable zero cross detector, false means disable. + */ +static inline void PDM_EnableHwvadZeroCrossDetector(PDM_Type *base, bool enable) +{ + if (enable) + { + base->VAD0_ZCD |= PDM_VAD0_ZCD_VADZCDEN_MASK; + } + else + { + base->VAD0_ZCD &= ~PDM_VAD0_ZCD_VADZCDEN_MASK; + } +} + +/*! + * @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); + +/*! + * @brief Reads noise data. + * + * @param base PDM base pointer. + * @return Data in PDM noise data register. + */ +static inline uint16_t PDM_GetNoiseData(PDM_Type *base) +{ + return (uint16_t)base->VAD0_NDATA; +} + +/*! + * @brief set hwvad internal filter status . + * Note: filter initial status should be asserted for two more cycles, then set it to normal operation. + * @param base PDM base pointer. + * @param status internal filter status. + */ +static inline void PDM_SetHwvadInternalFilterStatus(PDM_Type *base, pdm_hwvad_filter_status_t status) +{ + base->VAD0_CTRL_1 = (base->VAD0_CTRL_1 & (~PDM_VAD0_CTRL_1_VADST10_MASK)) | (uint32_t)status; +} + +/*! + * @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, + * }; + * @endcode + * @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); + +/*! + * 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); + +/*! + * @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); +/*! @} */ +#endif + +/*! + * @name Transactional + * @{ + */ + +/*! + * @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); + +/*! + * @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, support data width configurations,_pdm_data_width. + * @retval kStatus_PDM_ChannelConfig_Failed or kStatus_Success. + */ +status_t PDM_TransferSetChannelConfig( + PDM_Type *base, pdm_handle_t *handle, uint32_t channel, const pdm_channel_config_t *config, uint32_t format); + +/*! + * @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); + +/*! + * @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); + +/*! + * @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); + +/*! @} */ + +#if defined(__cplusplus) +} +#endif /*_cplusplus*/ + +/*! @} */ + +#endif /* _FSL_PDM_H_ */ diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.c b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.c new file mode 100644 index 0000000000..3c8104b5f3 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.c @@ -0,0 +1,459 @@ +/* + * Copyright 2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_pdm_edma.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.pdm_edma" +#endif + +/******************************************************************************* + * Definitations + ******************************************************************************/ +/* Used for 32byte aligned */ +#define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32) & ~0x1FU) + +/*<! Structure definition for pdm_edma_private_handle_t. The structure is private. */ +typedef struct _pdm_edma_private_handle +{ + PDM_Type *base; + pdm_edma_handle_t *handle; +} pdm_edma_private_handle_t; + +/*! @brief pdm transfer state */ +enum _pdm_edma_transfer_state +{ + kPDM_Busy = 0x0U, /*!< PDM is busy */ + kPDM_Idle, /*!< Transfer is done. */ +}; + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! + * @brief PDM EDMA callback for receive. + * + * @param handle pointer to pdm_edma_handle_t structure which stores the transfer state. + * @param userData Parameter for user callback. + * @param done If the DMA transfer finished. + * @param tcds The TCD index. + */ +static void PDM_EDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds); + +/*! + * @brief Mapping the enabled channel to a number power of 2. + * + * @param channel PDM channel number. + */ +static edma_modulo_t PDM_TransferMappingChannel(uint32_t *channel); +/******************************************************************************* + * Variables + ******************************************************************************/ + +/*! @brief pdm base address pointer */ +static PDM_Type *const s_pdmBases[] = PDM_BASE_PTRS; +/*<! Private handle only used for internally. */ +static pdm_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_pdmBases)]; +/******************************************************************************* + * Code + ******************************************************************************/ +static edma_modulo_t PDM_TransferMappingChannel(uint32_t *channel) +{ + edma_modulo_t modulo = kEDMA_ModuloDisable; +#if FSL_FEATURE_PDM_CHANNEL_NUM == 8U + if (*channel == 2U) + { + modulo = kEDMA_Modulo8bytes; + } + else if ((*channel == 3U) || (*channel == 4U)) + { + *channel = 4U; + modulo = kEDMA_Modulo16bytes; + } + else + { + modulo = kEDMA_ModuloDisable; + } +#endif + + return modulo; +} + +static void PDM_EDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds) +{ + pdm_edma_private_handle_t *privHandle = (pdm_edma_private_handle_t *)userData; + pdm_edma_handle_t *pdmHandle = privHandle->handle; + + if (!(pdmHandle->isLoopTransfer)) + { + (void)memset(&pdmHandle->tcd[pdmHandle->tcdDriver], 0, sizeof(edma_tcd_t)); + pdmHandle->tcdDriver = (pdmHandle->tcdDriver + 1U) % pdmHandle->tcdNum; + } + + pdmHandle->receivedBytes += + pdmHandle->tcd[pdmHandle->tcdDriver].BITER * (pdmHandle->tcd[pdmHandle->tcdDriver].NBYTES & 0x3FFU); + + /* If finished a block, call the callback function */ + if (pdmHandle->callback != NULL) + { + (pdmHandle->callback)(privHandle->base, pdmHandle, kStatus_PDM_Idle, pdmHandle->userData); + } + + pdmHandle->tcdUsedNum--; + /* If all data finished, just stop the transfer */ + if ((pdmHandle->tcdUsedNum == 0U) && !(pdmHandle->isLoopTransfer)) + { + /* Disable DMA enable bit */ + PDM_EnableDMA(privHandle->base, false); + EDMA_AbortTransfer(handle); + } +} + +/*! + * brief Initializes the PDM Rx eDMA handle. + * + * This function initializes the PDM slave DMA handle, which can be used for other PDM master transactional APIs. + * Usually, for a specified PDM instance, call this API once to get the initialized handle. + * + * param base PDM base pointer. + * param handle PDM eDMA handle pointer. + * param base PDM peripheral base address. + * param callback Pointer to user callback function. + * param userData User parameter passed to the callback function. + * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void PDM_TransferCreateHandleEDMA( + PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle) +{ + assert((handle != NULL) && (dmaHandle != NULL)); + + uint32_t instance = PDM_GetInstance(base); + + /* Zero the handle */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Set pdm base to handle */ + handle->dmaHandle = dmaHandle; + handle->callback = callback; + handle->userData = userData; + + /* Set PDM state to idle */ + handle->state = (uint32_t)kPDM_Idle; + + s_edmaPrivateHandle[instance].base = base; + s_edmaPrivateHandle[instance].handle = handle; + + /* Install callback for Tx dma channel */ + EDMA_SetCallback(dmaHandle, PDM_EDMACallback, &s_edmaPrivateHandle[instance]); +} + +/*! + * brief Initializes the multi PDM channel interleave type. + * + * This function initializes the PDM DMA handle member interleaveType, it shall be called only when application would + * like to use type kPDM_EDMAMultiChannelInterleavePerChannelBlock, since the default interleaveType is + * kPDM_EDMAMultiChannelInterleavePerChannelSample always + * + * param handle PDM eDMA handle pointer. + * param multiChannelInterleaveType Multi channel interleave type. + */ +void PDM_TransferSetMultiChannelInterleaveType(pdm_edma_handle_t *handle, + pdm_edma_multi_channel_interleave_t multiChannelInterleaveType) +{ + handle->interleaveType = multiChannelInterleaveType; +} + +/*! + * brief Install EDMA descriptor memory. + * + * param handle Pointer to EDMA channel transfer handle. + * param tcdAddr EDMA head descriptor address. + * param tcdNum EDMA link descriptor address. + */ +void PDM_TransferInstallEDMATCDMemory(pdm_edma_handle_t *handle, void *tcdAddr, size_t tcdNum) +{ + assert(handle != NULL); + + handle->tcd = (edma_tcd_t *)tcdAddr; + handle->tcdNum = tcdNum; +} + +/*! + * brief Configures the PDM channel. + * + * param base PDM base pointer. + * param handle PDM eDMA handle pointer. + * param channel channel index. + * param pdmConfig pdm channel configurations. + */ +void PDM_TransferSetChannelConfigEDMA(PDM_Type *base, + pdm_edma_handle_t *handle, + uint32_t channel, + const pdm_channel_config_t *config) +{ + assert((handle != NULL) && (config != NULL)); + assert(channel < (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM); + + /* Configure the PDM channel */ + PDM_SetChannelConfig(base, channel, config); + + /* record end channel number */ + handle->endChannel = (uint8_t)channel; + /* increase totoal enabled channel number */ + handle->channelNums++; + /* increase count pre channel numbers */ + handle->count = (uint8_t)(base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK); +} + +/*! + * brief Performs a non-blocking PDM receive using eDMA. + * + * note This interface returns immediately after the transfer initiates. Call + * the PDM_GetReceiveRemainingBytes to poll the transfer status and check whether the PDM transfer is finished. + * + * 1. Scatter gather case: + * This functio support dynamic scatter gather and staic scatter gather, + * a. for the dynamic scatter gather case: + * Application should call PDM_TransferReceiveEDMA function continuously to make sure new receive request is submit + *before the previous one finish. b. for the static scatter gather case: Application should use the link transfer + *feature and make sure a loop link transfer is provided, such as: code pdm_edma_transfer_t pdmXfer[2] = + * { + * { + * .data = s_buffer, + * .dataSize = BUFFER_SIZE, + * .linkTransfer = &pdmXfer[1], + * }, + * + * { + * .data = &s_buffer[BUFFER_SIZE], + * .dataSize = BUFFER_SIZE, + * .linkTransfer = &pdmXfer[0] + * }, + * }; + *endcode + * + * 2. Multi channel case: + * This function support receive multi pdm channel data, for example, if two channel is requested, + * code + * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_0, &channelConfig); + * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_1, &channelConfig); + * PDM_TransferReceiveEDMA(DEMO_PDM, &s_pdmRxHandle_0, pdmXfer); + * endcode + * The output data will be formatted as below if handle->interleaveType = + *kPDM_EDMAMultiChannelInterleavePerChannelSample : + * ------------------------------------------------------------------------- + * |CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL 1 | ....| + * ------------------------------------------------------------------------- + * + * The output data will be formatted as below if handle->interleaveType = kPDM_EDMAMultiChannelInterleavePerChannelBlock + *: + * ---------------------------------------------------------------------------------------------------------------------- + * |CHANNEL3 | CHANNEL3 | CHANNEL3 | .... | CHANNEL4 | CHANNEL 4 | CHANNEL4 |....| CHANNEL5 | CHANNEL 5 | CHANNEL5 + *|....| + * ---------------------------------------------------------------------------------------------------------------------- + * Note: the dataSize of xfer is the total data size, while application using + * kPDM_EDMAMultiChannelInterleavePerChannelBlock, the buffer size for each PDM channel is channelSize = dataSize / + * channelNums, there are limitation for this feature, + * 1. For 3 DMIC array: the dataSize shall be 4 * (channelSize) + * The addtional buffer is mandantory for edma modulo feature. + * 2. The kPDM_EDMAMultiChannelInterleavePerChannelBlock feature support below dmic array only, + * 2 DMIC array: CHANNEL3, CHANNEL4 + * 3 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5 + * 4 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6 + * Any other combinations is not support, that is to SAY, THE FEATURE SUPPORT RECEIVE START FROM CHANNEL3 ONLY AND 4 + * MAXIMUM DMIC CHANNELS. + * + * param base PDM base pointer + * param handle PDM eDMA handle pointer. + * param xfer Pointer to DMA transfer structure. + * retval kStatus_Success Start a PDM eDMA receive successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + * retval kStatus_RxBusy PDM is busy receiving data. + */ +status_t PDM_TransferReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_transfer_t *xfer) +{ + assert((handle != NULL) && (xfer != NULL)); + + edma_transfer_config_t config = {0}; + uint32_t startAddr = PDM_GetDataRegisterAddress(base, handle->endChannel - (handle->channelNums - 1UL)); + pdm_edma_transfer_t *currentTransfer = xfer; + uint32_t nextTcdIndex = 0U, tcdIndex = handle->tcdUser, destOffset = FSL_FEATURE_PDM_FIFO_WIDTH; + uint32_t mappedChannel = handle->channelNums; + edma_modulo_t modulo = kEDMA_ModuloDisable; + /* minor offset used for channel sample interleave transfer */ + edma_minor_offset_config_t minorOffset = { + .enableSrcMinorOffset = true, + .enableDestMinorOffset = false, + .minorOffset = 0xFFFFFU - mappedChannel * (uint32_t)FSL_FEATURE_PDM_FIFO_OFFSET + 1U}; + + /* Check if input parameter invalid */ + if ((xfer->data == NULL) || (xfer->dataSize == 0U)) + { + return kStatus_InvalidArgument; + } + + if ((handle->interleaveType == kPDM_EDMAMultiChannelInterleavePerChannelBlock) && (mappedChannel > 1U)) + { + /* Limitation of the feature, reference the API comments */ + if (((startAddr & 0xFU) != 0U) || (mappedChannel > 4U)) + { + return kStatus_InvalidArgument; + } + modulo = PDM_TransferMappingChannel(&mappedChannel); + if ((xfer->dataSize % mappedChannel) != 0U) + { + return kStatus_InvalidArgument; + } + destOffset = xfer->dataSize / mappedChannel; + /* reconfigure the minor loop offset for channel block interleave */ + minorOffset.enableSrcMinorOffset = false, minorOffset.enableDestMinorOffset = true, + minorOffset.minorOffset = + 0xFFFFFU - mappedChannel * (uint32_t)destOffset + (uint32_t)FSL_FEATURE_PDM_FIFO_WIDTH + 1U; + } + + while (currentTransfer != NULL) + { + if (handle->tcdUsedNum >= handle->tcdNum) + { + return kStatus_PDM_QueueFull; + } + else + { + uint32_t primask = DisableGlobalIRQ(); + handle->tcdUsedNum++; + EnableGlobalIRQ(primask); + } + + nextTcdIndex = (handle->tcdUser + 1U) % handle->tcdNum; + + if (mappedChannel == 1U) + { + EDMA_PrepareTransferConfig(&config, (void *)(uint32_t *)startAddr, FSL_FEATURE_PDM_FIFO_WIDTH, 0, + (uint8_t *)(uint32_t)currentTransfer->data, FSL_FEATURE_PDM_FIFO_WIDTH, + FSL_FEATURE_PDM_FIFO_WIDTH, handle->count * (uint32_t)FSL_FEATURE_PDM_FIFO_WIDTH, + currentTransfer->dataSize); + } + else + { + EDMA_PrepareTransferConfig(&config, (void *)(uint32_t *)startAddr, FSL_FEATURE_PDM_FIFO_WIDTH, + FSL_FEATURE_PDM_FIFO_OFFSET, (uint8_t *)(uint32_t)currentTransfer->data, + FSL_FEATURE_PDM_FIFO_WIDTH, (int16_t)destOffset, + mappedChannel * (uint32_t)FSL_FEATURE_PDM_FIFO_WIDTH, currentTransfer->dataSize); + } + + EDMA_TcdSetTransferConfig((edma_tcd_t *)&handle->tcd[handle->tcdUser], &config, + (edma_tcd_t *)&handle->tcd[nextTcdIndex]); + + if (mappedChannel > 1U) + { + EDMA_TcdSetMinorOffsetConfig((edma_tcd_t *)&handle->tcd[handle->tcdUser], &minorOffset); + + if (handle->interleaveType == kPDM_EDMAMultiChannelInterleavePerChannelBlock) + { + EDMA_TcdSetModulo((edma_tcd_t *)&handle->tcd[handle->tcdUser], modulo, kEDMA_ModuloDisable); + } + } + + EDMA_TcdEnableInterrupts((edma_tcd_t *)&handle->tcd[handle->tcdUser], (uint32_t)kEDMA_MajorInterruptEnable); + + handle->tcdUser = nextTcdIndex; + + currentTransfer = currentTransfer->linkTransfer; + + if (currentTransfer == xfer) + { + handle->isLoopTransfer = true; + break; + } + } + + if (handle->state != (uint32_t)kPDM_Busy) + { + EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, (edma_tcd_t *)&handle->tcd[tcdIndex]); + /* Start DMA transfer */ + EDMA_StartTransfer(handle->dmaHandle); + + /* Enable DMA enable bit */ + PDM_EnableDMA(base, true); + /* enable PDM */ + PDM_Enable(base, true); + + handle->state = (uint32_t)kPDM_Busy; + } + + return kStatus_Success; +} + +/*! + * brief Aborts a PDM receive using eDMA. + * + * This function only aborts the current transfer slots, the other transfer slots' information still kept + * in the handler. If users want to terminate all transfer slots, just call PDM_TransferTerminateReceiveEDMA. + * + * param base PDM base pointer + * param handle PDM eDMA handle pointer. + */ +void PDM_TransferAbortReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle) +{ + assert(handle != NULL); + + /* Disable dma */ + EDMA_AbortTransfer(handle->dmaHandle); + + /* Disable DMA enable bit */ + PDM_EnableDMA(base, false); + + /* Disable PDM */ + PDM_Enable(base, false); + + /* Handle the queue index */ + handle->tcdUsedNum--; + + /* Set the handle state */ + handle->state = (uint32_t)kPDM_Idle; +} + +/*! + * brief Terminate all PDM receive. + * + * This function will clear all transfer slots buffered in the pdm queue. If users only want to abort the + * current transfer slot, please call PDM_TransferAbortReceiveEDMA. + * + * param base PDM base pointer. + * param handle PDM eDMA handle pointer. + */ +void PDM_TransferTerminateReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle) +{ + assert(handle != NULL); + + /* Abort the current transfer */ + PDM_TransferAbortReceiveEDMA(base, handle); + + /* Clear all the internal information */ + (void)memset(handle->tcd, 0, sizeof(edma_tcd_t) * handle->tcdNum); + handle->tcdUser = 0U; + handle->tcdUsedNum = 0U; +} + +/*! + * brief Gets byte count received by PDM. + * + * param base PDM base pointer + * param handle PDM eDMA handle pointer. + * param count Bytes count received by PDM. + * retval kStatus_Success Succeed get the transfer count. + * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t PDM_TransferGetReceiveCountEDMA(PDM_Type *base, pdm_edma_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + *count = handle->receivedBytes; + + return kStatus_Success; +} diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.h b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.h new file mode 100644 index 0000000000..4da78192f9 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_edma.h @@ -0,0 +1,254 @@ +/* + * Copyright 2019 - 2020, NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_PDM_EDMA_H_ +#define _FSL_PDM_EDMA_H_ + +#include "fsl_edma.h" +#include "fsl_pdm.h" + +/*! + * @addtogroup pdm_edma PDM EDMA Driver + * @ingroup pdm + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +#define FSL_PDM_EDMA_DRIVER_VERSION (MAKE_VERSION(2, 6, 1)) /*!< Version 2.6.1 */ +/*@}*/ + +/*! @brief PDM edma handler */ +typedef struct _pdm_edma_handle pdm_edma_handle_t; + +/*!@brief pdm multi channel interleave type */ +typedef enum _pdm_edma_multi_channel_interleave +{ + kPDM_EDMAMultiChannelInterleavePerChannelSample = + 0U, /*!< multi channel PDM data interleave per channel sample + * ------------------------------------------------------------------------- + * |CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL 1 | ....| + * ------------------------------------------------------------------------- + */ + kPDM_EDMAMultiChannelInterleavePerChannelBlock = + 1U, /*!< multi channel PDM data interleave per channel block + * ---------------------------------------------------------------------------------------------------------------------------- + * |CHANNEL0 | CHANNEL0 | CHANNEL0 | ...... | CHANNEL1 | CHANNEL 1 | CHANNEL 1 | ....| CHANNEL2 | CHANNEL 2 + * | CHANNEL 2 | ....| + * ---------------------------------------------------------------------------------------------------------------------------- + */ +} pdm_edma_multi_channel_interleave_t; + +/*! @brief PDM edma transfer */ +typedef struct _pdm_edma_transfer +{ + volatile uint8_t *data; /*!< Data start address to transfer. */ + volatile size_t dataSize; /*!< Total Transfer bytes size. */ + struct _pdm_edma_transfer *linkTransfer; /*!< linked transfer configurations */ +} pdm_edma_transfer_t; + +/*! @brief PDM eDMA transfer callback function for finish and error */ +typedef void (*pdm_edma_callback_t)(PDM_Type *base, pdm_edma_handle_t *handle, status_t status, void *userData); + +/*! @brief PDM DMA transfer handle, users should not touch the content of the handle.*/ +struct _pdm_edma_handle +{ + edma_handle_t *dmaHandle; /*!< DMA handler for PDM send */ + uint8_t count; /*!< The transfer data count in a DMA request */ + uint32_t receivedBytes; /*!< total transfer count */ + uint32_t state; /*!< Internal state for PDM eDMA transfer */ + pdm_edma_callback_t callback; /*!< Callback for users while transfer finish or error occurs */ + bool isLoopTransfer; /*!< loop transfer */ + void *userData; /*!< User callback parameter */ + edma_tcd_t *tcd; /*!< TCD pool for eDMA transfer. */ + uint32_t tcdNum; /*!< TCD number */ + uint32_t tcdUser; /*!< Index for user to queue transfer. */ + uint32_t tcdDriver; /*!< Index for driver to get the transfer data and size */ + volatile uint32_t tcdUsedNum; /*!< Index for user to queue transfer. */ + + pdm_edma_multi_channel_interleave_t interleaveType; /*!< multi channel transfer interleave type */ + + uint8_t endChannel; /*!< The last enabled channel */ + uint8_t channelNums; /*!< total channel numbers */ +}; + +/******************************************************************************* + * APIs + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PDM eDMA Transactional + * @{ + */ + +/*! + * @brief Install EDMA descriptor memory. + * + * @param handle Pointer to EDMA channel transfer handle. + * @param tcdAddr EDMA head descriptor address. + * @param tcdNum EDMA link descriptor address. + */ +void PDM_TransferInstallEDMATCDMemory(pdm_edma_handle_t *handle, void *tcdAddr, size_t tcdNum); + +/*! + * @brief Initializes the PDM Rx eDMA handle. + * + * This function initializes the PDM slave DMA handle, which can be used for other PDM master transactional APIs. + * Usually, for a specified PDM instance, call this API once to get the initialized handle. + * + * @param base PDM base pointer. + * @param handle PDM eDMA handle pointer. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void PDM_TransferCreateHandleEDMA( + PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle); + +/*! + * @brief Initializes the multi PDM channel interleave type. + * + * This function initializes the PDM DMA handle member interleaveType, it shall be called only when application would + * like to use type kPDM_EDMAMultiChannelInterleavePerChannelBlock, since the default interleaveType is + * kPDM_EDMAMultiChannelInterleavePerChannelSample always + * + * @param handle PDM eDMA handle pointer. + * @param multiChannelInterleaveType Multi channel interleave type. + */ +void PDM_TransferSetMultiChannelInterleaveType(pdm_edma_handle_t *handle, + pdm_edma_multi_channel_interleave_t multiChannelInterleaveType); + +/*! + * @brief Configures the PDM channel. + * + * @param base PDM base pointer. + * @param handle PDM eDMA handle pointer. + * @param channel channel index. + * @param config pdm channel configurations. + */ +void PDM_TransferSetChannelConfigEDMA(PDM_Type *base, + pdm_edma_handle_t *handle, + uint32_t channel, + const pdm_channel_config_t *config); + +/*! + * @brief Performs a non-blocking PDM receive using eDMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * the PDM_GetReceiveRemainingBytes to poll the transfer status and check whether the PDM transfer is finished. + * + * 1. Scatter gather case: + * This functio support dynamic scatter gather and staic scatter gather, + * a. for the dynamic scatter gather case: + * Application should call PDM_TransferReceiveEDMA function continuously to make sure new receive request is submit + * before the previous one finish. b. for the static scatter gather case: Application should use the link transfer + * feature and make sure a loop link transfer is provided, such as: + * @code pdm_edma_transfer_t pdmXfer[2] = + * { + * { + * .data = s_buffer, + * .dataSize = BUFFER_SIZE, + * .linkTransfer = &pdmXfer[1], + * }, + * + * { + * .data = &s_buffer[BUFFER_SIZE], + * .dataSize = BUFFER_SIZE, + * .linkTransfer = &pdmXfer[0] + * }, + * }; + * @endcode + * + * 2. Multi channel case: + * This function support receive multi pdm channel data, for example, if two channel is requested, + * @code + * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_0, &channelConfig); + * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_1, &channelConfig); + * PDM_TransferReceiveEDMA(DEMO_PDM, &s_pdmRxHandle_0, pdmXfer); + * @endcode + * The output data will be formatted as below if handle->interleaveType = + * kPDM_EDMAMultiChannelInterleavePerChannelSample : + * ------------------------------------------------------------------------- + * |CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL 1 | ....| + * ------------------------------------------------------------------------- + * + * The output data will be formatted as below if handle->interleaveType = kPDM_EDMAMultiChannelInterleavePerChannelBlock + * : + * ---------------------------------------------------------------------------------------------------------------------- + * |CHANNEL3 | CHANNEL3 | CHANNEL3 | .... | CHANNEL4 | CHANNEL 4 | CHANNEL4 |....| CHANNEL5 | CHANNEL 5 | CHANNEL5 + * |....| + * ---------------------------------------------------------------------------------------------------------------------- + * Note: the dataSize of xfer is the total data size, while application using + * kPDM_EDMAMultiChannelInterleavePerChannelBlock, the buffer size for each PDM channel is channelSize = dataSize / + * channelNums, then there are limitation for this feature, + * 1. 3 DMIC array: the dataSize shall be 4 * (channelSize) + * The addtional buffer is mandantory for edma modulo feature. + * 2. The kPDM_EDMAMultiChannelInterleavePerChannelBlock feature support below dmic array only, + * 2 DMIC array: CHANNEL3, CHANNEL4 + * 3 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5 + * 4 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6 + * Any other combinations is not support, that is to SAY, THE FEATURE SUPPORT RECEIVE START FROM CHANNEL3 ONLY AND 4 + * MAXIMUM DMIC CHANNELS. + * + * @param base PDM base pointer + * @param handle PDM eDMA handle pointer. + * @param xfer Pointer to DMA transfer structure. + * @retval kStatus_Success Start a PDM eDMA receive successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_RxBusy PDM is busy receiving data. + */ +status_t PDM_TransferReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_transfer_t *xfer); + +/*! + * @brief Terminate all PDM receive. + * + * This function will clear all transfer slots buffered in the pdm queue. If users only want to abort the + * current transfer slot, please call PDM_TransferAbortReceiveEDMA. + * + * @param base PDM base pointer. + * @param handle PDM eDMA handle pointer. + */ +void PDM_TransferTerminateReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle); + +/*! + * @brief Aborts a PDM receive using eDMA. + * + * This function only aborts the current transfer slots, the other transfer slots' information still kept + * in the handler. If users want to terminate all transfer slots, just call PDM_TransferTerminateReceiveEDMA. + * + * @param base PDM base pointer + * @param handle PDM eDMA handle pointer. + */ +void PDM_TransferAbortReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle); + +/*! + * @brief Gets byte count received by PDM. + * + * @param base PDM base pointer + * @param handle PDM eDMA handle pointer. + * @param count Bytes count received by PDM. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t PDM_TransferGetReceiveCountEDMA(PDM_Type *base, pdm_edma_handle_t *handle, size_t *count); + +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ +#endif diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_sdma.h b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_sdma.h new file mode 100644 index 0000000000..9237341309 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/pdm/fsl_pdm_sdma.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018, Freescale Semiconductor, Inc. + * Copyright 2019 - 2020, NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_PDM_SDMA_H_ +#define _FSL_PDM_SDMA_H_ + +#include "fsl_pdm.h" +#include "fsl_sdma.h" + +/*! + * @addtogroup pdm_sdma PDM SDMA Driver + * @ingroup pdm + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +#define FSL_PDM_SDMA_DRIVER_VERSION (MAKE_VERSION(2, 7, 0)) /*!< Version 2.7.0 */ +/*@}*/ + +typedef struct _pdm_sdma_handle pdm_sdma_handle_t; + +/*! @brief PDM eDMA transfer callback function for finish and error */ +typedef void (*pdm_sdma_callback_t)(PDM_Type *base, pdm_sdma_handle_t *handle, status_t status, void *userData); + +/*! @brief PDM DMA transfer handle, users should not touch the content of the handle.*/ +struct _pdm_sdma_handle +{ + sdma_handle_t *dmaHandle; /*!< DMA handler for PDM send */ + uint8_t nbytes; /*!< eDMA minor byte transfer count initially configured. */ + uint8_t fifoWidth; /*!< fifo width */ + uint8_t endChannel; /*!< The last enabled channel */ + uint8_t channelNums; /*!< total channel numbers */ + uint32_t count; /*!< The transfer data count in a DMA request */ + uint32_t state; /*!< Internal state for PDM eDMA transfer */ + uint32_t eventSource; /*!< PDM event source number */ + pdm_sdma_callback_t callback; /*!< Callback for users while transfer finish or error occurs */ + void *userData; /*!< User callback parameter */ + sdma_buffer_descriptor_t bdPool[PDM_XFER_QUEUE_SIZE]; /*!< BD pool for SDMA transfer. */ + pdm_transfer_t pdmQueue[PDM_XFER_QUEUE_SIZE]; /*!< Transfer queue storing queued transfer. */ + size_t transferSize[PDM_XFER_QUEUE_SIZE]; /*!< Data bytes need to transfer */ + volatile uint8_t queueUser; /*!< Index for user to queue transfer. */ + volatile uint8_t queueDriver; /*!< Index for driver to get the transfer data and size */ +}; + +/******************************************************************************* + * APIs + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name eDMA Transactional + * @{ + */ + +/*! + * @brief Initializes the PDM eDMA handle. + * + * This function initializes the PDM DMA handle, which can be used for other PDM master transactional APIs. + * Usually, for a specified PDM instance, call this API once to get the initialized handle. + * + * @param base PDM base pointer. + * @param handle PDM eDMA handle pointer. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + * @param eventSource PDM event source number. + */ +void PDM_TransferCreateHandleSDMA(PDM_Type *base, + pdm_sdma_handle_t *handle, + pdm_sdma_callback_t callback, + void *userData, + sdma_handle_t *dmaHandle, + uint32_t eventSource); + +/*! + * @brief Performs a non-blocking PDM receive using eDMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * the PDM_GetReceiveRemainingBytes to poll the transfer status and check whether the PDM transfer is finished. + * + * @param base PDM base pointer + * @param handle PDM eDMA handle pointer. + * @param xfer Pointer to DMA transfer structure. + * @retval kStatus_Success Start a PDM eDMA receive successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_RxBusy PDM is busy receiving data. + */ +status_t PDM_TransferReceiveSDMA(PDM_Type *base, pdm_sdma_handle_t *handle, pdm_transfer_t *xfer); + +/*! + * @brief Aborts a PDM receive using eDMA. + * + * @param base PDM base pointer + * @param handle PDM eDMA handle pointer. + */ +void PDM_TransferAbortReceiveSDMA(PDM_Type *base, pdm_sdma_handle_t *handle); + +/*! + * @brief PDM channel configurations. + * + * @param base PDM base pointer. + * @param handle PDM eDMA handle pointer. + * @param channel channel number. + * @param config channel configurations. + */ +void PDM_SetChannelConfigSDMA(PDM_Type *base, + pdm_sdma_handle_t *handle, + uint32_t channel, + const pdm_channel_config_t *config); + +/*! + * @brief Terminate all the PDM sdma receive transfer. + * + * @param base PDM base pointer. + * @param handle PDM SDMA handle pointer. + */ +void PDM_TransferTerminateReceiveSDMA(PDM_Type *base, pdm_sdma_handle_t *handle); + +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ +#endif |