1 /*
2 * Copyright (c) 2018, Freescale Semiconductor, Inc.
3 * Copyright 2019-2020 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_pdm.h"
10 /* Component ID definition, used by tools. */
11 #ifndef FSL_COMPONENT_ID
12 #define FSL_COMPONENT_ID "platform.drivers.pdm"
13 #endif
14
15 /*******************************************************************************
16 * Definitations
17 ******************************************************************************/
18 /*! @brief Typedef for pdm rx interrupt handler. */
19 typedef void (*pdm_isr_t)(PDM_Type *base, pdm_handle_t *pdmHandle);
20 /*******************************************************************************
21 * Prototypes
22 ******************************************************************************/
23 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
24 /*!
25 * @brief Get the instance number for PDM.
26 *
27 * @param channelMask enabled channel.
28 * @param qualitymode selected quality mode.
29 * @param osr oversample rate.
30 * @param regdiv register divider.
31 */
32 static status_t PDM_ValidateSrcClockRate(uint32_t channelMask,
33 pdm_df_quality_mode_t qualityMode,
34 uint8_t osr,
35 uint32_t regDiv);
36 #endif
37
38 /*******************************************************************************
39 * Variables
40 ******************************************************************************/
41 /* Base pointer array */
42 static PDM_Type *const s_pdmBases[] = PDM_BASE_PTRS;
43 /*!@brief PDM handle pointer */
44 static pdm_handle_t *s_pdmHandle[ARRAY_SIZE(s_pdmBases)];
45 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
46 /* Clock name array */
47 static const clock_ip_name_t s_pdmClock[] = PDM_CLOCKS;
48 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
49
50 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
51 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
52 /* Clock name array */
53 static const clock_ip_name_t s_pdmFilterClock[] = PDM_FILTER_CLOCKS;
54 #endif
55 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
56
57 /*! @brief Pointer to tx IRQ handler for each instance. */
58 static pdm_isr_t s_pdmIsr;
59 #if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD)
60 /*! @brief callback for hwvad. */
61 static pdm_hwvad_notification_t s_pdm_hwvad_notification[ARRAY_SIZE(s_pdmBases)];
62 #endif
63 /*******************************************************************************
64 * Code
65 ******************************************************************************/
PDM_GetInstance(PDM_Type * base)66 uint32_t PDM_GetInstance(PDM_Type *base)
67 {
68 uint32_t instance;
69
70 /* Find the instance index from base address mappings. */
71 for (instance = 0; instance < ARRAY_SIZE(s_pdmBases); instance++)
72 {
73 if (s_pdmBases[instance] == base)
74 {
75 break;
76 }
77 }
78
79 assert(instance < ARRAY_SIZE(s_pdmBases));
80
81 return instance;
82 }
83
84 /*!
85 * brief PDM read fifo.
86 * Note: This function support 16 bit only for IP version that only supports 16bit.
87 *
88 * param base PDM base pointer.
89 * param startChannel start channel number.
90 * param channelNums total enabled channelnums.
91 * param buffer received buffer address.
92 * param size number of samples to read.
93 * param dataWidth sample width.
94 */
PDM_ReadFifo(PDM_Type * base,uint32_t startChannel,uint32_t channelNums,void * buffer,size_t size,uint32_t dataWidth)95 void PDM_ReadFifo(
96 PDM_Type *base, uint32_t startChannel, uint32_t channelNums, void *buffer, size_t size, uint32_t dataWidth)
97 {
98 uint32_t i = 0, j = 0U;
99 uint32_t *dataAddr = (uint32_t *)buffer;
100
101 for (i = 0U; i < size; i++)
102 {
103 for (j = 0; j < channelNums; j++)
104 {
105 #if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH != 2U)
106 *dataAddr = base->DATACH[startChannel + j] >> (dataWidth == 4U ? 0U : 8U);
107 dataAddr = (uint32_t *)((uint32_t)dataAddr + dataWidth);
108 #else
109 *dataAddr = base->DATACH[startChannel + j];
110 dataAddr = (uint32_t *)((uint32_t)dataAddr + 2U);
111 #endif
112 }
113 }
114 }
115
116 #if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH == 2U)
117 /*!
118 * brief PDM read data non blocking, only support 16bit data read.
119 * So the actually read data byte size in this function is (size * 2 * channelNums).
120 * param base PDM base pointer.
121 * param startChannel start channel number.
122 * param channelNums total enabled channelnums.
123 * param buffer received buffer address.
124 * param size number of 16bit data to read.
125 */
PDM_ReadNonBlocking(PDM_Type * base,uint32_t startChannel,uint32_t channelNums,int16_t * buffer,size_t size)126 void PDM_ReadNonBlocking(PDM_Type *base, uint32_t startChannel, uint32_t channelNums, int16_t *buffer, size_t size)
127 {
128 uint32_t i = 0, j = 0U;
129
130 for (i = 0U; i < size; i++)
131 {
132 for (j = 0; j < channelNums; j++)
133 {
134 *buffer++ = (int16_t)base->DATACH[startChannel + j];
135 }
136 }
137 }
138 #endif
139
140 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
PDM_ValidateSrcClockRate(uint32_t channelMask,pdm_df_quality_mode_t qualityMode,uint8_t osr,uint32_t regDiv)141 static status_t PDM_ValidateSrcClockRate(uint32_t channelMask,
142 pdm_df_quality_mode_t qualityMode,
143 uint8_t osr,
144 uint32_t regDiv)
145 {
146 uint32_t enabledChannel = 0U, i = 0U, factor = 0U, k = 0U;
147
148 for (i = 0U; i < (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM; i++)
149 {
150 if (((channelMask >> i) & 0x01U) != 0U)
151 {
152 enabledChannel++;
153 }
154 }
155
156 switch (qualityMode)
157 {
158 case kPDM_QualityModeMedium:
159 factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR;
160 k = 2U;
161 break;
162
163 case kPDM_QualityModeHigh:
164 factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR;
165 k = 1U;
166 break;
167
168 case kPDM_QualityModeLow:
169 factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR;
170 k = 4U;
171 break;
172
173 case kPDM_QualityModeVeryLow0:
174 factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR;
175 k = 2U;
176 break;
177
178 case kPDM_QualityModeVeryLow1:
179 factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR;
180 k = 4U;
181 break;
182
183 case kPDM_QualityModeVeryLow2:
184 factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR;
185 k = 8U;
186 break;
187
188 default:
189 assert(false);
190 break;
191 }
192
193 /* validate the minimum clock divider */
194 /* 2U is for canculating k, 100U is for determing the specific float number of clock divider */
195 if (((regDiv * k) / 2U * 100U) < (((10U + factor * enabledChannel) * 100U / (8U * osr)) * k / 2U))
196 {
197 return kStatus_Fail;
198 }
199
200 return kStatus_Success;
201 }
202 #endif
203
204 /*!
205 * brief PDM set sample rate.
206 *
207 * note This function is depend on the configuration of the PDM and PDM channel, so the correct call sequence is
208 * code
209 * PDM_Init(base, pdmConfig)
210 * PDM_SetChannelConfig(base, channel, &channelConfig)
211 * PDM_SetSampleRateConfig(base, source, sampleRate)
212 * endcode
213 * param base PDM base pointer
214 * param sourceClock_HZ PDM source clock frequency.
215 * param sampleRate_HZ PDM sample rate.
216 */
PDM_SetSampleRateConfig(PDM_Type * base,uint32_t sourceClock_HZ,uint32_t sampleRate_HZ)217 status_t PDM_SetSampleRateConfig(PDM_Type *base, uint32_t sourceClock_HZ, uint32_t sampleRate_HZ)
218 {
219 uint32_t osr = (base->CTRL_2 & PDM_CTRL_2_CICOSR_MASK) >> PDM_CTRL_2_CICOSR_SHIFT;
220 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
221 pdm_df_quality_mode_t qualityMode =
222 (pdm_df_quality_mode_t)(uint32_t)((base->CTRL_2 & PDM_CTRL_2_QSEL_MASK) >> PDM_CTRL_2_QSEL_SHIFT);
223 uint32_t enabledChannelMask = base->CTRL_1 & (uint32_t)kPDM_EnableChannelAll;
224 #endif
225
226 uint32_t pdmClockRate = 0U;
227 uint32_t regDiv = 0U;
228
229 /* get divider */
230 osr = 16U - osr;
231 pdmClockRate = sampleRate_HZ * osr * 8U;
232 regDiv = sourceClock_HZ / pdmClockRate;
233
234 if (regDiv > PDM_CTRL_2_CLKDIV_MASK)
235 {
236 return kStatus_Fail;
237 }
238
239 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
240 if (PDM_ValidateSrcClockRate(enabledChannelMask, qualityMode, (uint8_t)osr, regDiv) == kStatus_Fail)
241 {
242 return kStatus_Fail;
243 }
244 #endif
245
246 base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv);
247
248 return kStatus_Success;
249 }
250
251 /*!
252 * brief PDM set sample rate.
253 *
254 * deprecated Do not use this function. It has been superceded by @ref PDM_SetSampleRateConfig
255 * param base PDM base pointer
256 * param enableChannelMask PDM channel enable mask.
257 * param qualityMode quality mode.
258 * param osr cic oversample rate
259 * param clkDiv clock divider
260 */
PDM_SetSampleRate(PDM_Type * base,uint32_t enableChannelMask,pdm_df_quality_mode_t qualityMode,uint8_t osr,uint32_t clkDiv)261 status_t PDM_SetSampleRate(
262 PDM_Type *base, uint32_t enableChannelMask, pdm_df_quality_mode_t qualityMode, uint8_t osr, uint32_t clkDiv)
263 {
264 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
265 uint8_t realOsr = 16U - (osr & (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT));
266 #endif
267 uint32_t regDiv = clkDiv >> 1U;
268
269 switch (qualityMode)
270 {
271 case kPDM_QualityModeHigh:
272 regDiv <<= 1U;
273 break;
274 case kPDM_QualityModeLow:
275 case kPDM_QualityModeVeryLow1:
276 regDiv >>= 1U;
277 break;
278 case kPDM_QualityModeVeryLow2:
279 regDiv >>= 2U;
280 break;
281 default:
282 assert(false);
283 break;
284 }
285
286 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
287 if (PDM_ValidateSrcClockRate(enableChannelMask, qualityMode, realOsr, regDiv) == kStatus_Fail)
288 {
289 return kStatus_Fail;
290 }
291 #endif
292
293 assert(regDiv <= PDM_CTRL_2_CLKDIV_MASK);
294 base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv);
295
296 return kStatus_Success;
297 }
298
299 /*!
300 * brief Initializes the PDM peripheral.
301 *
302 * Ungates the PDM clock, resets the module, and configures PDM with a configuration structure.
303 * The configuration structure can be custom filled or set with default values by
304 * PDM_GetDefaultConfig().
305 *
306 * note This API should be called at the beginning of the application to use
307 * the PDM driver. Otherwise, accessing the PDM module can cause a hard fault
308 * because the clock is not enabled.
309 *
310 * param base PDM base pointer
311 * param config PDM configuration structure.
312 */
PDM_Init(PDM_Type * base,const pdm_config_t * config)313 void PDM_Init(PDM_Type *base, const pdm_config_t *config)
314 {
315 assert(config != NULL);
316 assert(config->fifoWatermark <= PDM_FIFO_CTRL_FIFOWMK_MASK);
317
318 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
319 /* Enable the PDM clock */
320 CLOCK_EnableClock(s_pdmClock[PDM_GetInstance(base)]);
321 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
322 CLOCK_EnableClock(s_pdmFilterClock[PDM_GetInstance(base)]);
323 #endif
324 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
325
326 /* Enable the module and disable the interface/all channel */
327 base->CTRL_1 &=
328 ~(PDM_CTRL_1_MDIS_MASK | PDM_CTRL_1_PDMIEN_MASK | PDM_CTRL_1_ERREN_MASK | (uint32_t)kPDM_EnableChannelAll);
329
330 /* wait all filter stopped */
331 while ((base->STAT & PDM_STAT_BSY_FIL_MASK) != 0U)
332 {
333 }
334
335 /* software reset */
336 base->CTRL_1 |= PDM_CTRL_1_SRES_MASK;
337
338 /* Set the configure settings */
339 base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DOZEN_MASK)) | PDM_CTRL_1_DOZEN(config->enableDoze);
340
341 base->CTRL_2 = (base->CTRL_2 & (~(PDM_CTRL_2_CICOSR_MASK | PDM_CTRL_2_QSEL_MASK))) |
342 PDM_CTRL_2_CICOSR(config->cicOverSampleRate) | PDM_CTRL_2_QSEL(config->qualityMode);
343
344 /* Set the watermark */
345 base->FIFO_CTRL = PDM_FIFO_CTRL_FIFOWMK(config->fifoWatermark);
346 }
347
348 /*!
349 * brief De-initializes the PDM peripheral.
350 *
351 * This API gates the PDM clock. The PDM module can't operate unless PDM_Init
352 * is called to enable the clock.
353 *
354 * param base PDM base pointer
355 */
PDM_Deinit(PDM_Type * base)356 void PDM_Deinit(PDM_Type *base)
357 {
358 /* disable PDM interface */
359 PDM_Enable(base, false);
360
361 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
362 CLOCK_DisableClock(s_pdmClock[PDM_GetInstance(base)]);
363 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
364 CLOCK_DisableClock(s_pdmFilterClock[PDM_GetInstance(base)]);
365 #endif
366 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
367 }
368
369 /*!
370 * brief Enables the PDM interrupt requests.
371 *
372 * param base PDM base pointer
373 * param mask interrupt source
374 * The parameter can be a combination of the following sources if defined.
375 * arg kPDM_ErrorInterruptEnable
376 * arg kPDM_FIFOInterruptEnable
377 */
PDM_EnableInterrupts(PDM_Type * base,uint32_t mask)378 void PDM_EnableInterrupts(PDM_Type *base, uint32_t mask)
379 {
380 if ((mask & (uint32_t)kPDM_FIFOInterruptEnable) != 0U)
381 {
382 base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DISEL_MASK)) | (uint32_t)kPDM_FIFOInterruptEnable;
383 }
384 if ((mask & (uint32_t)kPDM_ErrorInterruptEnable) != 0U)
385 {
386 base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_ERREN_MASK)) | (uint32_t)kPDM_ErrorInterruptEnable;
387 }
388 }
389
390 /*!
391 * brief PDM one channel configurations.
392 *
393 * param base PDM base pointer
394 * param config PDM channel configurations.
395 * param channel channel number.
396 * after completing the current frame in debug mode.
397 */
PDM_SetChannelConfig(PDM_Type * base,uint32_t channel,const pdm_channel_config_t * config)398 void PDM_SetChannelConfig(PDM_Type *base, uint32_t channel, const pdm_channel_config_t *config)
399 {
400 assert(config != NULL);
401 assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
402
403 uint32_t dcCtrl = 0U;
404
405 #if (defined(FSL_FEATURE_PDM_HAS_DC_OUT_CTRL) && (FSL_FEATURE_PDM_HAS_DC_OUT_CTRL))
406 dcCtrl = base->DC_OUT_CTRL;
407 /* configure gain and cut off freq */
408 dcCtrl &= ~((uint32_t)PDM_DC_OUT_CTRL_DCCONFIG0_MASK << (channel << 1U));
409 dcCtrl |= (uint32_t)config->outputCutOffFreq << (channel << 1U);
410 base->DC_OUT_CTRL = dcCtrl;
411 #endif
412
413 #if !(defined(FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED) && (FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED))
414 dcCtrl = base->DC_CTRL;
415 /* configure gain and cut off freq */
416 dcCtrl &= ~((uint32_t)PDM_DC_CTRL_DCCONFIG0_MASK << (channel << 1U));
417 dcCtrl |= (uint32_t)config->cutOffFreq << (channel << 1U);
418 base->DC_CTRL = dcCtrl;
419 #endif
420
421 PDM_SetChannelGain(base, channel, config->gain);
422
423 /* enable channel */
424 base->CTRL_1 |= 1UL << channel;
425 }
426
427 /*!
428 * brief Set the PDM channel gain.
429 *
430 * Please note for different quality mode, the valid gain value is different, reference RM for detail.
431 * param base PDM base pointer.
432 * param channel PDM channel index.
433 * param gain channel gain, the register gain value range is 0 - 15.
434 */
PDM_SetChannelGain(PDM_Type * base,uint32_t channel,pdm_df_output_gain_t gain)435 void PDM_SetChannelGain(PDM_Type *base, uint32_t channel, pdm_df_output_gain_t gain)
436 {
437 assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
438
439 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
440 uint32_t outCtrl = base->RANGE_CTRL;
441 #else
442 uint32_t outCtrl = base->OUT_CTRL;
443 #endif
444
445 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
446 outCtrl &= ~((uint32_t)PDM_RANGE_CTRL_RANGEADJ0_MASK << (channel << 2U));
447 #else
448 outCtrl &= ~((uint32_t)PDM_OUT_CTRL_OUTGAIN0_MASK << (channel << 2U));
449 #endif
450
451 outCtrl |= (uint32_t)gain << (channel << 2U);
452
453 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
454 base->RANGE_CTRL = outCtrl;
455 #else
456 base->OUT_CTRL = outCtrl;
457 #endif
458 }
459
460 /*!
461 * brief PDM set channel transfer config.
462 *
463 * param base PDM base pointer.
464 * param handle PDM handle pointer.
465 * param channel PDM channel.
466 * param config channel config.
467 * param format data format.
468 */
PDM_TransferSetChannelConfig(PDM_Type * base,pdm_handle_t * handle,uint32_t channel,const pdm_channel_config_t * config,uint32_t format)469 status_t PDM_TransferSetChannelConfig(
470 PDM_Type *base, pdm_handle_t *handle, uint32_t channel, const pdm_channel_config_t *config, uint32_t format)
471 {
472 assert(handle != NULL);
473
474 PDM_SetChannelConfig(base, channel, config);
475
476 handle->format = format;
477
478 if (handle->channelNums == 0U)
479 {
480 handle->startChannel = (uint8_t)channel;
481 }
482
483 handle->channelNums++;
484
485 if (handle->channelNums > (uint8_t)FSL_FEATURE_PDM_CHANNEL_NUM)
486 {
487 return kStatus_PDM_ChannelConfig_Failed;
488 }
489
490 return kStatus_Success;
491 }
492
493 /*!
494 * brief Initializes the PDM handle.
495 *
496 * This function initializes the handle for the PDM transactional APIs. Call
497 * this function once to get the handle initialized.
498 *
499 * param base PDM base pointer.
500 * param handle PDM handle pointer.
501 * param callback Pointer to the user callback function.
502 * param userData User parameter passed to the callback function.
503 */
PDM_TransferCreateHandle(PDM_Type * base,pdm_handle_t * handle,pdm_transfer_callback_t callback,void * userData)504 void PDM_TransferCreateHandle(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_callback_t callback, void *userData)
505 {
506 assert(handle != NULL);
507
508 /* Zero the handle */
509 (void)memset(handle, 0, sizeof(*handle));
510
511 s_pdmHandle[PDM_GetInstance(base)] = handle;
512
513 handle->callback = callback;
514 handle->userData = userData;
515 handle->watermark = (uint8_t)(base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK);
516
517 /* Set the isr pointer */
518 s_pdmIsr = PDM_TransferHandleIRQ;
519
520 /* Enable RX event IRQ */
521 (void)EnableIRQ(PDM_EVENT_IRQn);
522 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
523 /* Enable FIFO error IRQ */
524 (void)EnableIRQ(PDM_ERROR_IRQn);
525 #endif
526 }
527
528 /*!
529 * brief Performs an interrupt non-blocking receive transfer on PDM.
530 *
531 * note This API returns immediately after the transfer initiates.
532 * Call the PDM_RxGetTransferStatusIRQ to poll the transfer status and check whether
533 * the transfer is finished. If the return status is not kStatus_PDM_Busy, the transfer
534 * is finished.
535 *
536 * param base PDM base pointer
537 * param handle Pointer to the pdm_handle_t structure which stores the transfer state.
538 * param xfer Pointer to the pdm_transfer_t structure.
539 * retval kStatus_Success Successfully started the data receive.
540 * retval kStatus_PDM_Busy Previous receive still not finished.
541 */
PDM_TransferReceiveNonBlocking(PDM_Type * base,pdm_handle_t * handle,pdm_transfer_t * xfer)542 status_t PDM_TransferReceiveNonBlocking(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_t *xfer)
543 {
544 assert(handle != NULL);
545
546 /* Check if the queue is full */
547 if (handle->pdmQueue[handle->queueUser].data != NULL)
548 {
549 return kStatus_PDM_QueueFull;
550 }
551
552 /* Add into queue */
553 handle->transferSize[handle->queueUser] = xfer->dataSize;
554 handle->pdmQueue[handle->queueUser].data = xfer->data;
555 handle->pdmQueue[handle->queueUser].dataSize = xfer->dataSize;
556 handle->queueUser = (handle->queueUser + 1U) % PDM_XFER_QUEUE_SIZE;
557
558 /* Set state to busy */
559 handle->state = kStatus_PDM_Busy;
560
561 /* Enable interrupt */
562 PDM_EnableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable);
563
564 PDM_Enable(base, true);
565
566 return kStatus_Success;
567 }
568
569 /*!
570 * brief Aborts the current IRQ receive.
571 *
572 * note This API can be called when an interrupt non-blocking transfer initiates
573 * to abort the transfer early.
574 *
575 * param base PDM base pointer
576 * param handle Pointer to the pdm_handle_t structure which stores the transfer state.
577 */
PDM_TransferAbortReceive(PDM_Type * base,pdm_handle_t * handle)578 void PDM_TransferAbortReceive(PDM_Type *base, pdm_handle_t *handle)
579 {
580 assert(handle != NULL);
581
582 /* Use FIFO request interrupt and fifo error */
583 PDM_DisableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable | (uint32_t)kPDM_ErrorInterruptEnable);
584 PDM_Enable(base, false);
585 handle->state = kStatus_PDM_Idle;
586 /* Clear the queue */
587 (void)memset(handle->pdmQueue, 0, sizeof(pdm_transfer_t) * PDM_XFER_QUEUE_SIZE);
588 handle->queueDriver = 0;
589 handle->queueUser = 0;
590 }
591
592 /*!
593 * brief Tx interrupt handler.
594 *
595 * param base PDM base pointer.
596 * param handle Pointer to the pdm_handle_t structure.
597 */
PDM_TransferHandleIRQ(PDM_Type * base,pdm_handle_t * handle)598 void PDM_TransferHandleIRQ(PDM_Type *base, pdm_handle_t *handle)
599 {
600 assert(handle != NULL);
601
602 #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
603 uint32_t status = 0U;
604
605 #if (defined(FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ) && (FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ == 1U))
606 if (PDM_GetStatus(base) & PDM_STAT_LOWFREQF_MASK)
607 {
608 PDM_ClearStatus(base, PDM_STAT_LOWFREQF_MASK);
609 if (handle->callback != NULL)
610 {
611 (handle->callback)(base, handle, kStatus_PDM_CLK_LOW, handle->userData);
612 }
613 }
614 #endif
615 status = PDM_GetFifoStatus(base);
616 if (status != 0U)
617 {
618 PDM_ClearFIFOStatus(base, status);
619 if (handle->callback != NULL)
620 {
621 (handle->callback)(base, handle, kStatus_PDM_FIFO_ERROR, handle->userData);
622 }
623 }
624
625 #if !(defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL)
626 status = PDM_GetOutputStatus(base);
627 if (status != 0U)
628 {
629 PDM_ClearOutputStatus(base, status);
630 if (handle->callback != NULL)
631 {
632 (handle->callback)(base, handle, kStatus_PDM_Output_ERROR, handle->userData);
633 }
634 }
635 #endif
636 #endif
637
638 /* Handle transfer */
639 if (((base->STAT & 0xFFU) != 0U) && (handle->channelNums != 0U) &&
640 ((base->CTRL_1 & PDM_CTRL_1_DISEL_MASK) == (0x2UL << PDM_CTRL_1_DISEL_SHIFT)))
641 {
642 PDM_ClearStatus(base, 0xFFU);
643 /* Judge if the data need to transmit is less than space */
644 uint8_t size = (uint8_t)MIN((handle->pdmQueue[handle->queueDriver].dataSize),
645 ((uint32_t)handle->watermark * handle->channelNums * handle->format));
646
647 PDM_ReadFifo(base, handle->startChannel, handle->channelNums,
648 (uint8_t *)(uint32_t)handle->pdmQueue[handle->queueDriver].data,
649 ((size_t)size / handle->channelNums / handle->format), handle->format);
650
651 /* Update the internal counter */
652 handle->pdmQueue[handle->queueDriver].dataSize -= size;
653 handle->pdmQueue[handle->queueDriver].data = &(handle->pdmQueue[handle->queueDriver].data[size]);
654 }
655
656 /* If finished a block, call the callback function */
657 if (handle->pdmQueue[handle->queueDriver].dataSize == 0U)
658 {
659 handle->pdmQueue[handle->queueDriver].data = NULL;
660 handle->queueDriver = (handle->queueDriver + 1U) % PDM_XFER_QUEUE_SIZE;
661 if (handle->callback != NULL)
662 {
663 (handle->callback)(base, handle, kStatus_PDM_Idle, handle->userData);
664 }
665 }
666
667 /* If all data finished, just stop the transfer */
668 if (handle->pdmQueue[handle->queueDriver].data == NULL)
669 {
670 PDM_TransferAbortReceive(base, handle);
671 }
672 }
673
674 #if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD)
675 /*!
676 * brief set HWVAD in envelope based mode .
677 * Recommand configurations,
678 * code
679 * static const pdm_hwvad_config_t hwvadConfig = {
680 * .channel = 0,
681 * .initializeTime = 10U,
682 * .cicOverSampleRate = 0U,
683 * .inputGain = 0U,
684 * .frameTime = 10U,
685 * .cutOffFreq = kPDM_HwvadHpfBypassed,
686 * .enableFrameEnergy = false,
687 * .enablePreFilter = true,
688 };
689
690 * static const pdm_hwvad_noise_filter_t noiseFilterConfig = {
691 * .enableAutoNoiseFilter = false,
692 * .enableNoiseMin = true,
693 * .enableNoiseDecimation = true,
694 * .noiseFilterAdjustment = 0U,
695 * .noiseGain = 7U,
696 * .enableNoiseDetectOR = true,
697 * };
698 * code
699 * param base PDM base pointer.
700 * param hwvadConfig internal filter status.
701 * param noiseConfig Voice activity detector noise filter configure structure pointer.
702 * param zcdConfig Voice activity detector zero cross detector configure structure pointer .
703 * param signalGain signal gain value.
704 */
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)705 void PDM_SetHwvadInEnvelopeBasedMode(PDM_Type *base,
706 const pdm_hwvad_config_t *hwvadConfig,
707 const pdm_hwvad_noise_filter_t *noiseConfig,
708 const pdm_hwvad_zero_cross_detector_t *zcdConfig,
709 uint32_t signalGain)
710 {
711 assert(hwvadConfig != NULL);
712 assert(noiseConfig != NULL);
713
714 uint32_t i = 0U;
715
716 PDM_SetHwvadConfig(base, hwvadConfig);
717 PDM_SetHwvadSignalFilterConfig(base, true, signalGain);
718 PDM_SetHwvadNoiseFilterConfig(base, noiseConfig);
719 PDM_EnableHwvad(base, true);
720
721 if (NULL != zcdConfig)
722 {
723 PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig);
724 }
725
726 PDM_Enable(base, true);
727
728 while (PDM_GetHwvadInitialFlag(base) != 0U)
729 {
730 }
731
732 for (i = 0; i < 3U; i++)
733 {
734 /* set HWVAD interal filter stauts initial */
735 PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterInitial);
736 }
737
738 PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterNormalOperation);
739 }
740
741 /*!
742 * brief set HWVAD in energy based mode .
743 * Recommand configurations,
744 * code
745 * static const pdm_hwvad_config_t hwvadConfig = {
746 * .channel = 0,
747 * .initializeTime = 10U,
748 * .cicOverSampleRate = 0U,
749 * .inputGain = 0U,
750 * .frameTime = 10U,
751 * .cutOffFreq = kPDM_HwvadHpfBypassed,
752 * .enableFrameEnergy = true,
753 * .enablePreFilter = true,
754 };
755
756 * static const pdm_hwvad_noise_filter_t noiseFilterConfig = {
757 * .enableAutoNoiseFilter = true,
758 * .enableNoiseMin = false,
759 * .enableNoiseDecimation = false,
760 * .noiseFilterAdjustment = 0U,
761 * .noiseGain = 7U,
762 * .enableNoiseDetectOR = false,
763 * };
764 * code
765 * param base PDM base pointer.
766 * param hwvadConfig internal filter status.
767 * param noiseConfig Voice activity detector noise filter configure structure pointer.
768 * param zcdConfig Voice activity detector zero cross detector configure structure pointer .
769 * param signalGain signal gain value, signal gain value should be properly according to application.
770 */
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)771 void PDM_SetHwvadInEnergyBasedMode(PDM_Type *base,
772 const pdm_hwvad_config_t *hwvadConfig,
773 const pdm_hwvad_noise_filter_t *noiseConfig,
774 const pdm_hwvad_zero_cross_detector_t *zcdConfig,
775 uint32_t signalGain)
776 {
777 assert(hwvadConfig != NULL);
778 assert(noiseConfig != NULL);
779
780 PDM_SetHwvadConfig(base, hwvadConfig);
781 /* signal filter need to disable, but signal gain value should be set */
782 base->VAD0_SCONFIG = PDM_VAD0_SCONFIG_VADSGAIN(signalGain);
783 PDM_SetHwvadNoiseFilterConfig(base, noiseConfig);
784 PDM_EnableHwvad(base, true);
785
786 if (NULL != zcdConfig)
787 {
788 PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig);
789 }
790
791 PDM_Enable(base, true);
792 }
793
794 /*!
795 * brief Configure voice activity detector.
796 *
797 * param base PDM base pointer
798 * param config Voice activity detector configure structure pointer .
799 */
PDM_SetHwvadConfig(PDM_Type * base,const pdm_hwvad_config_t * config)800 void PDM_SetHwvadConfig(PDM_Type *base, const pdm_hwvad_config_t *config)
801 {
802 assert(config != NULL);
803
804 uint32_t ctrl1 = base->VAD0_CTRL_1;
805
806 /* Configure VAD0_CTRL_1 register */
807 ctrl1 &= ~(PDM_VAD0_CTRL_1_VADCHSEL_MASK | PDM_VAD0_CTRL_1_VADCICOSR_MASK | PDM_VAD0_CTRL_1_VADINITT_MASK);
808 ctrl1 |= (PDM_VAD0_CTRL_1_VADCHSEL(config->channel) | PDM_VAD0_CTRL_1_VADCICOSR(config->cicOverSampleRate) |
809 PDM_VAD0_CTRL_1_VADINITT(config->initializeTime));
810 base->VAD0_CTRL_1 = ctrl1;
811
812 /* Configure VAD0_CTRL_2 register */
813 base->VAD0_CTRL_2 =
814 (PDM_VAD0_CTRL_2_VADFRENDIS((config->enableFrameEnergy == true) ? 0U : 1U) |
815 PDM_VAD0_CTRL_2_VADPREFEN(config->enablePreFilter) | PDM_VAD0_CTRL_2_VADFRAMET(config->frameTime) |
816 PDM_VAD0_CTRL_2_VADINPGAIN(config->inputGain) | PDM_VAD0_CTRL_2_VADHPF(config->cutOffFreq));
817 }
818
819 /*!
820 * brief Configure voice activity detector signal filter.
821 *
822 * param base PDM base pointer
823 * param enableMaxBlock If signal maximum block enabled.
824 * param signalGain Gain value for the signal energy.
825 */
PDM_SetHwvadSignalFilterConfig(PDM_Type * base,bool enableMaxBlock,uint32_t signalGain)826 void PDM_SetHwvadSignalFilterConfig(PDM_Type *base, bool enableMaxBlock, uint32_t signalGain)
827 {
828 uint32_t signalConfig = base->VAD0_SCONFIG;
829
830 signalConfig &= ~(PDM_VAD0_SCONFIG_VADSMAXEN_MASK | PDM_VAD0_SCONFIG_VADSGAIN_MASK);
831 signalConfig |= (PDM_VAD0_SCONFIG_VADSMAXEN(enableMaxBlock) | PDM_VAD0_SCONFIG_VADSGAIN(signalGain)) |
832 PDM_VAD0_SCONFIG_VADSFILEN_MASK;
833 base->VAD0_SCONFIG = signalConfig;
834 }
835
836 /*!
837 * brief Configure voice activity detector noise filter.
838 *
839 * param base PDM base pointer
840 * param config Voice activity detector noise filter configure structure pointer .
841 */
PDM_SetHwvadNoiseFilterConfig(PDM_Type * base,const pdm_hwvad_noise_filter_t * config)842 void PDM_SetHwvadNoiseFilterConfig(PDM_Type *base, const pdm_hwvad_noise_filter_t *config)
843 {
844 assert(config != NULL);
845
846 base->VAD0_NCONFIG =
847 (PDM_VAD0_NCONFIG_VADNFILAUTO(config->enableAutoNoiseFilter) |
848 PDM_VAD0_NCONFIG_VADNOREN(config->enableNoiseDetectOR) | PDM_VAD0_NCONFIG_VADNMINEN(config->enableNoiseMin) |
849 PDM_VAD0_NCONFIG_VADNDECEN(config->enableNoiseDecimation) |
850 PDM_VAD0_NCONFIG_VADNFILADJ(config->noiseFilterAdjustment) | PDM_VAD0_NCONFIG_VADNGAIN(config->noiseGain));
851 }
852
853 /*!
854 * brief Configure voice activity detector zero cross detector.
855 *
856 * param base PDM base pointer
857 * param config Voice activity detector zero cross detector configure structure pointer .
858 */
PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type * base,const pdm_hwvad_zero_cross_detector_t * config)859 void PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type *base, const pdm_hwvad_zero_cross_detector_t *config)
860 {
861 assert(config != NULL);
862
863 uint32_t zcd = (base->VAD0_ZCD & (~(PDM_VAD0_ZCD_VADZCDTH_MASK | PDM_VAD0_ZCD_VADZCDADJ_MASK |
864 PDM_VAD0_ZCD_VADZCDAUTO_MASK | PDM_VAD0_ZCD_VADZCDAND_MASK)));
865
866 zcd |= (PDM_VAD0_ZCD_VADZCDTH(config->threshold) | PDM_VAD0_ZCD_VADZCDADJ(config->adjustmentThreshold) |
867 PDM_VAD0_ZCD_VADZCDAUTO(config->enableAutoThreshold) | PDM_VAD0_ZCD_VADZCDAND(config->zcdAnd)) |
868 PDM_VAD0_ZCD_VADZCDEN_MASK;
869
870 base->VAD0_ZCD = zcd;
871 }
872
873 /*!
874 * brief Enable/Disable hwvad callback.
875
876 * This function enable/disable the hwvad interrupt for the selected PDM peripheral.
877 *
878 * param base Base address of the PDM peripheral.
879 * param vadCallback callback Pointer to store callback function, should be NULL when disable.
880 * param userData user data.
881 * param enable true is enable, false is disable.
882 * retval None.
883 */
PDM_EnableHwvadInterruptCallback(PDM_Type * base,pdm_hwvad_callback_t vadCallback,void * userData,bool enable)884 void PDM_EnableHwvadInterruptCallback(PDM_Type *base, pdm_hwvad_callback_t vadCallback, void *userData, bool enable)
885 {
886 uint32_t instance = PDM_GetInstance(base);
887
888 if (enable)
889 {
890 PDM_EnableHwvadInterrupts(base, (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable);
891 NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn);
892 (void)EnableIRQ(PDM_HWVAD_EVENT_IRQn);
893 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
894 NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn);
895 (void)EnableIRQ(PDM_HWVAD_ERROR_IRQn);
896 #endif
897 s_pdm_hwvad_notification[instance].callback = vadCallback;
898 s_pdm_hwvad_notification[instance].userData = userData;
899 }
900 else
901 {
902 PDM_DisableHwvadInterrupts(base,
903 (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable);
904 (void)DisableIRQ(PDM_HWVAD_EVENT_IRQn);
905 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
906 (void)DisableIRQ(PDM_HWVAD_ERROR_IRQn);
907 NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn);
908 #endif
909 s_pdm_hwvad_notification[instance].callback = NULL;
910 s_pdm_hwvad_notification[instance].userData = NULL;
911 NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn);
912 }
913 }
914
915 #if (defined PDM)
916 void PDM_HWVAD_EVENT_DriverIRQHandler(void);
PDM_HWVAD_EVENT_DriverIRQHandler(void)917 void PDM_HWVAD_EVENT_DriverIRQHandler(void)
918 {
919 if ((PDM_GetHwvadInterruptStatusFlags(PDM) & (uint32_t)kPDM_HwvadStatusVoiceDetectFlag) != 0U)
920 {
921 PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusVoiceDetectFlag);
922 if (s_pdm_hwvad_notification[0].callback != NULL)
923 {
924 s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_VoiceDetected, s_pdm_hwvad_notification[0].userData);
925 }
926 }
927 #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
928 else
929 {
930 PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation);
931 if (s_pdm_hwvad_notification[0].callback != NULL)
932 {
933 s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData);
934 }
935 }
936 #endif
937 SDK_ISR_EXIT_BARRIER;
938 }
939
940 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
941 void PDM_HWVAD_ERROR_DriverIRQHandler(void);
PDM_HWVAD_ERROR_DriverIRQHandler(void)942 void PDM_HWVAD_ERROR_DriverIRQHandler(void)
943 {
944 PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation);
945 if (s_pdm_hwvad_notification[0].callback != NULL)
946 {
947 s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData);
948 }
949 SDK_ISR_EXIT_BARRIER;
950 }
951 #endif
952 #endif
953 #endif
954
955 #if defined(PDM)
956 void PDM_EVENT_DriverIRQHandler(void);
PDM_EVENT_DriverIRQHandler(void)957 void PDM_EVENT_DriverIRQHandler(void)
958 {
959 assert(s_pdmHandle[0] != NULL);
960 s_pdmIsr(PDM, s_pdmHandle[0]);
961 SDK_ISR_EXIT_BARRIER;
962 }
963 #endif
964