1 /***************************************************************************/ /**
2 * \file cyhal_adc_mic.c
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon Analog/Digital
6 * convert. This interface abstracts out the chip specific details. If any chip
7 * specific functionality is necessary, or performance is critical the low level
8 * functions can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2021 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_impl_adcmic ADC (Analog Digital Converter)
32 * \ingroup group_hal_impl
33 * \{
34 * \section cyhal_adcmic_impl_features Features
35 * The CAT1B ADC supports the following features:
36 * * Resolution: 16 bit
37 * * Sample rate: Fixed 480 ksps
38 * * Minimum acquisition time: Up to 2084 ns
39 * * SW-based async transfer only (DMA is not supported)
40 * * VREF: @ref CYHAL_ADC_REF_INTERNAL (1.8V) only
41 * * Only @ref CYHAL_POWER_LEVEL_DEFAULT and @ref CYHAL_POWER_LEVEL_OFF are defined.
42 * * Single ended vneg: @ref CYHAL_ADC_VNEG_VSSA
43 *
44 * The following functions are not supported:
45 * * Differential channels
46 * * Continuous scanning
47 * * @ref CYHAL_ADC_EOS event (this is only applicable when continuously scanning)
48 * * Averaging. In @ref cyhal_adc_config_t, average count must be 1 and average_mode_flags must be 0.
49 * In @ref cyhal_adc_channel_config_t, enable_averaging must be false.
50 * * External vref and bypass pins
51 * *
52 * \} group_hal_impl_adcmic
53 */
54
55 #include "cyhal_adc.h"
56 #include "cyhal_clock.h"
57 #include "cyhal_gpio.h"
58 #include "cyhal_hwmgr.h"
59 #include "cyhal_utils.h"
60 #include "cyhal_system.h"
61 #include <string.h>
62
63 #if (_CYHAL_DRIVER_AVAILABLE_ADC_MIC)
64 #include "cy_adcmic.h"
65
66 #if defined(__cplusplus)
67 extern "C"
68 {
69 #endif
70
71 #define _CYHAL_ADCMIC_NUM_CHANNELS(obj) (sizeof(obj->channel_config) / sizeof(obj->channel_config[0]))
72 static const uint8_t _CYHAL_ADCMIC_RESOLUTION = 16u;
73 static const uint32_t _CYHAL_ADCMIC_TARGET_CLOCK_HZ = 24000000u;
74 static const uint32_t _CYHAL_ADCMIC_SAMPLE_RATE_HZ = 480000u; /* Fixed by the HW */
75 static const uint32_t _CYHAL_ADCMIC_ACQUISITION_TIME_NS = 2084u; /* 1 / 480 ksps */
76
77 static const cy_stc_adcmic_dc_path_config_t _CYHAL_ADCMIC_DEFAULT_DC_CONFIG =
78 {
79 .range = CY_ADCMIC_DC_RANGE_1_8V,
80 .input = CY_ADCMIC_REFGND, /* Will be updated to match the actual input when a read is triggered */
81 .tmrLatch = false, /* Don't wait for timer trigger to latch result */
82 };
83
84 static const cy_stc_adcmic_config_t _CYHAL_ADCMIC_DEFAULT_CONFIG =
85 {
86 .source = CY_ADCMIC_DC,
87 .sampleRate = CY_ADCMIC_480KSPS, /* This is the only sample rate supported for DC measurement */
88 .anaConfig = NULL, /* Only applicable for the analog mic input path */
89 .digConfig = NULL, /* Only applicable for the digital PDM input path */
90 /* Safe to cast away const becuase this struct is itself stored in flash */
91 .dcConfig = (cy_stc_adcmic_dc_path_config_t *)&_CYHAL_ADCMIC_DEFAULT_DC_CONFIG,
92 .biQuadConfig = NULL, /* BiQuad isn't used for DC measurements */
93 .fifoConfig = NULL, /* FIFO isn't used for DC measurements */
94 .tmrTrgConfig = NULL, /* The timer and triggers aren't useful in DC measurement mode */
95 };
96
97 static MXS40ADCMIC_Type *const _cyhal_adcmic_base[] =
98 {
99 #if (CY_IP_MXS40ADCMIC_INSTANCES <= 1)
100 MXS40ADCMIC0,
101 #else
102 #warning Unhandled ADCMIC instance count
103 #endif
104 };
105
106 static const en_clk_dst_t _cyhal_adcmic_clock[] =
107 {
108 #if (CY_IP_MXS40ADCMIC_INSTANCES <= 1)
109 PCLK_ADCMIC_CLK_HF,
110 #else
111 #warning Unhandled ADCMIC instance count
112 #endif
113 };
114
115 static cyhal_adc_t* _cyhal_adcmic_config_structs[CY_IP_MXS40ADCMIC_INSTANCES];
116
117 static const IRQn_Type _cyhal_adcmic_irq_n[] =
118 {
119 #if (CY_IP_MXS40ADCMIC_INSTANCES <= 1)
120 adcmic_interrupt_adcmic_IRQn,
121 #else
122 #warning Unhandled ADCMIC instance count
123 #endif
124 };
125
_cyhal_adcmic_get_block_from_irqn(IRQn_Type irqn)126 static uint8_t _cyhal_adcmic_get_block_from_irqn(IRQn_Type irqn)
127 {
128 switch (irqn)
129 {
130 #if (CY_IP_MXS40ADCMIC_INSTANCES <= 1)
131 case adcmic_interrupt_adcmic_IRQn:
132 return 0;
133 #else
134 #warning Unhandled ADCMIC instance count
135 #endif
136 default:
137 CY_ASSERT(false); // Should never be called with a non-ADCMIC IRQn
138 return 0;
139 }
140 }
141
142 /* Find the next enabled channel, starting from current_idx and adjusting the buffer
143 * along the way to account for disabled channels */
_cyhal_adcmic_find_next_channel(cyhal_adc_t * obj,uint8_t * current_idx,int32_t ** buffer)144 static void _cyhal_adcmic_find_next_channel(cyhal_adc_t* obj, uint8_t* current_idx, int32_t **buffer)
145 {
146 uint8_t start_idx = *current_idx;
147 do
148 {
149 if(NULL != obj->channel_config[*current_idx])
150 {
151 if(obj->channel_config[*current_idx]->enabled)
152 {
153 break;
154 }
155 else
156 {
157 ++(*buffer);
158 }
159 *current_idx = (*current_idx + 1) % _CYHAL_ADCMIC_NUM_CHANNELS(obj);
160 }
161 } while(*current_idx != start_idx); /* While we haven't wrapped completely around */
162 }
163
_cyhal_adcmic_irq_handler(void)164 static void _cyhal_adcmic_irq_handler(void)
165 {
166 /* The only interrupt we enable is DC conversion complete */
167 IRQn_Type irqn = _CYHAL_UTILS_GET_CURRENT_IRQN();
168 uint8_t block = _cyhal_adcmic_get_block_from_irqn(irqn);
169 cyhal_adc_t* obj = _cyhal_adcmic_config_structs[block];
170 Cy_ADCMic_ClearInterrupt(obj->base, CY_ADCMIC_INTR_DC);
171 obj->conversion_complete = true;
172
173 if(obj->async_scans_remaining > 0)
174 {
175 *(obj->async_buff_next) = Cy_ADCMic_GetDcResult(obj->base);
176 uint8_t old_channel = obj->current_channel_index;
177 _cyhal_adcmic_find_next_channel(obj, &(obj->current_channel_index), &(obj->async_buff_next));
178 /* we know that at least one channel was enabled, so we don't have to worry about that case,
179 * but we do need to check for rollover */
180 if(obj->current_channel_index <= old_channel)
181 {
182 --(obj->async_scans_remaining);
183 if(obj->async_scans_remaining == 0)
184 {
185 /* We're done, notify the user if they asked us to */
186 obj->async_buff_next = NULL;
187 Cy_ADCMic_DisableInterrupt(obj->base, CY_ADCMIC_INTR_DC);
188 if(0 != (CYHAL_ADC_ASYNC_READ_COMPLETE & ((cyhal_adc_event_t)obj->user_enabled_events)))
189 {
190 cyhal_adc_event_callback_t callback = (cyhal_adc_event_callback_t)obj->callback_data.callback;
191 if(NULL != callback)
192 {
193 callback(obj->callback_data.callback_arg, CYHAL_ADC_ASYNC_READ_COMPLETE);
194 }
195 }
196 }
197 }
198 }
199 }
200
_cyhal_adcmic_convert_channel_sel(uint8_t bit_index)201 static cy_en_adcmic_dc_channel_t _cyhal_adcmic_convert_channel_sel(uint8_t bit_index)
202 {
203 static const cy_en_adcmic_dc_channel_t gpio_channel[] =
204 {
205 CY_ADCMIC_GPIO0,
206 CY_ADCMIC_GPIO1,
207 CY_ADCMIC_GPIO2,
208 CY_ADCMIC_GPIO3,
209 CY_ADCMIC_GPIO4,
210 CY_ADCMIC_GPIO5,
211 CY_ADCMIC_GPIO6,
212 CY_ADCMIC_GPIO7,
213 CY_ADCMIC_GPIO8,
214 CY_ADCMIC_GPIO9,
215 CY_ADCMIC_GPIO10,
216 CY_ADCMIC_GPIO11,
217 CY_ADCMIC_GPIO12,
218 CY_ADCMIC_GPIO13,
219 CY_ADCMIC_GPIO14,
220 CY_ADCMIC_GPIO15,
221 CY_ADCMIC_GPIO16,
222 CY_ADCMIC_GPIO17,
223 CY_ADCMIC_GPIO18,
224 CY_ADCMIC_GPIO19,
225 CY_ADCMIC_GPIO20,
226 CY_ADCMIC_GPIO21,
227 CY_ADCMIC_GPIO22,
228 CY_ADCMIC_GPIO23,
229 CY_ADCMIC_GPIO24,
230 CY_ADCMIC_GPIO25,
231 CY_ADCMIC_GPIO26,
232 CY_ADCMIC_GPIO27
233 };
234
235 if (bit_index < sizeof(gpio_channel) / sizeof(gpio_channel[0]))
236 {
237 return gpio_channel[bit_index];
238 }
239 else
240 {
241 CY_ASSERT(false); /* We only support GPIO channels and the above defines all of them */
242 return CY_ADCMIC_REFGND;
243 }
244 }
245
246 /*******************************************************************************
247 * ADC HAL Functions
248 *******************************************************************************/
249
_cyhal_adc_config_hw(cyhal_adc_t * obj,const cyhal_adc_configurator_t * cfg,cyhal_gpio_t pin,bool owned_by_configurator)250 cy_rslt_t _cyhal_adc_config_hw(cyhal_adc_t *obj, const cyhal_adc_configurator_t* cfg, cyhal_gpio_t pin, bool owned_by_configurator)
251 {
252 CY_ASSERT(NULL != obj);
253
254 cy_rslt_t result = CY_RSLT_SUCCESS;
255 memset(obj, 0, sizeof(cyhal_adc_t));
256 obj->resource.type = CYHAL_RSC_INVALID;
257
258 obj->owned_by_configurator = owned_by_configurator;
259
260 if(NULL == cfg->resource && NC != pin)
261 {
262 for (uint32_t i = 0; i < sizeof(cyhal_pin_map_adcmic_gpio_adc_in)/sizeof(cyhal_resource_pin_mapping_t); i++)
263 {
264 if (pin == cyhal_pin_map_adcmic_gpio_adc_in[i].pin)
265 {
266 // Force the channel_num to 0 since the ADC_MIC pin_map repurposes that field
267 cyhal_resource_inst_t inst = { CYHAL_RSC_ADCMIC, cyhal_pin_map_adcmic_gpio_adc_in[i].block_num, 0 };
268 if (CY_RSLT_SUCCESS == cyhal_hwmgr_reserve(&inst))
269 {
270 obj->resource = inst;
271 break;
272 }
273 }
274 }
275
276 if (obj->resource.type == CYHAL_RSC_INVALID)
277 {
278 result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
279 }
280 }
281 else if(NULL != cfg->resource)
282 {
283 obj->resource = *cfg->resource;
284 }
285 else
286 {
287 result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
288 }
289
290 en_clk_dst_t pclk = (en_clk_dst_t)0;
291 if (CY_RSLT_SUCCESS == result)
292 {
293 obj->resource = *cfg->resource;
294
295 obj->base = _cyhal_adcmic_base[obj->resource.block_num];
296 pclk = _cyhal_adcmic_clock[obj->resource.block_num];
297 if (NULL != cfg->clock)
298 {
299 obj->clock = *cfg->clock;
300 obj->dedicated_clock = false;
301 }
302 else if (CY_RSLT_SUCCESS ==
303 (result = _cyhal_utils_allocate_clock(&(obj->clock), &(obj->resource), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true)))
304 {
305 obj->dedicated_clock = true;
306 result = cyhal_clock_set_enabled(&(obj->clock), true /* enabled */, false /* don't wait for lock. Not applicable to dividers */);
307 }
308 }
309
310 if (CY_RSLT_SUCCESS == result && obj->clock.block != CYHAL_CLOCK_BLOCK_HF)
311 {
312 if (CY_SYSCLK_SUCCESS != _cyhal_utils_peri_pclk_assign_divider(pclk, &(obj->clock)))
313 result = CYHAL_ADC_RSLT_FAILED_CLOCK;
314 }
315
316 if (CY_RSLT_SUCCESS == result)
317 {
318 if(obj->dedicated_clock)
319 {
320 if(CY_RSLT_SUCCESS != _cyhal_utils_set_clock_frequency(&(obj->clock), _CYHAL_ADCMIC_TARGET_CLOCK_HZ, NULL))
321 {
322 result = CYHAL_ADC_RSLT_FAILED_CLOCK;
323 }
324 }
325 }
326
327 if (result == CY_RSLT_SUCCESS)
328 {
329 result = (cy_rslt_t)Cy_ADCMic_Init(obj->base, cfg->config);
330 }
331
332 if (result == CY_RSLT_SUCCESS)
333 {
334 Cy_ADCMic_Enable(obj->base);
335 _cyhal_adcmic_config_structs[obj->resource.block_num] = obj;
336 cy_stc_sysint_t irqCfg = { _cyhal_adcmic_irq_n[obj->resource.block_num], CYHAL_ISR_PRIORITY_DEFAULT };
337 Cy_SysInt_Init(&irqCfg, _cyhal_adcmic_irq_handler);
338 NVIC_EnableIRQ(_cyhal_adcmic_irq_n[obj->resource.block_num]);
339 }
340 else
341 {
342 cyhal_adc_free(obj);
343 }
344 return result;
345 }
346
cyhal_adc_init(cyhal_adc_t * obj,cyhal_gpio_t pin,const cyhal_clock_t * clk)347 cy_rslt_t cyhal_adc_init(cyhal_adc_t *obj, cyhal_gpio_t pin, const cyhal_clock_t *clk)
348 {
349 cyhal_adc_configurator_t config;
350 config.resource = NULL;
351 config.config = &_CYHAL_ADCMIC_DEFAULT_CONFIG;
352 config.clock = clk;
353 config.num_channels = 0u;
354
355 cy_rslt_t result = _cyhal_adc_config_hw(obj, &config, pin, false);
356 return result;
357 }
358
cyhal_adc_init_cfg(cyhal_adc_t * adc,cyhal_adc_channel_t ** channels,uint8_t * num_channels,const cyhal_adc_configurator_t * cfg)359 cy_rslt_t cyhal_adc_init_cfg(cyhal_adc_t *adc, cyhal_adc_channel_t** channels, uint8_t* num_channels,
360 const cyhal_adc_configurator_t *cfg)
361 {
362 cy_rslt_t result = CY_RSLT_SUCCESS;
363 if(*num_channels < cfg->num_channels)
364 {
365 result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
366 }
367
368 if(CY_RSLT_SUCCESS == result)
369 {
370 *num_channels = cfg->num_channels;
371 result = _cyhal_adc_config_hw(adc, cfg, NC, true);
372 }
373
374 if(CY_RSLT_SUCCESS == result)
375 {
376 /* config_hw will have initialized the channels in the ADC HW and the configurator will
377 * have set up the routing, but we need to initialize the channel structs */
378 for(int i = 0; i < *num_channels; ++i)
379 {
380 cyhal_adc_channel_t* channel = channels[i];
381 memset(channel, 0, sizeof(cyhal_adc_channel_t));
382 channel->adc = adc;
383 channel->channel_idx = i;
384 /* Nothing in this flow needs to know what the pins are - and the inputs aren't even
385 * necesssarily pins. The configurator takes care of resource reservation and routing for us */
386 channel->vplus = NC;
387 channel->enabled = true;
388 }
389 }
390 return result;
391 }
392
cyhal_adc_free(cyhal_adc_t * obj)393 void cyhal_adc_free(cyhal_adc_t *obj)
394 {
395 CY_ASSERT(NULL != obj);
396 if (CYHAL_RSC_INVALID != obj->resource.type)
397 {
398 IRQn_Type irqn = _cyhal_adcmic_irq_n[obj->resource.block_num];
399 NVIC_DisableIRQ(irqn);
400 _cyhal_adcmic_config_structs[obj->resource.block_num] = NULL;
401
402 if(obj->dedicated_clock)
403 {
404 _cyhal_utils_peri_pclk_disable_divider(_cyhal_adcmic_clock[obj->resource.block_num], &(obj->clock));
405 cyhal_clock_free(&obj->clock);
406 }
407
408 if (NULL != obj->base)
409 {
410 Cy_ADCMic_Disable(obj->base);
411 obj->base = NULL;
412 }
413
414 if(false == obj->owned_by_configurator)
415 {
416 cyhal_hwmgr_free(&obj->resource);
417 }
418 }
419 }
420
cyhal_adc_configure(cyhal_adc_t * obj,const cyhal_adc_config_t * config)421 cy_rslt_t cyhal_adc_configure(cyhal_adc_t *obj, const cyhal_adc_config_t *config)
422 {
423 /* The hardware is very limited, so all we can do is check that the config matches what we support */
424 CY_UNUSED_PARAMETER(obj);
425 if((false != config->continuous_scanning)
426 || (_CYHAL_ADCMIC_RESOLUTION != config->resolution)
427 || (1u != config->average_count)
428 || (0u != config->average_mode_flags)
429 || (0u != config->ext_vref_mv)
430 || (CYHAL_ADC_VNEG_VSSA != config->vneg)
431 || (CYHAL_ADC_REF_INTERNAL != config->vref)
432 || (NC != config->ext_vref)
433 || (false != config->is_bypassed)
434 || (NC != config->bypass_pin))
435 {
436 return CYHAL_ADC_RSLT_BAD_ARGUMENT;
437 }
438
439 return CY_RSLT_SUCCESS;
440 }
441
cyhal_adc_set_power(cyhal_adc_t * obj,cyhal_power_level_t power)442 cy_rslt_t cyhal_adc_set_power(cyhal_adc_t *obj, cyhal_power_level_t power)
443 {
444 // The ADC doesn't have selectable power levels in the same way that the opamps do.
445 if(CYHAL_POWER_LEVEL_OFF == power)
446 {
447 Cy_ADCMic_Disable(obj->base);
448 }
449 else
450 {
451 Cy_ADCMic_Enable(obj->base);
452 }
453 return CY_RSLT_SUCCESS;
454 }
455
cyhal_adc_set_sample_rate(cyhal_adc_t * obj,uint32_t desired_sample_rate_hz,uint32_t * achieved_sample_rate_hz)456 cy_rslt_t cyhal_adc_set_sample_rate(cyhal_adc_t* obj, uint32_t desired_sample_rate_hz, uint32_t* achieved_sample_rate_hz)
457 {
458 /* Only one sample rate supported, so all we can do is validate */
459 CY_UNUSED_PARAMETER(obj);
460 *achieved_sample_rate_hz = _CYHAL_ADCMIC_SAMPLE_RATE_HZ;
461 return (_CYHAL_ADCMIC_SAMPLE_RATE_HZ == desired_sample_rate_hz) ? CY_RSLT_SUCCESS : CYHAL_ADC_RSLT_BAD_ARGUMENT;
462 }
463
cyhal_adc_channel_init_diff(cyhal_adc_channel_t * obj,cyhal_adc_t * adc,cyhal_gpio_t vplus,cyhal_gpio_t vminus,const cyhal_adc_channel_config_t * cfg)464 cy_rslt_t cyhal_adc_channel_init_diff(cyhal_adc_channel_t *obj, cyhal_adc_t* adc, cyhal_gpio_t vplus, cyhal_gpio_t vminus, const cyhal_adc_channel_config_t* cfg)
465 {
466 CY_ASSERT(obj != NULL);
467 CY_ASSERT(adc != NULL);
468
469 cy_rslt_t result = CY_RSLT_SUCCESS;
470
471 memset(obj, 0, sizeof(cyhal_adc_channel_t));
472 obj->vplus = NC;
473
474 // Check for invalid pin or pin belonging to a different SAR
475 const cyhal_resource_pin_mapping_t *vplus_map = _cyhal_utils_get_resource(vplus,
476 cyhal_pin_map_adcmic_gpio_adc_in,
477 sizeof(cyhal_pin_map_adcmic_gpio_adc_in)/sizeof(cyhal_pin_map_adcmic_gpio_adc_in[0]),
478 &(adc->resource));
479
480 if(NULL == vplus_map)
481 {
482 result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
483 }
484
485 if(CY_RSLT_SUCCESS == result && CYHAL_ADC_VNEG != vminus)
486 {
487 result = CYHAL_ADC_RSLT_BAD_ARGUMENT; /* Only single-ended supported on this ADC HW */
488 }
489
490 if(CY_RSLT_SUCCESS == result)
491 {
492 /* For this block, we reuse the drive mode field to store the bit index on the adcmic.
493 * Pull off and save that value, but provide the real required drive mode for connecting
494 */
495 uint8_t bit_index = vplus_map->channel_num;
496 obj->channel_sel = _cyhal_adcmic_convert_channel_sel(bit_index);
497 result = _cyhal_utils_reserve_and_connect(vplus_map, CYHAL_PIN_MAP_DRIVE_MODE_ADCMIC_GPIO_ADC_IN);
498 }
499
500 uint8_t chosen_channel = 0;
501 if (CY_RSLT_SUCCESS == result)
502 {
503 obj->vplus = vplus;
504 // Find the first available channel
505 for(chosen_channel = 0; chosen_channel < _CYHAL_ADCMIC_NUM_CHANNELS(obj->adc); ++chosen_channel)
506 {
507 if(NULL == adc->channel_config[chosen_channel])
508 {
509 break;
510 }
511 }
512 if (chosen_channel >= _CYHAL_ADCMIC_NUM_CHANNELS(obj->adc))
513 result = CYHAL_ADC_RSLT_NO_CHANNELS;
514 }
515
516 if(CY_RSLT_SUCCESS == result)
517 {
518 // Don't set the ADC until here so that free knows whether we have allocated
519 // the channel on the parent ADC instance (and therefore doesn't try to free it if
520 // something fails further up)
521 obj->adc = adc;
522 obj->channel_idx = chosen_channel;
523 obj->adc->channel_config[chosen_channel] = obj;
524 }
525
526 if(CY_RSLT_SUCCESS == result)
527 {
528 result = cyhal_adc_channel_configure(obj, cfg);
529 }
530
531 if(CY_RSLT_SUCCESS != result)
532 {
533 cyhal_adc_channel_free(obj);
534 }
535
536 return result;
537 }
538
cyhal_adc_channel_configure(cyhal_adc_channel_t * obj,const cyhal_adc_channel_config_t * config)539 cy_rslt_t cyhal_adc_channel_configure(cyhal_adc_channel_t *obj, const cyhal_adc_channel_config_t *config)
540 {
541 CY_ASSERT(NULL != obj);
542
543 cy_rslt_t result = CY_RSLT_SUCCESS;
544 if((config->min_acquisition_ns > _CYHAL_ADCMIC_ACQUISITION_TIME_NS) || config->enable_averaging)
545 {
546 result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
547 }
548 if(CY_RSLT_SUCCESS == result)
549 {
550 obj->enabled = config->enabled;
551 }
552
553 return result;
554 }
555
cyhal_adc_channel_free(cyhal_adc_channel_t * obj)556 void cyhal_adc_channel_free(cyhal_adc_channel_t *obj)
557 {
558 if(obj->adc != NULL)
559 {
560 // Disable the channel. No per-channel configuration was statically set, so nothing to unconfigure
561 obj->adc->channel_config[obj->channel_idx] = NULL;
562 obj->adc = NULL;
563 }
564
565 if(false == obj->adc->owned_by_configurator)
566 {
567 _cyhal_utils_release_if_used(&(obj->vplus));
568 }
569 }
570
cyhal_adc_read_u16(const cyhal_adc_channel_t * obj)571 uint16_t cyhal_adc_read_u16(const cyhal_adc_channel_t *obj)
572 {
573 Cy_ADCMic_SelectDcChannel(obj->adc->base, obj->channel_sel);
574 /* Cy_ADCMic_IsEndConversion relies on and clears the EOS interrupt status bit.
575 * We don't know how this read will be used in combination with interrupts,
576 * so implement our own interrupt-driven EOS flag.
577 */
578 Cy_ADCMic_ClearInterrupt(obj->adc->base, CY_ADCMIC_INTR_DC);
579 Cy_ADCMic_EnableInterrupt(obj->adc->base, CY_ADCMIC_INTR_DC);
580 obj->adc->conversion_complete = false;
581 Cy_ADCMic_DisableInterrupt(obj->adc->base, CY_ADCMIC_INTR_DC);
582 int retry = 1000;
583 while(!obj->adc->conversion_complete && retry > 0)
584 {
585 cyhal_system_delay_us(1u); /* Conversion should take ~2 us */
586 --retry;
587 }
588
589 uint16_t result = Cy_ADCMic_GetDcResult(obj->adc->base);
590 return result;
591 }
592
cyhal_adc_read(const cyhal_adc_channel_t * obj)593 int32_t cyhal_adc_read(const cyhal_adc_channel_t *obj)
594 {
595 return (int32_t)cyhal_adc_read_u16(obj);
596 }
597
cyhal_adc_read_uv(const cyhal_adc_channel_t * obj)598 int32_t cyhal_adc_read_uv(const cyhal_adc_channel_t *obj)
599 {
600 CY_ASSERT(NULL != obj);
601
602 int32_t counts = cyhal_adc_read(obj);
603 return Cy_ADCMic_CountsTo_uVolts(obj->adc->base, counts);
604 }
605
_cyhal_adcmic_start_async_read(cyhal_adc_t * obj,size_t num_scan,int32_t * result_list)606 static void _cyhal_adcmic_start_async_read(cyhal_adc_t* obj, size_t num_scan, int32_t* result_list)
607 {
608 CY_ASSERT(NULL == obj->async_buff_next); /* Transfer already in progress */
609 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
610 obj->current_channel_index = 0;
611 obj->async_scans_remaining = num_scan;
612 obj->async_buff_next = result_list;
613 _cyhal_adcmic_find_next_channel(obj, &(obj->current_channel_index), &(obj->async_buff_next));
614 if(NULL == obj->channel_config[obj->current_channel_index]
615 || (false == obj->channel_config[obj->current_channel_index]->enabled))
616 {
617 /* No enabled channels found, we're done */
618 obj->async_buff_next = NULL;
619 obj->async_scans_remaining = 0u;
620 }
621 else
622 {
623 Cy_ADCMic_SelectDcChannel(obj->base, obj->channel_config[obj->current_channel_index]->channel_sel);
624 Cy_ADCMic_ClearInterrupt(obj->base, CY_ADCMIC_INTR_DC);
625 Cy_ADCMic_EnableInterrupt(obj->base, CY_ADCMIC_INTR_DC);
626 }
627 cyhal_system_critical_section_exit(savedIntrStatus);
628 }
629
cyhal_adc_read_async(cyhal_adc_t * obj,size_t num_scan,int32_t * result_list)630 cy_rslt_t cyhal_adc_read_async(cyhal_adc_t* obj, size_t num_scan, int32_t* result_list)
631 {
632 CY_ASSERT(NULL != obj);
633 obj->async_transfer_in_uv = false;
634 _cyhal_adcmic_start_async_read(obj, num_scan, result_list);
635 return CY_RSLT_SUCCESS;
636 }
637
cyhal_adc_read_async_uv(cyhal_adc_t * obj,size_t num_scan,int32_t * result_list)638 cy_rslt_t cyhal_adc_read_async_uv(cyhal_adc_t* obj, size_t num_scan, int32_t* result_list)
639 {
640 CY_ASSERT(NULL != obj);
641 obj->async_transfer_in_uv = true;
642 _cyhal_adcmic_start_async_read(obj, num_scan, result_list);
643 return CY_RSLT_SUCCESS;
644 }
645
cyhal_adc_set_async_mode(cyhal_adc_t * obj,cyhal_async_mode_t mode,uint8_t dma_priority)646 cy_rslt_t cyhal_adc_set_async_mode(cyhal_adc_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)
647 {
648 CY_UNUSED_PARAMETER(obj);
649 CY_UNUSED_PARAMETER(dma_priority);
650 CY_ASSERT(NULL != obj);
651 CY_ASSERT(NULL == obj->async_buff_next); /* Can't swap mode while a transfer is running */
652 if(mode == CYHAL_ASYNC_DMA)
653 {
654 /* DMA not supported on this HW. CPU intervention is required after every sample anyway,
655 * so triggering the DMA would involve more overhead than just CPU copying the 32 bits */
656 return CYHAL_ADC_RSLT_BAD_ARGUMENT;
657 }
658 else
659 {
660 return CY_RSLT_SUCCESS;
661 }
662 }
663
cyhal_adc_register_callback(cyhal_adc_t * obj,cyhal_adc_event_callback_t callback,void * callback_arg)664 void cyhal_adc_register_callback(cyhal_adc_t *obj, cyhal_adc_event_callback_t callback, void *callback_arg)
665 {
666 CY_ASSERT(NULL != obj);
667
668 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
669 obj->callback_data.callback = (cy_israddress) callback;
670 obj->callback_data.callback_arg = callback_arg;
671 cyhal_system_critical_section_exit(savedIntrStatus);
672 }
673
cyhal_adc_enable_event(cyhal_adc_t * obj,cyhal_adc_event_t event,uint8_t intr_priority,bool enable)674 void cyhal_adc_enable_event(cyhal_adc_t *obj, cyhal_adc_event_t event, uint8_t intr_priority, bool enable)
675 {
676 /* Continuous scanning isn't supported (the hardware is converting continuously, but it's not automatically
677 * scanning across all enabled channels). We listen for EOC internally at times but that doesn't correspond to
678 * the EOS event. So there's no interrupts that we need to enable/disable here. */
679 if(enable)
680 {
681 obj->user_enabled_events |= event;
682 }
683 else
684 {
685 obj->user_enabled_events &= ~event;
686 }
687
688 IRQn_Type irqn = _cyhal_adcmic_irq_n[obj->resource.block_num];
689 NVIC_SetPriority(irqn, intr_priority);
690 }
691
cyhal_adc_connect_digital(cyhal_adc_t * obj,cyhal_source_t source,cyhal_adc_input_t input)692 cy_rslt_t cyhal_adc_connect_digital(cyhal_adc_t *obj, cyhal_source_t source, cyhal_adc_input_t input)
693 {
694 /* No trigger inputs supported on this hardware */
695 CY_UNUSED_PARAMETER(obj);
696 CY_UNUSED_PARAMETER(source);
697 CY_UNUSED_PARAMETER(input);
698 return CYHAL_ADC_RSLT_BAD_ARGUMENT;
699 }
700
cyhal_adc_enable_output(cyhal_adc_t * obj,cyhal_adc_output_t output,cyhal_source_t * source)701 cy_rslt_t cyhal_adc_enable_output(cyhal_adc_t *obj, cyhal_adc_output_t output, cyhal_source_t *source)
702 {
703 /* No trigger outputs supported on this hardware */
704 CY_UNUSED_PARAMETER(obj);
705 CY_UNUSED_PARAMETER(output);
706 CY_UNUSED_PARAMETER(source);
707 return CYHAL_ADC_RSLT_BAD_ARGUMENT;
708 }
709
cyhal_adc_disconnect_digital(cyhal_adc_t * obj,cyhal_source_t source,cyhal_adc_input_t input)710 cy_rslt_t cyhal_adc_disconnect_digital(cyhal_adc_t *obj, cyhal_source_t source, cyhal_adc_input_t input)
711 {
712 /* No trigger inputs supported on this hardware */
713 CY_UNUSED_PARAMETER(obj);
714 CY_UNUSED_PARAMETER(source);
715 CY_UNUSED_PARAMETER(input);
716 return CYHAL_ADC_RSLT_BAD_ARGUMENT;
717 }
718
cyhal_adc_disable_output(cyhal_adc_t * obj,cyhal_adc_output_t output)719 cy_rslt_t cyhal_adc_disable_output(cyhal_adc_t *obj, cyhal_adc_output_t output)
720 {
721 /* No trigger outputs supported on this hardware */
722 CY_UNUSED_PARAMETER(obj);
723 CY_UNUSED_PARAMETER(output);
724 return CYHAL_ADC_RSLT_BAD_ARGUMENT;
725 }
726
727 #if defined(__cplusplus)
728 }
729 #endif
730
731 #endif /* _CYHAL_DRIVER_AVAILABLE_ADC_MIC */
732