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