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