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