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