1 /***************************************************************************//**
2 * \file cyhal_pdmpcm.h
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon PDM/PCM.
6 * This interface abstracts out the chip specific details. If any chip specific
7 * functionality is necessary, or performance is critical the low level functions
8 * can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 *     http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29 
30 /**
31 * \addtogroup group_hal_pdmpcm PDM/PCM (Pulse-Density Modulation to Pulse-Code Modulation Converter)
32 * \ingroup group_hal
33 * \{
34 * High level interface for interacting with the pulse-density modulation to
35 * pulse-code modulation (PDM/PCM) converter.
36 *
37 * The PDM/PCM converter is a asynchronous operation. A PDM-PCM converter is used
38 * to convert 1-bit digital audio streaming data to PCM data. The sample rate, word
39 * size, and channels can all be configured.
40 *
41 * \section section_pdmpcm_features Features
42 *
43 * * Supports FIFO buffer for Incoming Data
44 * * Configurable Gain Settings
45 * * Configurable Word Length
46 * * Configurable interrupt and callback assignment from PDM/PCM events - \ref cyhal_pdm_pcm_event_t
47 *
48 * \section section_pdmpcm_quickstart Quick Start
49 * Initialize a PDM/PCM converter instance using the \ref cyhal_pdm_pcm_init and
50 * provide the clock and data pins.<br>
51 * See \ref subsection_pdmpcm_snippet_1 for example initialization.
52 * \note The clock parameter (const \ref cyhal_clock_t *clk) is optional and
53 * can be set to NULL to generate and use an available clock resource with a default
54 * frequency.
55 *
56 * \section section_pdmpcm_snippets Code Snippets
57 * \note Error checking is omitted for clarity
58 *
59 * \subsection subsection_pdmpcm_snippet_1 Snippet 1: PDM/PCM Initialization and Configuration
60 * This snippet initializes a PCM/PCM resource for conversion and assigns the pins.
61 *
62 * \snippet hal_pdmpcm.c snippet_cyhal_pdmpcm_init
63 *
64 * \subsection subsection_pdmpcm_snippet_2 Snippet 2: PDM/PCM Asynchronous Receive
65 * This snippet shows how to receive data in the background using \ref cyhal_pdm_pcm_read_async.
66 * Notification of the asynchronous read completion is achieved by using \ref cyhal_pdm_pcm_register_callback
67 * to register a callback function and \ref cyhal_pdm_pcm_enable_event to enable callling the
68 * callback when an synchonous read completes.
69 *
70 * \snippet hal_pdmpcm.c snippet_cyhal_pdmpcm_async_receive
71 *
72 * \section subsection_pdmpcm_moreinformation More Information
73 *
74 * <b>Code examples (Github)</b>
75 * * <a href="https://github.com/infineon/mtb-example-psoc6-pdm-pcm" ><b>
76 PSoC™ 6 MCU: PDM-to-PCM</b></a>
77 * * <a href="https://github.com/infineon/mtb-example-psoc6-pdm-to-i2s" ><b>
78 PSoC™ 6 MCU: PDM to I2S</b></a>
79 */
80 
81 #pragma once
82 
83 #include <stdbool.h>
84 #include <stddef.h>
85 #include <stdint.h>
86 #include "cyhal_general_types.h"
87 #include "cyhal_hw_types.h"
88 #include "cyhal_pin_package.h"
89 #include "cyhal_syspm.h"
90 
91 #if defined(__cplusplus)
92 extern "C" {
93 #endif
94 
95 /** \addtogroup group_hal_results_pdmpcm PDM/PCM HAL Results
96  *  PDM/PCM specific return codes
97  *  \ingroup group_hal_results
98  *  \{ *//**
99  */
100 
101 /** The pin PDM/PCM hardware cannot be initialized with the passed in pin */
102 #define CYHAL_PDM_PCM_RSLT_ERR_INVALID_PIN              \
103     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_PDMPCM, 0))
104 /** A configuration parameter is invalid: sample_rate, decimation_rate, PCM word length, left/right gain.
105  * See the implementation specific documentation for valid range */
106 #define CYHAL_PDM_PCM_RSLT_ERR_INVALID_CONFIG_PARAM     \
107     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_PDMPCM, 1))
108 /** An async read operation is already progres */
109 #define CYHAL_PDM_PCM_RSLT_ERR_ASYNC_IN_PROGRESS        \
110     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_PDMPCM, 2))
111 
112 /**
113  * \}
114  */
115 
116 /** PDM/PCM interrupt triggers */
117 typedef enum {
118     CYHAL_PDM_PCM_RX_HALF_FULL   = 0x01, /**< RX hardware buffer is half full */
119     CYHAL_PDM_PCM_RX_NOT_EMPTY   = 0x02, /**< RX hardware buffer is not empty */
120     CYHAL_PDM_PCM_RX_OVERFLOW    = 0x04, /**< Attempt to write to a full RX hardware buffer */
121     CYHAL_PDM_PCM_RX_UNDERFLOW   = 0x08, /**< Attempt to read from an empty buffer */
122     CYHAL_PDM_PCM_ASYNC_COMPLETE = 0x10, /**< Async operation completed */
123 } cyhal_pdm_pcm_event_t;
124 
125 /** PDM/PCM channel select */
126 typedef enum {
127     CYHAL_PDM_PCM_MODE_LEFT,   /**< The channel mono left */
128     CYHAL_PDM_PCM_MODE_RIGHT,  /**< The channel mono right */
129     CYHAL_PDM_PCM_MODE_STEREO, /**< The channel stereo */
130 } cyhal_pdm_pcm_mode_t;
131 
132 /** Describes the current configuration of a PDM/PCM */
133 typedef struct
134 {
135     uint32_t sample_rate;       /**< Sample rate in Hz */
136     uint8_t decimation_rate;    /**< PDM decimation rate */
137     cyhal_pdm_pcm_mode_t mode;  /**< left, right, or stereo */
138     uint8_t word_length;        /**< PCM word length in bits, see the implementation specific documentation for valid range */
139     int16_t left_gain;           /**< PGA in 0.5 dB increment, for example a value of 5 would mean +2.5 dB. The closest fit value will be used, see the implementation specific documentation for valid ranges. This may be negative if the implementation supports it. */
140     int16_t right_gain;          /**< PGA in 0.5 dB increment, for example a value of 5 would mean +2.5 dB. The closest fit value will be used, see the implementation specific documentation for valid ranges. This may be negative if the implementation supports it. */
141 } cyhal_pdm_pcm_cfg_t;
142 
143 /** Handler for PDM/PCM interrupts */
144 typedef void (*cyhal_pdm_pcm_event_callback_t)(void *handler_arg, cyhal_pdm_pcm_event_t event);
145 
146 /** Initialize the PDM/PCM peripheral
147  *
148  * Configures the pins used by PDM/PCM converter, sets a default format and frequency, and enables the peripheral
149  * @param[out] obj        Pointer to a PDM/PCM object. The caller must allocate the memory
150  *                          for this object but the init function will initialize its contents.
151  * @param[in]  pin_data   The pin to use for PDM input
152  * @param[in]  pin_clk    The pin to use for PDM clock output
153  * @param[in]  clk_source The clock source for PDM/PCM block
154  * @param[in]  cfg        The configuration for the PDM/PCM block
155  * @return The status of the init request
156  */
157 cy_rslt_t cyhal_pdm_pcm_init(cyhal_pdm_pcm_t *obj, cyhal_gpio_t pin_data, cyhal_gpio_t pin_clk,
158                 const cyhal_clock_t *clk_source, const cyhal_pdm_pcm_cfg_t *cfg);
159 
160 /** Initialize the PDM/PCM out peripheral using a configurator generated configuration struct
161   *
162  * @param[out] obj              Pointer to a PDM/PCM object. The caller must allocate the memory
163  *                              for this object but the init function will initialize its contents.
164  * @param[in] cfg               Configuration structure generated by a configurator.
165  * @return The status of the init request
166  */
167 cy_rslt_t cyhal_pdm_pcm_init_cfg(cyhal_pdm_pcm_t *obj, const cyhal_pdm_pcm_configurator_t* cfg);
168 
169 /** Release a PDM/PCM object, behavior is undefined if an asynchronous read is in progress
170  *
171  * Return the peripheral, pins and clock owned by the PDM/PCM object to their reset state
172  * @param[in,out] obj The PDM/PCM object to deinitialize
173  */
174 void cyhal_pdm_pcm_free(cyhal_pdm_pcm_t *obj);
175 
176 /**
177  * Start the PDM/PCM operation
178  *
179  * @param[in] obj The PDM/PCM object to start
180  * @return the status of the start request
181  */
182 cy_rslt_t cyhal_pdm_pcm_start(cyhal_pdm_pcm_t *obj);
183 
184 /**
185  * Stop the PDM/PCM operation
186  *
187  * @param[in] obj The PDM/PCM object to start
188  * @return the status of the stop request
189  */
190 cy_rslt_t cyhal_pdm_pcm_stop(cyhal_pdm_pcm_t *obj);
191 
192 /** Checks if the specified PDM/PCM peripheral is enabled (regardless of whether any
193   * unread data has been received).
194   *
195   * The PDM/PCM peripheral can be enabled by calling @ref cyhal_pdm_pcm_start and disabled by calling
196   * @ref cyhal_pdm_pcm_stop
197   *
198   * @param[in] obj  The I2S peripheral to check
199   * @return Whether the I2S receive function is enabled.
200   */
201 bool cyhal_pdm_pcm_is_enabled(cyhal_pdm_pcm_t *obj);
202 
203 /** Updates the PDM/PCM channel gains. Each integer increment represent a 0.5 dB value.
204  * For example: a gain value of 5 would mean +2.5 dB.
205  * If the exact gain value requested is not supported, it will be rounded to the
206  * nearest legal value. See the implementation specific documentation for valid ranges.
207  *
208  * \note Gains may be negative if the implementation supports it.
209  *
210  * @param[in] obj        The PDM/PCM object to configure
211  * @param[in] gain_left  The gain of the left channel in units of 0.5 dB
212  * @param[in] gain_right The gain of the right channel in units of 0.5 dB
213  * @return The status of the set gain operation. An error will be returned if the value is outside of range supported by HW.
214  */
215 cy_rslt_t cyhal_pdm_pcm_set_gain(cyhal_pdm_pcm_t *obj, int16_t gain_left, int16_t gain_right);
216 
217 /** Clears the hardware buffer
218  *
219  * @param[in] obj The PDM/PCM peripheral
220  * @return The status of the clear request
221  */
222 cy_rslt_t cyhal_pdm_pcm_clear(cyhal_pdm_pcm_t *obj);
223 
224 /** Reads data synchronously
225  *
226  * This will read either `length` words or the number of words that are currently available in the
227  * receive buffer, whichever is less, then return. The value pointed to by `length` will be updated
228  * to reflect the number of words that were actually read.
229  *  If there are less data in FIFO than length, length will be update with number of words read.
230  *
231  * @param[in]     obj    The PDM/PCM peripheral
232  * @param[out]    data   Pointer to word array where incoming data will be stored. Buffer must be aligned to word-size.
233  *                       Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits,
234  *                       each word will consume two bytes. But if the word length is 20, each word will consume 32 bits.
235  *                       Negative value will use sign-extension. -1 with 24-bit word length will have 32-bit value of 0xFFFFFFFF.
236  * @param[in,out] length Number of 32-bit words to read, updated with the number actually read
237  * @return The status of the read request
238  */
239 cy_rslt_t cyhal_pdm_pcm_read(cyhal_pdm_pcm_t *obj, void *data, size_t *length);
240 
241 /** Begin asynchronous PDM/PCM read
242  *
243  * This will transfer `length` words into the buffer pointed to by `data` in the background. When the
244  * requested quantity of data has been read, the @ref CYHAL_PDM_PCM_ASYNC_COMPLETE event will be raised.
245  * See @ref cyhal_pdm_pcm_register_callback and @ref cyhal_pdm_pcm_enable_event.
246  *
247  * cyhal_pdm_pcm_set_async_mode can be used to control whether this uses DMA or a CPU-driven transfer.
248  *
249  * @param[in]  obj     The PDM/PCM object
250  * @param[out] data    Pointer to word array where incoming data will be stored. Buffer must be aligned to word-size.
251  *                     Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits,
252  *                     each word will consume two bytes. But if the word length is 20, each word will consume 32 bits.
253  *                     Negative value will use sign-extension. -1 with 24-bit word length will have 32-bit value of 0xFFFFFFFF.
254  * @param[in]  length  Number of  words to read
255  * @return The status of the read_async request
256  */
257 cy_rslt_t cyhal_pdm_pcm_read_async(cyhal_pdm_pcm_t *obj, void *data, size_t length);
258 
259 /** Checks if an async read operation is pending
260  *
261  * @param[in] obj  The PDM/PCM peripheral to check
262  * @return Indication of whether a PDM/PCM async operation is pending
263  */
264 bool cyhal_pdm_pcm_is_pending(cyhal_pdm_pcm_t *obj);
265 
266 /** Abort an PDM/PCM operation started by cyhal_pdm_pcm_read_async function
267  *
268  * @param[in] obj The PDM/PCM peripheral to stop
269  * @return The status of the abort_async request
270  */
271 cy_rslt_t cyhal_pdm_pcm_abort_async(cyhal_pdm_pcm_t *obj);
272 
273 /** Register a PDM/PCM event handler
274  *
275  * This function will be called when one of the events enabled by \ref cyhal_pdm_pcm_enable_event occurs.
276  *
277  * @param[in] obj          The PDM/PCM object
278  * @param[in] callback     The callback handler which will be invoked when the interrupt fires
279  * @param[in] callback_arg Generic argument that will be provided to the callback when called
280  */
281 void cyhal_pdm_pcm_register_callback(cyhal_pdm_pcm_t *obj, cyhal_pdm_pcm_event_callback_t callback, void *callback_arg);
282 
283 /** Configure PDM/PCM event enablement.
284  *
285  * @param[in] obj            The PDM/PCM object
286  * @param[in] event          The PDM/PCM event type
287  * @param[in] intr_priority  The priority for NVIC interrupt events
288  * @param[in] enable         True to turn on events, False to turn off
289  */
290 void cyhal_pdm_pcm_enable_event(cyhal_pdm_pcm_t *obj, cyhal_pdm_pcm_event_t event, uint8_t intr_priority, bool enable);
291 
292 /** Set the mechanism that is used to perform PDM/PCM asynchronous operation. The default is SW.
293  *
294  * When an enabled event occurs, the function specified by \ref cyhal_pdm_pcm_register_callback will be called.
295  *
296  * @param[in] obj          The PDM/PCM object
297  * @param[in] mode         The transfer mode
298  * @param[in] dma_priority The priority, if DMA is used. Valid values are the same as for @ref cyhal_dma_init.
299  *                         If DMA is not selected, the only valid value is CYHAL_DMA_PRIORITY_DEFAULT, and no
300                            guarantees are made about prioritization.
301  * @return The status of the set mode request
302  */
303 cy_rslt_t cyhal_pdm_pcm_set_async_mode(cyhal_pdm_pcm_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority);
304 
305 #if defined(__cplusplus)
306 }
307 #endif
308 
309 #ifdef CYHAL_PDMPCM_IMPL_HEADER
310 #include CYHAL_PDMPCM_IMPL_HEADER
311 #endif /* CYHAL_PDMPCM_IMPL_HEADER */
312 
313 /** \} group_hal_pdmpcm */
314