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 = (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT) + 1U - 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 = (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT) + 1U -
266 (osr & (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT));
267 #endif
268 uint32_t regDiv = clkDiv >> 1U;
269
270 switch (qualityMode)
271 {
272 case kPDM_QualityModeHigh:
273 regDiv <<= 1U;
274 break;
275 case kPDM_QualityModeLow:
276 case kPDM_QualityModeVeryLow1:
277 regDiv >>= 1U;
278 break;
279 case kPDM_QualityModeVeryLow2:
280 regDiv >>= 2U;
281 break;
282 default:
283 assert(false);
284 break;
285 }
286
287 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
288 if (PDM_ValidateSrcClockRate(enableChannelMask, qualityMode, realOsr, regDiv) == kStatus_Fail)
289 {
290 return kStatus_Fail;
291 }
292 #endif
293
294 assert(regDiv <= PDM_CTRL_2_CLKDIV_MASK);
295 base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv);
296
297 return kStatus_Success;
298 }
299
300 /*!
301 * brief Initializes the PDM peripheral.
302 *
303 * Ungates the PDM clock, resets the module, and configures PDM with a configuration structure.
304 * The configuration structure can be custom filled or set with default values by
305 * PDM_GetDefaultConfig().
306 *
307 * note This API should be called at the beginning of the application to use
308 * the PDM driver. Otherwise, accessing the PDM module can cause a hard fault
309 * because the clock is not enabled.
310 *
311 * param base PDM base pointer
312 * param config PDM configuration structure.
313 */
PDM_Init(PDM_Type * base,const pdm_config_t * config)314 void PDM_Init(PDM_Type *base, const pdm_config_t *config)
315 {
316 assert(config != NULL);
317 assert(config->fifoWatermark <= PDM_FIFO_CTRL_FIFOWMK_MASK);
318
319 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
320 /* Enable the PDM clock */
321 CLOCK_EnableClock(s_pdmClock[PDM_GetInstance(base)]);
322 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
323 CLOCK_EnableClock(s_pdmFilterClock[PDM_GetInstance(base)]);
324 #endif
325 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
326
327 /* Enable the module and disable the interface/all channel */
328 base->CTRL_1 &=
329 ~(PDM_CTRL_1_MDIS_MASK | PDM_CTRL_1_PDMIEN_MASK | PDM_CTRL_1_ERREN_MASK | (uint32_t)kPDM_EnableChannelAll);
330
331 /* wait all filter stopped */
332 while ((base->STAT & PDM_STAT_BSY_FIL_MASK) != 0U)
333 {
334 }
335
336 /* software reset */
337 base->CTRL_1 |= PDM_CTRL_1_SRES_MASK;
338
339 /* Set the configure settings */
340 #if !(defined(FSL_FEATURE_PDM_HAS_NO_DOZEN) && FSL_FEATURE_PDM_HAS_NO_DOZEN)
341 PDM_EnableDoze(base, config->enableDoze);
342 #endif
343 base->CTRL_2 = (base->CTRL_2 & (~(PDM_CTRL_2_CICOSR_MASK | PDM_CTRL_2_QSEL_MASK))) |
344 PDM_CTRL_2_CICOSR(config->cicOverSampleRate) | PDM_CTRL_2_QSEL(config->qualityMode);
345
346 #if defined(FSL_FEATURE_PDM_HAS_DECIMATION_FILTER_BYPASS) && FSL_FEATURE_PDM_HAS_DECIMATION_FILTER_BYPASS
347 base->CTRL_2 = (base->CTRL_2 & ~PDM_CTRL_2_DEC_BYPASS_MASK) | PDM_CTRL_2_DEC_BYPASS(config->enableFilterBypass);
348 #endif
349 /* Set the watermark */
350 base->FIFO_CTRL = PDM_FIFO_CTRL_FIFOWMK(config->fifoWatermark);
351 }
352
353 /*!
354 * brief De-initializes the PDM peripheral.
355 *
356 * This API gates the PDM clock. The PDM module can't operate unless PDM_Init
357 * is called to enable the clock.
358 *
359 * param base PDM base pointer
360 */
PDM_Deinit(PDM_Type * base)361 void PDM_Deinit(PDM_Type *base)
362 {
363 /* disable PDM interface */
364 PDM_DisableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable | (uint32_t)kPDM_ErrorInterruptEnable);
365 PDM_Enable(base, false);
366
367 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
368 CLOCK_DisableClock(s_pdmClock[PDM_GetInstance(base)]);
369 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
370 CLOCK_DisableClock(s_pdmFilterClock[PDM_GetInstance(base)]);
371 #endif
372 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
373 }
374
375 /*!
376 * brief Enables the PDM interrupt requests.
377 *
378 * param base PDM base pointer
379 * param mask interrupt source
380 * The parameter can be a combination of the following sources if defined.
381 * arg kPDM_ErrorInterruptEnable
382 * arg kPDM_FIFOInterruptEnable
383 */
PDM_EnableInterrupts(PDM_Type * base,uint32_t mask)384 void PDM_EnableInterrupts(PDM_Type *base, uint32_t mask)
385 {
386 if ((mask & (uint32_t)kPDM_FIFOInterruptEnable) != 0U)
387 {
388 base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DISEL_MASK)) | (uint32_t)kPDM_FIFOInterruptEnable;
389 }
390 if ((mask & (uint32_t)kPDM_ErrorInterruptEnable) != 0U)
391 {
392 base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_ERREN_MASK)) | (uint32_t)kPDM_ErrorInterruptEnable;
393 }
394 }
395
396 /*!
397 * brief PDM one channel configurations.
398 *
399 * param base PDM base pointer
400 * param config PDM channel configurations.
401 * param channel channel number.
402 * after completing the current frame in debug mode.
403 */
PDM_SetChannelConfig(PDM_Type * base,uint32_t channel,const pdm_channel_config_t * config)404 void PDM_SetChannelConfig(PDM_Type *base, uint32_t channel, const pdm_channel_config_t *config)
405 {
406 assert(config != NULL);
407 assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
408
409 uint32_t dcCtrl = 0U;
410
411 #if (defined(FSL_FEATURE_PDM_HAS_DC_OUT_CTRL) && (FSL_FEATURE_PDM_HAS_DC_OUT_CTRL))
412 dcCtrl = base->DC_OUT_CTRL;
413 /* configure gain and cut off freq */
414 dcCtrl &= ~((uint32_t)PDM_DC_OUT_CTRL_DCCONFIG0_MASK << (channel << 1U));
415 dcCtrl |= (uint32_t)config->outputCutOffFreq << (channel << 1U);
416 base->DC_OUT_CTRL = dcCtrl;
417 #endif
418
419 #if !(defined(FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED) && (FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED))
420 dcCtrl = base->DC_CTRL;
421 /* configure gain and cut off freq */
422 dcCtrl &= ~((uint32_t)PDM_DC_CTRL_DCCONFIG0_MASK << (channel << 1U));
423 dcCtrl |= (uint32_t)config->cutOffFreq << (channel << 1U);
424 base->DC_CTRL = dcCtrl;
425 #endif
426
427 PDM_SetChannelGain(base, channel, config->gain);
428
429 /* enable channel */
430 base->CTRL_1 |= 1UL << channel;
431 }
432
433 /*!
434 * brief Set the PDM channel gain.
435 *
436 * Please note for different quality mode, the valid gain value is different, reference RM for detail.
437 * param base PDM base pointer.
438 * param channel PDM channel index.
439 * param gain channel gain, the register gain value range is 0 - 15.
440 */
PDM_SetChannelGain(PDM_Type * base,uint32_t channel,pdm_df_output_gain_t gain)441 void PDM_SetChannelGain(PDM_Type *base, uint32_t channel, pdm_df_output_gain_t gain)
442 {
443 assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
444
445 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
446 uint32_t outCtrl = base->RANGE_CTRL;
447 #else
448 uint32_t outCtrl = base->OUT_CTRL;
449 #endif
450
451 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
452 outCtrl &= ~((uint32_t)PDM_RANGE_CTRL_RANGEADJ0_MASK << (channel << 2U));
453 #else
454 outCtrl &= ~((uint32_t)PDM_OUT_CTRL_OUTGAIN0_MASK << (channel << 2U));
455 #endif
456
457 outCtrl |= (uint32_t)gain << (channel << 2U);
458
459 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
460 base->RANGE_CTRL = outCtrl;
461 #else
462 base->OUT_CTRL = outCtrl;
463 #endif
464 }
465
466 /*!
467 * brief PDM set channel transfer config.
468 *
469 * param base PDM base pointer.
470 * param handle PDM handle pointer.
471 * param channel PDM channel.
472 * param config channel config.
473 * param format data format.
474 */
PDM_TransferSetChannelConfig(PDM_Type * base,pdm_handle_t * handle,uint32_t channel,const pdm_channel_config_t * config,uint32_t format)475 status_t PDM_TransferSetChannelConfig(
476 PDM_Type *base, pdm_handle_t *handle, uint32_t channel, const pdm_channel_config_t *config, uint32_t format)
477 {
478 assert(handle != NULL);
479
480 PDM_SetChannelConfig(base, channel, config);
481
482 handle->format = format;
483
484 if (handle->channelNums == 0U)
485 {
486 handle->startChannel = (uint8_t)channel;
487 }
488
489 handle->channelNums++;
490
491 if (handle->channelNums > (uint8_t)FSL_FEATURE_PDM_CHANNEL_NUM)
492 {
493 return kStatus_PDM_ChannelConfig_Failed;
494 }
495
496 return kStatus_Success;
497 }
498
499 /*!
500 * brief Initializes the PDM handle.
501 *
502 * This function initializes the handle for the PDM transactional APIs. Call
503 * this function once to get the handle initialized.
504 *
505 * param base PDM base pointer.
506 * param handle PDM handle pointer.
507 * param callback Pointer to the user callback function.
508 * param userData User parameter passed to the callback function.
509 */
PDM_TransferCreateHandle(PDM_Type * base,pdm_handle_t * handle,pdm_transfer_callback_t callback,void * userData)510 void PDM_TransferCreateHandle(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_callback_t callback, void *userData)
511 {
512 assert(handle != NULL);
513
514 /* Zero the handle */
515 (void)memset(handle, 0, sizeof(*handle));
516
517 s_pdmHandle[PDM_GetInstance(base)] = handle;
518
519 handle->callback = callback;
520 handle->userData = userData;
521 handle->watermark = (uint8_t)(base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK);
522
523 /* Set the isr pointer */
524 s_pdmIsr = PDM_TransferHandleIRQ;
525
526 /* Enable RX event IRQ */
527 (void)EnableIRQ(PDM_EVENT_IRQn);
528 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
529 /* Enable FIFO error IRQ */
530 (void)EnableIRQ(PDM_ERROR_IRQn);
531 #endif
532 }
533
534 /*!
535 * brief Performs an interrupt non-blocking receive transfer on PDM.
536 *
537 * note This API returns immediately after the transfer initiates.
538 * Call the PDM_RxGetTransferStatusIRQ to poll the transfer status and check whether
539 * the transfer is finished. If the return status is not kStatus_PDM_Busy, the transfer
540 * is finished.
541 *
542 * param base PDM base pointer
543 * param handle Pointer to the pdm_handle_t structure which stores the transfer state.
544 * param xfer Pointer to the pdm_transfer_t structure.
545 * retval kStatus_Success Successfully started the data receive.
546 * retval kStatus_PDM_Busy Previous receive still not finished.
547 */
PDM_TransferReceiveNonBlocking(PDM_Type * base,pdm_handle_t * handle,pdm_transfer_t * xfer)548 status_t PDM_TransferReceiveNonBlocking(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_t *xfer)
549 {
550 assert(handle != NULL);
551
552 /* Check if the queue is full */
553 if (handle->pdmQueue[handle->queueUser].data != NULL)
554 {
555 return kStatus_PDM_QueueFull;
556 }
557
558 /* Add into queue */
559 handle->transferSize[handle->queueUser] = xfer->dataSize;
560 handle->pdmQueue[handle->queueUser].data = xfer->data;
561 handle->pdmQueue[handle->queueUser].dataSize = xfer->dataSize;
562 handle->queueUser = (handle->queueUser + 1U) % PDM_XFER_QUEUE_SIZE;
563
564 /* Set state to busy */
565 handle->state = kStatus_PDM_Busy;
566
567 /* Enable interrupt */
568 PDM_EnableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable);
569
570 PDM_Enable(base, true);
571
572 return kStatus_Success;
573 }
574
575 /*!
576 * brief Aborts the current IRQ receive.
577 *
578 * note This API can be called when an interrupt non-blocking transfer initiates
579 * to abort the transfer early.
580 *
581 * param base PDM base pointer
582 * param handle Pointer to the pdm_handle_t structure which stores the transfer state.
583 */
PDM_TransferAbortReceive(PDM_Type * base,pdm_handle_t * handle)584 void PDM_TransferAbortReceive(PDM_Type *base, pdm_handle_t *handle)
585 {
586 assert(handle != NULL);
587
588 /* Use FIFO request interrupt and fifo error */
589 PDM_DisableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable | (uint32_t)kPDM_ErrorInterruptEnable);
590 PDM_Enable(base, false);
591 handle->state = kStatus_PDM_Idle;
592 /* Clear the queue */
593 (void)memset(handle->pdmQueue, 0, sizeof(pdm_transfer_t) * PDM_XFER_QUEUE_SIZE);
594 handle->queueDriver = 0;
595 handle->queueUser = 0;
596 }
597
598 /*!
599 * brief Tx interrupt handler.
600 *
601 * param base PDM base pointer.
602 * param handle Pointer to the pdm_handle_t structure.
603 */
PDM_TransferHandleIRQ(PDM_Type * base,pdm_handle_t * handle)604 void PDM_TransferHandleIRQ(PDM_Type *base, pdm_handle_t *handle)
605 {
606 assert(handle != NULL);
607
608 #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
609 uint32_t status = 0U;
610
611 #if (defined(FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ) && (FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ == 1U))
612 if (PDM_GetStatus(base) & PDM_STAT_LOWFREQF_MASK)
613 {
614 PDM_ClearStatus(base, PDM_STAT_LOWFREQF_MASK);
615 if (handle->callback != NULL)
616 {
617 (handle->callback)(base, handle, kStatus_PDM_CLK_LOW, handle->userData);
618 }
619 }
620 #endif
621 status = PDM_GetFifoStatus(base);
622 if (status != 0U)
623 {
624 PDM_ClearFIFOStatus(base, status);
625 if (handle->callback != NULL)
626 {
627 (handle->callback)(base, handle, kStatus_PDM_FIFO_ERROR, handle->userData);
628 }
629 }
630
631 #if !(defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL)
632 status = PDM_GetOutputStatus(base);
633 if (status != 0U)
634 {
635 PDM_ClearOutputStatus(base, status);
636 if (handle->callback != NULL)
637 {
638 (handle->callback)(base, handle, kStatus_PDM_Output_ERROR, handle->userData);
639 }
640 }
641 #endif
642 #endif
643
644 /* Handle transfer */
645 if (((base->STAT & 0xFFU) != 0U) && (handle->channelNums != 0U) &&
646 ((base->CTRL_1 & PDM_CTRL_1_DISEL_MASK) == (0x2UL << PDM_CTRL_1_DISEL_SHIFT)))
647 {
648 PDM_ClearStatus(base, 0xFFU);
649 /* Judge if the data need to transmit is less than space */
650 uint8_t size = (uint8_t)MIN((handle->pdmQueue[handle->queueDriver].dataSize),
651 ((uint32_t)handle->watermark * handle->channelNums * handle->format));
652
653 PDM_ReadFifo(base, handle->startChannel, handle->channelNums,
654 (uint8_t *)(uint32_t)handle->pdmQueue[handle->queueDriver].data,
655 ((size_t)size / handle->channelNums / handle->format), handle->format);
656
657 /* Update the internal counter */
658 handle->pdmQueue[handle->queueDriver].dataSize -= size;
659 handle->pdmQueue[handle->queueDriver].data = &(handle->pdmQueue[handle->queueDriver].data[size]);
660 }
661
662 /* If finished a block, call the callback function */
663 if (handle->pdmQueue[handle->queueDriver].dataSize == 0U)
664 {
665 handle->pdmQueue[handle->queueDriver].data = NULL;
666 handle->queueDriver = (handle->queueDriver + 1U) % PDM_XFER_QUEUE_SIZE;
667 if (handle->callback != NULL)
668 {
669 (handle->callback)(base, handle, kStatus_PDM_Idle, handle->userData);
670 }
671 }
672
673 /* If all data finished, just stop the transfer */
674 if (handle->pdmQueue[handle->queueDriver].data == NULL)
675 {
676 PDM_TransferAbortReceive(base, handle);
677 }
678 }
679
680 #if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD)
681 /*!
682 * brief set HWVAD in envelope based mode .
683 * Recommand configurations,
684 * code
685 * static const pdm_hwvad_config_t hwvadConfig = {
686 * .channel = 0,
687 * .initializeTime = 10U,
688 * .cicOverSampleRate = 0U,
689 * .inputGain = 0U,
690 * .frameTime = 10U,
691 * .cutOffFreq = kPDM_HwvadHpfBypassed,
692 * .enableFrameEnergy = false,
693 * .enablePreFilter = true,
694 };
695
696 * static const pdm_hwvad_noise_filter_t noiseFilterConfig = {
697 * .enableAutoNoiseFilter = false,
698 * .enableNoiseMin = true,
699 * .enableNoiseDecimation = true,
700 * .noiseFilterAdjustment = 0U,
701 * .noiseGain = 7U,
702 * .enableNoiseDetectOR = true,
703 * };
704 * code
705 * param base PDM base pointer.
706 * param hwvadConfig internal filter status.
707 * param noiseConfig Voice activity detector noise filter configure structure pointer.
708 * param zcdConfig Voice activity detector zero cross detector configure structure pointer .
709 * param signalGain signal gain value.
710 */
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)711 void PDM_SetHwvadInEnvelopeBasedMode(PDM_Type *base,
712 const pdm_hwvad_config_t *hwvadConfig,
713 const pdm_hwvad_noise_filter_t *noiseConfig,
714 const pdm_hwvad_zero_cross_detector_t *zcdConfig,
715 uint32_t signalGain)
716 {
717 assert(hwvadConfig != NULL);
718 assert(noiseConfig != NULL);
719
720 uint32_t i = 0U;
721
722 PDM_SetHwvadConfig(base, hwvadConfig);
723 PDM_SetHwvadSignalFilterConfig(base, true, signalGain);
724 PDM_SetHwvadNoiseFilterConfig(base, noiseConfig);
725 PDM_EnableHwvad(base, true);
726
727 if (NULL != zcdConfig)
728 {
729 PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig);
730 }
731
732 PDM_Enable(base, true);
733
734 while (PDM_GetHwvadInitialFlag(base) != 0U)
735 {
736 }
737
738 for (i = 0; i < 3U; i++)
739 {
740 /* set HWVAD interal filter stauts initial */
741 PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterInitial);
742 }
743
744 PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterNormalOperation);
745 }
746
747 /*!
748 * brief set HWVAD in energy based mode .
749 * Recommand configurations,
750 * code
751 * static const pdm_hwvad_config_t hwvadConfig = {
752 * .channel = 0,
753 * .initializeTime = 10U,
754 * .cicOverSampleRate = 0U,
755 * .inputGain = 0U,
756 * .frameTime = 10U,
757 * .cutOffFreq = kPDM_HwvadHpfBypassed,
758 * .enableFrameEnergy = true,
759 * .enablePreFilter = true,
760 };
761
762 * static const pdm_hwvad_noise_filter_t noiseFilterConfig = {
763 * .enableAutoNoiseFilter = true,
764 * .enableNoiseMin = false,
765 * .enableNoiseDecimation = false,
766 * .noiseFilterAdjustment = 0U,
767 * .noiseGain = 7U,
768 * .enableNoiseDetectOR = false,
769 * };
770 * code
771 * param base PDM base pointer.
772 * param hwvadConfig internal filter status.
773 * param noiseConfig Voice activity detector noise filter configure structure pointer.
774 * param zcdConfig Voice activity detector zero cross detector configure structure pointer .
775 * param signalGain signal gain value, signal gain value should be properly according to application.
776 */
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)777 void PDM_SetHwvadInEnergyBasedMode(PDM_Type *base,
778 const pdm_hwvad_config_t *hwvadConfig,
779 const pdm_hwvad_noise_filter_t *noiseConfig,
780 const pdm_hwvad_zero_cross_detector_t *zcdConfig,
781 uint32_t signalGain)
782 {
783 assert(hwvadConfig != NULL);
784 assert(noiseConfig != NULL);
785
786 PDM_SetHwvadConfig(base, hwvadConfig);
787 /* signal filter need to disable, but signal gain value should be set */
788 base->VAD0_SCONFIG = PDM_VAD0_SCONFIG_VADSGAIN(signalGain);
789 PDM_SetHwvadNoiseFilterConfig(base, noiseConfig);
790 PDM_EnableHwvad(base, true);
791
792 if (NULL != zcdConfig)
793 {
794 PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig);
795 }
796
797 PDM_Enable(base, true);
798 }
799
800 /*!
801 * brief Configure voice activity detector.
802 *
803 * param base PDM base pointer
804 * param config Voice activity detector configure structure pointer .
805 */
PDM_SetHwvadConfig(PDM_Type * base,const pdm_hwvad_config_t * config)806 void PDM_SetHwvadConfig(PDM_Type *base, const pdm_hwvad_config_t *config)
807 {
808 assert(config != NULL);
809
810 uint32_t ctrl1 = base->VAD0_CTRL_1;
811
812 /* Configure VAD0_CTRL_1 register */
813 ctrl1 &= ~(PDM_VAD0_CTRL_1_VADCHSEL_MASK | PDM_VAD0_CTRL_1_VADCICOSR_MASK | PDM_VAD0_CTRL_1_VADINITT_MASK);
814 ctrl1 |= (PDM_VAD0_CTRL_1_VADCHSEL(config->channel) | PDM_VAD0_CTRL_1_VADCICOSR(config->cicOverSampleRate) |
815 PDM_VAD0_CTRL_1_VADINITT(config->initializeTime));
816 base->VAD0_CTRL_1 = ctrl1;
817
818 /* Configure VAD0_CTRL_2 register */
819 base->VAD0_CTRL_2 =
820 (PDM_VAD0_CTRL_2_VADFRENDIS((config->enableFrameEnergy == true) ? 0U : 1U) |
821 PDM_VAD0_CTRL_2_VADPREFEN(config->enablePreFilter) | PDM_VAD0_CTRL_2_VADFRAMET(config->frameTime) |
822 PDM_VAD0_CTRL_2_VADINPGAIN(config->inputGain) | PDM_VAD0_CTRL_2_VADHPF(config->cutOffFreq));
823 }
824
825 /*!
826 * brief Configure voice activity detector signal filter.
827 *
828 * param base PDM base pointer
829 * param enableMaxBlock If signal maximum block enabled.
830 * param signalGain Gain value for the signal energy.
831 */
PDM_SetHwvadSignalFilterConfig(PDM_Type * base,bool enableMaxBlock,uint32_t signalGain)832 void PDM_SetHwvadSignalFilterConfig(PDM_Type *base, bool enableMaxBlock, uint32_t signalGain)
833 {
834 uint32_t signalConfig = base->VAD0_SCONFIG;
835
836 signalConfig &= ~(PDM_VAD0_SCONFIG_VADSMAXEN_MASK | PDM_VAD0_SCONFIG_VADSGAIN_MASK);
837 signalConfig |= (PDM_VAD0_SCONFIG_VADSMAXEN(enableMaxBlock) | PDM_VAD0_SCONFIG_VADSGAIN(signalGain)) |
838 PDM_VAD0_SCONFIG_VADSFILEN_MASK;
839 base->VAD0_SCONFIG = signalConfig;
840 }
841
842 /*!
843 * brief Configure voice activity detector noise filter.
844 *
845 * param base PDM base pointer
846 * param config Voice activity detector noise filter configure structure pointer .
847 */
PDM_SetHwvadNoiseFilterConfig(PDM_Type * base,const pdm_hwvad_noise_filter_t * config)848 void PDM_SetHwvadNoiseFilterConfig(PDM_Type *base, const pdm_hwvad_noise_filter_t *config)
849 {
850 assert(config != NULL);
851
852 base->VAD0_NCONFIG =
853 (PDM_VAD0_NCONFIG_VADNFILAUTO(config->enableAutoNoiseFilter) |
854 PDM_VAD0_NCONFIG_VADNOREN(config->enableNoiseDetectOR) | PDM_VAD0_NCONFIG_VADNMINEN(config->enableNoiseMin) |
855 PDM_VAD0_NCONFIG_VADNDECEN(config->enableNoiseDecimation) |
856 PDM_VAD0_NCONFIG_VADNFILADJ(config->noiseFilterAdjustment) | PDM_VAD0_NCONFIG_VADNGAIN(config->noiseGain));
857 }
858
859 /*!
860 * brief Configure voice activity detector zero cross detector.
861 *
862 * param base PDM base pointer
863 * param config Voice activity detector zero cross detector configure structure pointer .
864 */
PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type * base,const pdm_hwvad_zero_cross_detector_t * config)865 void PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type *base, const pdm_hwvad_zero_cross_detector_t *config)
866 {
867 assert(config != NULL);
868
869 uint32_t zcd = (base->VAD0_ZCD & (~(PDM_VAD0_ZCD_VADZCDTH_MASK | PDM_VAD0_ZCD_VADZCDADJ_MASK |
870 PDM_VAD0_ZCD_VADZCDAUTO_MASK | PDM_VAD0_ZCD_VADZCDAND_MASK)));
871
872 zcd |= (PDM_VAD0_ZCD_VADZCDTH(config->threshold) | PDM_VAD0_ZCD_VADZCDADJ(config->adjustmentThreshold) |
873 PDM_VAD0_ZCD_VADZCDAUTO(config->enableAutoThreshold) | PDM_VAD0_ZCD_VADZCDAND(config->zcdAnd)) |
874 PDM_VAD0_ZCD_VADZCDEN_MASK;
875
876 base->VAD0_ZCD = zcd;
877 }
878
879 /*!
880 * brief Enable/Disable hwvad callback.
881
882 * This function enable/disable the hwvad interrupt for the selected PDM peripheral.
883 *
884 * param base Base address of the PDM peripheral.
885 * param vadCallback callback Pointer to store callback function, should be NULL when disable.
886 * param userData user data.
887 * param enable true is enable, false is disable.
888 * retval None.
889 */
PDM_EnableHwvadInterruptCallback(PDM_Type * base,pdm_hwvad_callback_t vadCallback,void * userData,bool enable)890 void PDM_EnableHwvadInterruptCallback(PDM_Type *base, pdm_hwvad_callback_t vadCallback, void *userData, bool enable)
891 {
892 uint32_t instance = PDM_GetInstance(base);
893
894 if (enable)
895 {
896 PDM_EnableHwvadInterrupts(base, (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable);
897 NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn);
898 (void)EnableIRQ(PDM_HWVAD_EVENT_IRQn);
899 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
900 NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn);
901 (void)EnableIRQ(PDM_HWVAD_ERROR_IRQn);
902 #endif
903 s_pdm_hwvad_notification[instance].callback = vadCallback;
904 s_pdm_hwvad_notification[instance].userData = userData;
905 }
906 else
907 {
908 PDM_DisableHwvadInterrupts(base,
909 (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable);
910 (void)DisableIRQ(PDM_HWVAD_EVENT_IRQn);
911 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
912 (void)DisableIRQ(PDM_HWVAD_ERROR_IRQn);
913 NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn);
914 #endif
915 s_pdm_hwvad_notification[instance].callback = NULL;
916 s_pdm_hwvad_notification[instance].userData = NULL;
917 NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn);
918 }
919 }
920
921 #if (defined PDM)
922 void PDM_HWVAD_EVENT_DriverIRQHandler(void);
PDM_HWVAD_EVENT_DriverIRQHandler(void)923 void PDM_HWVAD_EVENT_DriverIRQHandler(void)
924 {
925 if ((PDM_GetHwvadInterruptStatusFlags(PDM) & (uint32_t)kPDM_HwvadStatusVoiceDetectFlag) != 0U)
926 {
927 PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusVoiceDetectFlag);
928 if (s_pdm_hwvad_notification[0].callback != NULL)
929 {
930 s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_VoiceDetected, s_pdm_hwvad_notification[0].userData);
931 }
932 }
933 #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
934 else
935 {
936 PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation);
937 if (s_pdm_hwvad_notification[0].callback != NULL)
938 {
939 s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData);
940 }
941 }
942 #endif
943 SDK_ISR_EXIT_BARRIER;
944 }
945
946 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
947 void PDM_HWVAD_ERROR_DriverIRQHandler(void);
PDM_HWVAD_ERROR_DriverIRQHandler(void)948 void PDM_HWVAD_ERROR_DriverIRQHandler(void)
949 {
950 PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation);
951 if (s_pdm_hwvad_notification[0].callback != NULL)
952 {
953 s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData);
954 }
955 SDK_ISR_EXIT_BARRIER;
956 }
957 #endif
958 #endif
959 #endif
960
961 #if defined(PDM)
962 void PDM_EVENT_DriverIRQHandler(void);
PDM_EVENT_DriverIRQHandler(void)963 void PDM_EVENT_DriverIRQHandler(void)
964 {
965 assert(s_pdmHandle[0] != NULL);
966 s_pdmIsr(PDM, s_pdmHandle[0]);
967 SDK_ISR_EXIT_BARRIER;
968 }
969 #endif
970