Files
Beslan 0ef1717155
Some checks failed
Basic Checks / license-check (push) Has been cancelled
Basic Checks / include-check (push) Has been cancelled
Basic Checks / style-check (push) Has been cancelled
Basic Checks / docs-check (push) Has been cancelled
Basic Checks / python-tests (push) Has been cancelled
Basic Checks / pin-validation (push) Has been cancelled
Basic Checks / cmake-checks (push) Has been cancelled
Basic Checks / frozen-tools-check (push) Has been cancelled
Publish or Update docker image for head of branch / prepare-tags (push) Has been cancelled
Release or update docker image for a released mbed-os version / prepare-tags (push) Has been cancelled
Publish or Update docker image for head of branch / build-container (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/amd64) (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/arm64) (push) Has been cancelled
Publish or Update docker image for head of branch / deploy-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / build-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/amd64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/arm64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / deploy-container (push) Has been cancelled
Prune temporary docker images / prune-images (push) Has been cancelled
Mirror mbed-os-6.15.0
2026-07-10 18:42:39 +03:00

1736 lines
62 KiB
C

/***************************************************************************/ /**
* \file cyhal_adc.c
*
* \brief
* Provides a high level interface for interacting with the Cypress Analog/Digital
* convert. This interface abstracts out the chip specific details. If any chip
* specific functionality is necessary, or performance is critical the low level
* functions can be used directly.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
/**
* \addtogroup group_hal_impl_adc ADC (Analog Digital Converter)
* \ingroup group_hal_impl
* \{
* \section cyhal_adc_impl_features Features
* The CAT1/CAT2 (PMG/PSoC 4/PSoC 6) ADC supports the following features:
* * Resolution: 12 bit
* * Only @ref CYHAL_POWER_LEVEL_DEFAULT and CYHAL_POWER_LEVEL_OFF are defined. The default power
* level will automatically adjust based on smple rate.
* * Average counts: 2, 4, 8, 16, 32, 64, 128, 256
* * Up to four unique acquisition times.
* * DMA-based transfer when using @ref cyhal_adc_read_async. When using @ref cyhal_adc_read_async_uv,
* only interrupt-driven software copy is supported.
* \} group_hal_impl_adc
*/
#include <cmsis_compiler.h>
#include "cyhal_adc.h"
#include "cyhal_analog_common.h"
#include "cyhal_clock.h"
#include "cyhal_dma.h"
#include "cyhal_gpio.h"
#include "cyhal_hwmgr.h"
#include "cyhal_utils.h"
#include "cyhal_interconnect.h"
#include "cyhal_syspm.h"
#include "cyhal_system.h"
#include <string.h>
#if defined(CY_IP_MXS40PASS_SAR_INSTANCES)
#define _CYHAL_ADC_SAR_INSTANCES CY_IP_MXS40PASS_SAR_INSTANCES
#elif defined(CY_IP_M0S8PASS4A_INSTANCES)
#define _CYHAL_ADC_SAR_INSTANCES CY_IP_M0S8PASS4A_INSTANCES
#endif
#if defined(_CYHAL_ADC_SAR_INSTANCES)
#if defined(__cplusplus)
extern "C"
{
#endif
// The PDL for M0S8 PASS doesn't take the register as an argument; it always writes to MUX_SWITCH0
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
#define _CYHAL_ADC_SARSEQ_STATE(state) (state)
#define _CYHAL_ADC_SWITCH_STATE(state) (state)
#define _CYHAL_ADC_SET_SWITCH(base, mask, state) Cy_SAR_SetAnalogSwitch((base), (mask), (state))
#else
#define _CYHAL_ADC_SWITCH_STATE(state) ((state) ? CY_SAR_SWITCH_CLOSE : CY_SAR_SWITCH_OPEN)
#define _CYHAL_ADC_SARSEQ_STATE(state) ((state) ? CY_SAR_SWITCH_SEQ_CTRL_ENABLE : CY_SAR_SWITCH_SEQ_CTRL_DISABLE)
#define _CYHAL_ADC_SET_SWITCH(base, mask, state) Cy_SAR_SetAnalogSwitch((base), CY_SAR_MUX_SWITCH0, (mask), _CYHAL_ADC_SWITCH_STATE((state)))
#endif
static SAR_Type *const _cyhal_adc_base[] =
{
#if (CY_IP_MXS40PASS_SAR_INSTANCES == 1)
SAR,
#else
#if (_CYHAL_ADC_SAR_INSTANCES >= 1)
SAR0,
#endif
#if (_CYHAL_ADC_SAR_INSTANCES >= 2)
SAR1,
#endif
#if (_CYHAL_ADC_SAR_INSTANCES >= 3)
#warning Unhandled SAR instance count
#endif
#endif
};
static const en_clk_dst_t _cyhal_adc_clock[] =
{
#if (CY_IP_MXS40PASS_SAR_INSTANCES == 1)
PCLK_PASS_CLOCK_SAR,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 1)
PCLK_PASS0_CLOCK_SAR,
#elif (CY_IP_MXS40PASS_SAR_INSTANCES == 2)
PCLK_PASS_CLOCK_SAR0,
PCLK_PASS_CLOCK_SAR1,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 2)
PCLK_PASS0_CLOCK_SAR,
PCLK_PASS1_CLOCK_SAR,
#else
#warning Unhandled SAR instance count
#endif
};
static const cyhal_source_t _cyhal_adc_tr_out[] =
{
#if (CY_IP_MXS40PASS_SAR_INSTANCES == 1)
CYHAL_TRIGGER_PASS_TR_SAR_OUT,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 1)
CYHAL_TRIGGER_PASS0_TR_SAR_OUT,
#elif (CY_IP_MXS40PASS_SAR_INSTANCES == 2)
CYHAL_TRIGGER_PASS_TR_SAR_OUT0,
CYHAL_TRIGGER_PASS_TR_SAR_OUT1,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 2)
CYHAL_TRIGGER_PASS0_TR_SAR_OUT,
CYHAL_TRIGGER_PASS1_TR_SAR_OUT,
#else
#warning Unhandled SAR instance count
#endif
};
static const cyhal_dest_t _cyhal_adc_tr_in[] =
{
#if (CY_IP_MXS40PASS_SAR_INSTANCES == 1)
CYHAL_TRIGGER_PASS_TR_SAR_IN,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 1)
CYHAL_TRIGGER_PASS0_TR_SAR_IN,
#elif (CY_IP_MXS40PASS_SAR_INSTANCES == 2)
CYHAL_TRIGGER_PASS_TR_SAR_IN0,
CYHAL_TRIGGER_PASS_TR_SAR_IN1,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 2)
CYHAL_TRIGGER_PASS0_TR_SAR_IN,
CYHAL_TRIGGER_PASS1_TR_SAR_IN,
#else
#warning Unhandled SAR instance count
#endif
};
static cyhal_adc_t* _cyhal_adc_config_structs[_CYHAL_ADC_SAR_INSTANCES];
static const IRQn_Type _cyhal_adc_irq_n[] =
{
#if (CY_IP_MXS40PASS_SAR_INSTANCES == 1)
pass_interrupt_sar_IRQn,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 1)
pass_0_interrupt_sar_IRQn,
#elif (CY_IP_MXS40PASS_SAR_INSTANCES == 2)
pass_interrupt_sar_0_IRQn,
pass_interrupt_sar_1_IRQn,
#elif (CY_IP_M0S8PASS4A_INSTANCES == 2)
pass_0_interrupt_sar_IRQn,
pass_0_interrupt_sar_IRQn,
#else
#warning Unhandled SAR instance count
#endif
};
static uint8_t _cyhal_adc_get_block_from_irqn(IRQn_Type irqn)
{
switch (irqn)
{
#if (CY_CPU_CORTEX_M4 || CY_IP_M0S8PASS4A_INSTANCES) // M0S8 only has one processor, a CM0 variant
#if (CY_IP_MXS40PASS_SAR_INSTANCES == 1)
case pass_interrupt_sar_IRQn:
return 0;
#elif (CY_IP_M0S8PASS4A_INSTANCES == 1)
case pass_0_interrupt_sar_IRQn:
return 0;
#elif (CY_IP_MXS40PASS_SAR_INSTANCES == 2)
case pass_interrupt_sar_0_IRQn:
return 0;
case pass_interrupt_sar_1_IRQn:
return 1;
#elif (CY_IP_M0S8PASS4A_INSTANCES == 2)
case pass_0_interrupt_sar_IRQn:
return 0;
case pass_1_interrupt_sar_IRQn:
return 1;
#else
#warning Unhandled SAR instance count
#endif
#endif /* (CY_CPU_CORTEX_M4 || CY_IP_M0S8PASS4A_INSTANCES) */
default:
CY_ASSERT(false); // Should never be called with a non-SAR IRQn
return 0;
}
}
#if defined(CY_IP_MXS40PASS_SAR_INSTANCES)
/* OR in the following user-configurable values: vref, bypass, vneg, */
#define _CYHAL_ADC_DEFAULT_CTRL ((uint32_t)CY_SAR_VREF_PWR_100 | (uint32_t)CY_SAR_VREF_SEL_BGR \
| (uint32_t)CY_SAR_BYPASS_CAP_DISABLE | (uint32_t)CY_SAR_CTRL_NEGVREF_HW \
| (uint32_t)CY_SAR_CTRL_COMP_DLY_12 | (uint32_t)CY_SAR_COMP_PWR_100 \
| (uint32_t)CY_SAR_DEEPSLEEP_SARMUX_OFF | (uint32_t)CY_SAR_SARSEQ_SWITCH_ENABLE)
/* Default configuration. OR in the average count, and average mode */
#define _CYHAL_ADC_DEFAULT_SAMPLE ((uint32_t)CY_SAR_RIGHT_ALIGN | (uint32_t)CY_SAR_TRIGGER_MODE_FW_ONLY \
| (uint32_t)CY_SAR_SINGLE_ENDED_SIGNED | (uint32_t)CY_SAR_DIFFERENTIAL_SIGNED \
| (uint32_t)CY_SAR_TRIGGER_MODE_FW_ONLY)
static const cy_stc_sar_config_t _CYHAL_ADC_DEFAULT_PDL_CONFIG =
{
/* .ctrl is populated from _CYHAL_ADC_DEFAULT_CTRL plus the user's configuration */
/* .sampleCtrl is puopulated from _CYHAL_ADC_DEFAULT_SAMPLE plus the user's configuration */
.sampleTime01 = (10UL << SAR_SAMPLE_TIME01_SAMPLE_TIME0_Pos), // Sample times 1, 2, and 3 are not used
.sampleTime23 = 0UL,
.rangeThres = (0UL << CY_SAR_RANGE_HIGH_SHIFT) | (0UL << CY_SAR_RANGE_LOW_SHIFT),
.rangeCond = CY_SAR_RANGE_COND_BELOW,
.chanEn = 0UL,
.chanConfig = { 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL},
.intrMask = (uint32_t) CY_SAR_INTR_EOS,
.satIntrMask = 0UL,
.rangeIntrMask = 0UL,
.muxSwitch = 0UL,
.muxSwitchSqCtrl = 0UL,
.configRouting = true,
/* .vrefMvValue is populated from the user's configuration */
};
#endif
#define _CYHAL_ADC_RESOLUTION 12u
#define _CYHAL_ADC_INTERNAL_VREF_MV 1200UL
#define _CYHAL_ADC_CONVERSION_CYCLES (_CYHAL_ADC_RESOLUTION + 2)
static const cyhal_adc_config_t _CYHAL_ADC_DEFAULT_CONFIG =
{
.resolution = _CYHAL_ADC_RESOLUTION,
.average_count = 1,
.average_mode_flags = CYHAL_ADC_AVG_MODE_AVERAGE,
.continuous_scanning = true,
.vneg = CYHAL_ADC_VNEG_VREF,
.vref = CYHAL_ADC_REF_INTERNAL,
.ext_vref = NC,
.ext_vref_mv = 0u,
.is_bypassed = false,
.bypass_pin = NC,
};
/*******************************************************************************
* Internal helper functions
*******************************************************************************/
static uint8_t _cyhal_adc_max_configured_channel(const cyhal_adc_t* obj)
{
uint8_t max = 0;
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
if(NULL != obj->channel_config[i])
{
max = i;
}
}
return max;
}
static uint32_t _cyhal_adc_get_mux_switch_control(cyhal_gpio_t gpio)
{
static const uint32_t mux_lookup[] =
{
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P0_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P1_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P2_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P3_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P4_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P5_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P6_Msk,
SAR_MUX_SWITCH_HW_CTRL_MUX_HW_CTRL_P7_Msk,
#else
(uint32_t)CY_SAR_MUX_SQ_CTRL_P0,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P1,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P2,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P3,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P4,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P5,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P6,
(uint32_t)CY_SAR_MUX_SQ_CTRL_P7
#endif
};
uint8_t pin = CYHAL_GET_PIN(gpio);
CY_ASSERT(pin < sizeof(mux_lookup)/sizeof(mux_lookup[0]));
return mux_lookup[pin];
}
static uint32_t _cyhal_adc_get_fw_switch_control(cyhal_gpio_t gpio, bool is_vplus)
{
static const uint32_t vplus_lookup[] =
{
(uint32_t)CY_SAR_MUX_FW_P0_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P1_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P2_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P3_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P4_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P5_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P6_VPLUS,
(uint32_t)CY_SAR_MUX_FW_P7_VPLUS
};
static const uint32_t vminus_lookup[] =
{
(uint32_t)CY_SAR_MUX_FW_P0_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P1_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P2_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P3_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P4_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P5_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P6_VMINUS,
(uint32_t)CY_SAR_MUX_FW_P7_VMINUS
};
uint8_t pin = CYHAL_GET_PIN(gpio);
CY_ASSERT(pin < sizeof(vplus_lookup)/sizeof(vplus_lookup[0]));
return (uint32_t)(is_vplus ? vplus_lookup[pin] : vminus_lookup[pin]);
}
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
static uint32_t _cyhal_adc_get_pin_addr(cyhal_gpio_t gpio)
{
// On this version of the PASS, there is no explicit vminus address; it is implied by vplus
static const cy_en_sar_chan_config_port_pin_addr_t vplus_lookup[] =
{
CY_SAR_ADDR_SARMUX_0,
CY_SAR_ADDR_SARMUX_1,
CY_SAR_ADDR_SARMUX_2,
CY_SAR_ADDR_SARMUX_3,
CY_SAR_ADDR_SARMUX_4,
CY_SAR_ADDR_SARMUX_5,
CY_SAR_ADDR_SARMUX_6,
CY_SAR_ADDR_SARMUX_7
};
uint8_t pin = CYHAL_GET_PIN(gpio);
CY_ASSERT(pin < sizeof(vplus_lookup)/sizeof(vplus_lookup[0]));
return (uint32_t)vplus_lookup[pin];
}
#else
static uint32_t _cyhal_adc_get_pin_addr(cyhal_gpio_t gpio, bool is_vplus)
{
static const cy_en_sar_chan_config_pos_pin_addr_t vplus_lookup[] =
{
CY_SAR_CHAN_POS_PIN_ADDR_0,
CY_SAR_CHAN_POS_PIN_ADDR_1,
CY_SAR_CHAN_POS_PIN_ADDR_2,
CY_SAR_CHAN_POS_PIN_ADDR_3,
CY_SAR_CHAN_POS_PIN_ADDR_4,
CY_SAR_CHAN_POS_PIN_ADDR_5,
CY_SAR_CHAN_POS_PIN_ADDR_6,
CY_SAR_CHAN_POS_PIN_ADDR_7
};
static const cy_en_sar_chan_config_neg_pin_addr_t vminus_lookup[] =
{
CY_SAR_CHAN_NEG_PIN_ADDR_0,
CY_SAR_CHAN_NEG_PIN_ADDR_1,
CY_SAR_CHAN_NEG_PIN_ADDR_2,
CY_SAR_CHAN_NEG_PIN_ADDR_3,
CY_SAR_CHAN_NEG_PIN_ADDR_4,
CY_SAR_CHAN_NEG_PIN_ADDR_5,
CY_SAR_CHAN_NEG_PIN_ADDR_6,
CY_SAR_CHAN_NEG_PIN_ADDR_7
};
uint8_t pin = CYHAL_GET_PIN(gpio);
CY_ASSERT(pin < sizeof(vplus_lookup)/sizeof(vplus_lookup[0]));
uint32_t pin_addr = is_vplus ? (uint32_t)vplus_lookup[pin] : (uint32_t)vminus_lookup[pin];
return is_vplus ? (pin_addr | CY_SAR_POS_PORT_ADDR_SARMUX) : (pin_addr | CY_SAR_NEG_PORT_ADDR_SARMUX);
}
#endif
static cy_en_sar_ctrl_vref_sel_t _cyhal_adc_convert_vref(cyhal_adc_vref_t vref)
{
switch(vref)
{
case CYHAL_ADC_REF_INTERNAL:
return CY_SAR_VREF_SEL_BGR;
case CYHAL_ADC_REF_EXTERNAL:
return CY_SAR_VREF_SEL_EXT;
case CYHAL_ADC_REF_VDDA:
return CY_SAR_VREF_SEL_VDDA;
case CYHAL_ADC_REF_VDDA_DIV_2:
return CY_SAR_VREF_SEL_VDDA_DIV_2;
default:
CY_ASSERT(false);
return CY_SAR_VREF_SEL_BGR;
}
}
static cy_rslt_t _cyhal_adc_convert_average_count(uint32_t hal_count, cy_en_sar_sample_ctrl_avg_cnt_t* pdl_count)
{
switch(hal_count)
{
case 1: /* Average count of 1 is achieved by disabling averaging for all channels */
case 2:
*pdl_count = CY_SAR_AVG_CNT_2;
return CY_RSLT_SUCCESS;
case 4:
*pdl_count = CY_SAR_AVG_CNT_4;
return CY_RSLT_SUCCESS;
case 8:
*pdl_count = CY_SAR_AVG_CNT_8;
return CY_RSLT_SUCCESS;
case 16:
*pdl_count = CY_SAR_AVG_CNT_16;
return CY_RSLT_SUCCESS;
case 32:
*pdl_count = CY_SAR_AVG_CNT_32;
return CY_RSLT_SUCCESS;
case 64:
*pdl_count = CY_SAR_AVG_CNT_64;
return CY_RSLT_SUCCESS;
case 128:
*pdl_count = CY_SAR_AVG_CNT_128;
return CY_RSLT_SUCCESS;
case 256:
*pdl_count = CY_SAR_AVG_CNT_256;
return CY_RSLT_SUCCESS;
default:
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
}
static uint32_t _cyhal_adc_get_vref_mv(const cyhal_adc_config_t* hal_config)
{
switch(hal_config->vref)
{
case CYHAL_ADC_REF_INTERNAL:
return _CYHAL_ADC_INTERNAL_VREF_MV;
case CYHAL_ADC_REF_EXTERNAL:
CY_ASSERT(hal_config->ext_vref_mv > 0); // Should have been error checked already
return hal_config->ext_vref_mv;
case CYHAL_ADC_REF_VDDA_DIV_2:
return cyhal_syspm_get_supply_voltage(CYHAL_VOLTAGE_SUPPLY_VDDA) / 2;
default:
CY_ASSERT(CYHAL_ADC_REF_VDDA == hal_config->vref);
return cyhal_syspm_get_supply_voltage(CYHAL_VOLTAGE_SUPPLY_VDDA);
}
}
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
static cy_rslt_t _cyhal_adc_convert_resolution(uint8_t hal_resolution, cy_en_sar_sample_ctrl_sub_resolution_t *pdl_resolution)
{
switch(hal_resolution)
{
case 10:
*pdl_resolution = CY_SAR_SUB_RESOLUTION_10B;
break;
case 8:
*pdl_resolution = CY_SAR_SUB_RESOLUTION_8B;
break;
default:
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
return CY_RSLT_SUCCESS;
}
static void _cyhal_adc_extract_channel_conf(cyhal_adc_t* adc, cy_stc_sar_channel_config_t* channel_configs)
{
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
channel_configs[i].addr = (cy_en_sar_chan_config_port_pin_addr_t)_FLD2VAL(SAR_CHAN_CONFIG_PIN_ADDR, adc->base->CHAN_CONFIG[i]);
channel_configs[i].addr |= (cy_en_sar_chan_config_port_pin_addr_t)_FLD2VAL(SAR_CHAN_CONFIG_PORT_ADDR, adc->base->CHAN_CONFIG[i]);
channel_configs[i].avgEn = _FLD2BOOL(SAR_CHAN_CONFIG_AVG_EN, adc->base->CHAN_CONFIG[i]);
channel_configs[i].sampleTimeSel = _FLD2VAL(SAR_CHAN_CONFIG_SAMPLE_TIME_SEL, adc->base->CHAN_CONFIG[i]);
channel_configs[i].differential = _FLD2BOOL(SAR_CHAN_CONFIG_DIFFERENTIAL_EN, adc->base->CHAN_CONFIG[i]);
channel_configs[i].resolution = (cy_en_sar_channel_ctrl_resolution_t)_FLD2VAL(SAR_CHAN_CONFIG_RESOLUTION, adc->base->CHAN_CONFIG[i]);
}
}
static cy_rslt_t _cyhal_adc_populate_pdl_config(const cyhal_adc_config_t* hal_config, cy_stc_sar_config_t* pdl_config,
cy_stc_sar_channel_config_t* channel_configs /* Array, length CY_SAR_SEQ_NUM_CHANNELS */)
{
memset(pdl_config, 0, sizeof(cy_stc_sar_config_t));
cy_rslt_t result = CY_RSLT_SUCCESS;
pdl_config->vrefSel = _cyhal_adc_convert_vref(hal_config->vref);
pdl_config->vrefBypCapEn = hal_config->is_bypassed;
pdl_config->negSel = (hal_config->vneg == CYHAL_ADC_VNEG_VSSA) ? CY_SAR_NEG_SEL_VSSA_KELVIN : CY_SAR_NEG_SEL_VREF;
pdl_config->negVref = CY_SAR_NEGVREF_HW;
pdl_config->boostPump = true;
pdl_config->power = CY_SAR_NORMAL_PWR;
pdl_config->sarMuxDsEn = false; // This only enables the routing in deepsleep, not the SAR. And we only use the routing with the SAR
pdl_config->switchDisable = false; // We are using the SARSEQ
if(hal_config->resolution != _CYHAL_ADC_RESOLUTION)
{
result = _cyhal_adc_convert_resolution(hal_config->resolution, &pdl_config->subResolution);
}
if(CY_RSLT_SUCCESS == result)
{
pdl_config->leftAlign = false;
pdl_config->singleEndedSigned = true;
pdl_config->differentialSigned = true;
pdl_config->avgShift = true;
result = _cyhal_adc_convert_average_count(hal_config->average_count, &pdl_config->avgCnt);
}
if(CY_RSLT_SUCCESS == result)
{
pdl_config->trigMode = CY_SAR_TRIGGER_MODE_FW_ONLY;
pdl_config->eosEn = false;
// Will be updated after configuration when we populate sample times after adding channels
pdl_config->sampleTime0 = pdl_config->sampleTime1 = pdl_config->sampleTime2 = pdl_config->sampleTime3 = 10u;
// Skipping range thresholds because we don't expose the range detect feature.
// We always need to populate channel configuration structs here because otherwise the PDL will not
// initialize the default gain value that the channels will need when and if we add them later.
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
pdl_config->channelConfig[i] = &channel_configs[i];
}
// Routing will be configured and channels will be enabled as channels are added
pdl_config->vrefMvValue = _cyhal_adc_get_vref_mv(hal_config);
}
return result;
}
#else
static uint32_t _cyhal_adc_convert_average_mode(uint32_t average_mode_flags)
{
uint32 result = 0;
if(0u != (average_mode_flags & CYHAL_ADC_AVG_MODE_ACCUMULATE))
{
result |= CY_SAR_AVG_MODE_SEQUENTIAL_ACCUM;
}
else if(0u != (average_mode_flags & CYHAL_ADC_AVG_MODE_INTERLEAVED))
{
/* INTERLEAVED on its own does not divide the result back down */
result |= (CY_SAR_AVG_MODE_INTERLEAVED | SAR_SAMPLE_CTRL_AVG_SHIFT_Msk);
}
else
{
result |= CY_SAR_AVG_MODE_SEQUENTIAL_FIXED;
}
return result;
}
/* Populates the PDL config struct with settings from the ADC config struct */
static cy_rslt_t _cyhal_adc_populate_pdl_config(const cyhal_adc_config_t* hal_config, cy_stc_sar_config_t* pdl_config)
{
memset(pdl_config, 0, sizeof(cy_stc_sar_config_t));
if(hal_config->resolution != _CYHAL_ADC_RESOLUTION) /* SAR does not support configurable resolution */
{
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
if (((hal_config->average_mode_flags & CYHAL_ADC_AVG_MODE_ACCUMULATE) > 0) &&
((hal_config->average_mode_flags & CYHAL_ADC_AVG_MODE_INTERLEAVED) > 0))
{
/* Accumulate mode is not compatible with interleaved averaging */
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
*pdl_config = _CYHAL_ADC_DEFAULT_PDL_CONFIG;
uint32_t ctrl = _CYHAL_ADC_DEFAULT_CTRL;
ctrl |= (uint32_t)_cyhal_adc_convert_vref(hal_config->vref);
ctrl |= (hal_config->is_bypassed) ? CY_SAR_BYPASS_CAP_ENABLE : CY_SAR_BYPASS_CAP_DISABLE;
ctrl |= (hal_config->vneg == CYHAL_ADC_VNEG_VSSA) ? CY_SAR_NEG_SEL_VSSA_KELVIN : CY_SAR_NEG_SEL_VREF;
uint32_t sample_ctrl = _CYHAL_ADC_DEFAULT_SAMPLE;
cy_en_sar_sample_ctrl_avg_cnt_t pdl_avg;
cy_rslt_t result = _cyhal_adc_convert_average_count(hal_config->average_count, &pdl_avg);
if(CY_RSLT_SUCCESS == result)
{
sample_ctrl |= (uint32_t)pdl_avg;
sample_ctrl |= _cyhal_adc_convert_average_mode(hal_config->average_mode_flags);
pdl_config->ctrl = ctrl;
pdl_config->sampleCtrl = sample_ctrl;
pdl_config->vrefMvValue = _cyhal_adc_get_vref_mv(hal_config);
}
return result;
}
#endif
static void _cyhal_adc_irq_handler(void)
{
/* The only enabled event is scan finished */
cyhal_adc_event_t hal_event = CYHAL_ADC_EOS;
IRQn_Type irqn = _CYHAL_UTILS_GET_CURRENT_IRQN();
uint8_t block = _cyhal_adc_get_block_from_irqn(irqn);
cyhal_adc_t* obj = _cyhal_adc_config_structs[block];
Cy_SAR_ClearInterrupt(obj->base, CY_SAR_INTR_EOS);
obj->conversion_complete = true;
uint8_t num_channels = _cyhal_adc_max_configured_channel(obj) + 1;
if(obj->async_scans_remaining > 0)
{
/* Can't read millivolts out via DMA */
if(CYHAL_ASYNC_SW == obj->async_mode || obj->async_transfer_in_uv)
{
for(uint8_t i = 0; i < num_channels; ++i)
{
int32_t counts = Cy_SAR_GetResult32(obj->base, i);
*obj->async_buff_next = obj->async_transfer_in_uv ? Cy_SAR_CountsTo_uVolts(obj->base, i, counts) : counts;
++obj->async_buff_next;
}
--(obj->async_scans_remaining);
if(0 == obj->async_scans_remaining)
{
obj->async_buff_next = obj->async_buff_orig = NULL;
hal_event |= CYHAL_ADC_ASYNC_READ_COMPLETE;
}
else if(false == obj->continuous_scanning)
{
Cy_SAR_StartConvert(obj->base, CY_SAR_START_CONVERT_SINGLE_SHOT);
}
/* If we're continously scanning, another scan will be kicked off automatically
* so we don't need to do anything */
}
else
{
CY_ASSERT(CYHAL_ASYNC_DMA == obj->async_mode);
#if defined(CY_IP_M0S8CPUSSV3_DMAC) || defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M4CPUSS_DMAC)
cyhal_dma_cfg_t dma_config =
{
.src_addr = (uint32_t)obj->base->CHAN_RESULT,
.src_increment = 1u,
.dst_addr = (uint32_t)obj->async_buff_next,
.dst_increment = 1u,
.transfer_width = 32u,
.length = num_channels,
.burst_size = 0u,
.action = CYHAL_DMA_TRANSFER_FULL
};
// Configure needs to happen after we've manipulated the descriptor config
cy_rslt_t result = cyhal_dma_configure(&(obj->dma), &dma_config);
if(CY_RSLT_SUCCESS == result)
{
result = cyhal_dma_start_transfer(&(obj->dma));
}
CY_ASSERT(CY_RSLT_SUCCESS == result);
/* Don't increment the buffer here - do that when the DMA completes */
if(false == obj->continuous_scanning)
{
Cy_SAR_StartConvert(obj->base, CY_SAR_START_CONVERT_SINGLE_SHOT);
}
#else
CY_ASSERT(false); // DMA not supported on the current device
#endif //defined(CY_IP_M0S8CPUSSV3_DMAC) || defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M4CPUSS_DMAC)
}
}
if(0 != (hal_event & ((cyhal_adc_event_t)obj->user_enabled_events)))
{
cyhal_adc_event_callback_t callback = (cyhal_adc_event_callback_t)obj->callback_data.callback;
if(NULL != callback)
{
callback(obj->callback_data.callback_arg, (cyhal_adc_event_t)(hal_event & obj->user_enabled_events));
}
}
}
#if defined(CY_IP_M0S8CPUSSV3_DMAC) || defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M4CPUSS_DMAC)
static void _cyhal_adc_dma_handler(void* arg, cyhal_dma_event_t event)
{
CY_ASSERT(CYHAL_DMA_TRANSFER_COMPLETE == event);
CY_UNUSED_PARAMETER(event);
cyhal_adc_t* obj = (cyhal_adc_t*)arg;
CY_ASSERT(CYHAL_ASYNC_DMA == obj->async_mode);
uint8_t num_channels = _cyhal_adc_max_configured_channel(obj) + 1;
CY_ASSERT(false == obj->async_transfer_in_uv);
obj->async_buff_next += num_channels;
--(obj->async_scans_remaining);
if(0 == obj->async_scans_remaining)
{
// DMA doesn't sign extend when we copy from 16 to 32 bits, so do the sign extension
// ourselves once all channel scans are complete.
while(obj->async_buff_orig != obj->async_buff_next)
{
// Mask off the upper two bytes because those contain mirrored status bits which
// are not part of the ADC counts
int16_t sar_result = (int16_t)(0xFFFF & *(obj->async_buff_orig));
*(obj->async_buff_orig) = sar_result;
++(obj->async_buff_orig);
}
obj->async_buff_next = obj->async_buff_orig = NULL;
if(0 != (CYHAL_ADC_ASYNC_READ_COMPLETE & ((cyhal_adc_event_t)obj->user_enabled_events)))
{
cyhal_adc_event_callback_t callback = (cyhal_adc_event_callback_t)obj->callback_data.callback;
if(NULL != callback)
{
callback(obj->callback_data.callback_arg, CYHAL_ADC_ASYNC_READ_COMPLETE);
}
}
}
}
#endif
/*******************************************************************************
* ADC HAL Functions
*******************************************************************************/
cy_rslt_t cyhal_adc_init(cyhal_adc_t *obj, cyhal_gpio_t pin, const cyhal_clock_t *clk)
{
const uint32_t DESIRED_DIVIDER = 8000000u; // 8 MHz. Required range is 1.7 - 18
CY_ASSERT(NULL != obj);
cy_rslt_t result = CY_RSLT_SUCCESS;
memset(obj, 0, sizeof(cyhal_adc_t));
obj->clock.reserved = false;
obj->resource.type = CYHAL_RSC_INVALID;
obj->async_mode = CYHAL_ASYNC_SW;
obj->source = CYHAL_TRIGGER_CPUSS_ZERO;
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
obj->resolution = _CYHAL_ADC_RESOLUTION;
obj->ext_vref = NC;
obj->bypass_pin = NC;
#endif
const cyhal_resource_pin_mapping_t* map = _cyhal_utils_try_alloc(pin, cyhal_pin_map_pass_sarmux_pads, sizeof(cyhal_pin_map_pass_sarmux_pads) / sizeof(cyhal_pin_map_pass_sarmux_pads[0]));
if (NULL == map)
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
cyhal_resource_inst_t adc_inst;
if (CY_RSLT_SUCCESS == result)
{
adc_inst = *map->inst;
/* No need to reserve - try_alloc did so for us already */
}
en_clk_dst_t pclk = (en_clk_dst_t)0;
if (CY_RSLT_SUCCESS == result)
{
obj->resource = adc_inst;
obj->base = _cyhal_adc_base[adc_inst.block_num];
pclk = (en_clk_dst_t)(_cyhal_adc_clock[adc_inst.block_num]);
if (NULL != clk)
{
obj->clock = *clk;
obj->dedicated_clock = false;
}
else if (CY_RSLT_SUCCESS ==
(result = _cyhal_utils_allocate_clock(&(obj->clock), &(obj->resource), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true)))
{
obj->dedicated_clock = true;
}
}
if (CY_RSLT_SUCCESS == result)
{
if (CY_SYSCLK_SUCCESS != Cy_SysClk_PeriphAssignDivider(pclk, (cy_en_divider_types_t)obj->clock.block, obj->clock.channel))
result = CYHAL_ADC_RSLT_FAILED_CLOCK;
}
if (CY_RSLT_SUCCESS == result)
{
if(obj->dedicated_clock)
{
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
uint32_t source_hz = Cy_SysClk_ClkSysGetFrequency();
#else
uint32_t source_hz = Cy_SysClk_ClkPeriGetFrequency();
#endif
uint32_t div = source_hz / DESIRED_DIVIDER;
if (0 == div ||
CY_SYSCLK_SUCCESS != Cy_SysClk_PeriphSetDivider((cy_en_divider_types_t)obj->clock.block, obj->clock.channel, div - 1) ||
CY_SYSCLK_SUCCESS != Cy_SysClk_PeriphEnableDivider((cy_en_divider_types_t)obj->clock.block, obj->clock.channel))
{
result = CYHAL_ADC_RSLT_FAILED_CLOCK;
}
}
}
cy_stc_sar_config_t pdl_config;
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
cy_stc_sar_channel_config_t chan_configs[CY_SAR_SEQ_NUM_CHANNELS];
memset(chan_configs, 0, sizeof(chan_configs));
// No channels have actually been configured yet, so an empty set of config structs is fine here
if (CY_RSLT_SUCCESS == result)
{
result = _cyhal_adc_populate_pdl_config(&_CYHAL_ADC_DEFAULT_CONFIG, &pdl_config, chan_configs);
}
#else
if (CY_RSLT_SUCCESS == result)
{
result = _cyhal_adc_populate_pdl_config(&_CYHAL_ADC_DEFAULT_CONFIG, &pdl_config);
}
#endif
if (result == CY_RSLT_SUCCESS)
{
result = (cy_rslt_t)Cy_SAR_Init(obj->base, &pdl_config);
}
if (result == CY_RSLT_SUCCESS)
{
Cy_SAR_SetVssaSarSeqCtrl(obj->base, _CYHAL_ADC_SARSEQ_STATE(true));
Cy_SAR_SetVssaVminusSwitch(obj->base, _CYHAL_ADC_SWITCH_STATE(true));
_cyhal_analog_init();
_cyhal_adc_config_structs[obj->resource.block_num] = obj;
cy_stc_sysint_t irqCfg = { _cyhal_adc_irq_n[obj->resource.block_num], CYHAL_ISR_PRIORITY_DEFAULT };
Cy_SysInt_Init(&irqCfg, _cyhal_adc_irq_handler);
NVIC_EnableIRQ(_cyhal_adc_irq_n[obj->resource.block_num]);
/* We always need to listen to the EOS interrupt for internal bookkeeping */
Cy_SAR_SetInterruptMask(obj->base, CY_SAR_INTR_EOS);
Cy_SAR_Enable(obj->base);
}
else
{
cyhal_adc_free(obj);
}
return result;
}
void cyhal_adc_free(cyhal_adc_t *obj)
{
if (NULL != obj && NULL != obj->base)
{
IRQn_Type irqn = _cyhal_adc_irq_n[obj->resource.block_num];
NVIC_DisableIRQ(irqn);
_cyhal_adc_config_structs[obj->resource.block_num] = NULL;
cy_rslt_t rslt;
rslt = cyhal_adc_disable_output(obj, CYHAL_ADC_OUTPUT_SCAN_COMPLETE);
CY_ASSERT(CY_RSLT_SUCCESS == rslt);
if (CYHAL_TRIGGER_CPUSS_ZERO != obj->source)
{
rslt = cyhal_adc_disconnect_digital(obj, obj->source, CYHAL_ADC_INPUT_START_SCAN);
CY_ASSERT(CY_RSLT_SUCCESS == rslt);
}
(void)rslt; // Disable compiler warning in release build
Cy_SAR_SetVssaSarSeqCtrl(obj->base, _CYHAL_ADC_SARSEQ_STATE(false));
Cy_SAR_SetVssaVminusSwitch(obj->base, _CYHAL_ADC_SWITCH_STATE(false));
Cy_SAR_Disable(obj->base);
if(obj->dedicated_clock)
{
Cy_SysClk_PeriphDisableDivider((cy_en_divider_types_t)obj->clock.block, obj->clock.channel);
cyhal_clock_free(&obj->clock);
}
_cyhal_analog_free();
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
_cyhal_utils_release_if_used(&(obj->ext_vref));
_cyhal_utils_release_if_used(&(obj->bypass_pin));
#endif
cyhal_hwmgr_free(&obj->resource);
obj->base = NULL;
}
}
cy_rslt_t _cyhal_adc_populate_acquisition_timers(cyhal_adc_t* obj)
{
const uint32_t ACQUISITION_CLOCKS_MIN = 2;
const uint32_t ACQUISITION_CLOCKS_MAX = 1023;
cy_rslt_t result = CY_RSLT_SUCCESS;
uint32_t clock_frequency_hz = cyhal_clock_get_frequency(&(obj->clock));
uint32_t clock_period_ns = (clock_frequency_hz > 0)
? _CYHAL_UTILS_NS_PER_SECOND / clock_frequency_hz
: 0;
uint16_t sample_timer_ns[] = { 0u, 0u, 0u, 0u };
uint8_t assigned_timer[CY_SAR_SEQ_NUM_CHANNELS];
for(uint8_t channel = 0; channel < CY_SAR_SEQ_NUM_CHANNELS; ++channel)
{
cyhal_adc_channel_t* chan_config = obj->channel_config[channel];
assigned_timer[channel] = 0u;
/* If the channel isn't in use, what we select doesn't matter */
if(NULL != chan_config)
{
bool found = false;
for(uint8_t timer = 0; timer < sizeof(sample_timer_ns) / sizeof(sample_timer_ns[0]); ++timer)
{
if(chan_config->minimum_acquisition_ns == sample_timer_ns[timer])
{
/* Matched a pre-existing timer; use that */
assigned_timer[channel] = timer;
found = true;
break;
}
else if(0 == sample_timer_ns[timer])
{
/* Found a free timer - allocate and use that */
sample_timer_ns[timer] = chan_config->minimum_acquisition_ns;
assigned_timer[channel] = timer;
found = true;
break;
}
}
if(false == found)
{
/* Ran out of acquisition timers */
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
}
}
if(CY_RSLT_SUCCESS == result)
{
uint16_t sample_timer_clocks[sizeof(sample_timer_ns) / sizeof(sample_timer_ns[0])];
for(uint8_t i = 0; i < sizeof(sample_timer_clocks) / sizeof(sample_timer_clocks[0]); ++i)
{
/* Convert from nanoseconds to clock cycles, rounding up */
uint32_t clock_cycles = (sample_timer_ns[i] + (clock_period_ns - 1)) / clock_period_ns;
if(clock_cycles < ACQUISITION_CLOCKS_MIN)
{
clock_cycles = ACQUISITION_CLOCKS_MIN;
}
else if(clock_cycles > ACQUISITION_CLOCKS_MAX)
{
clock_cycles = ACQUISITION_CLOCKS_MAX;
}
/* Per the register map, this should be one greater than the actual desired sampling cycle count */
sample_timer_clocks[i] = clock_cycles + 1;
}
obj->base->SAMPLE_TIME01 = (sample_timer_clocks[0] << SAR_SAMPLE_TIME01_SAMPLE_TIME0_Pos)
| (sample_timer_clocks[1] << SAR_SAMPLE_TIME01_SAMPLE_TIME1_Pos);
obj->base->SAMPLE_TIME23 = (sample_timer_clocks[2] << SAR_SAMPLE_TIME23_SAMPLE_TIME2_Pos)
| (sample_timer_clocks[3] << SAR_SAMPLE_TIME23_SAMPLE_TIME3_Pos);
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
obj->base->CHAN_CONFIG[i] &= ~SAR_CHAN_CONFIG_SAMPLE_TIME_SEL_Msk;
obj->base->CHAN_CONFIG[i] |= assigned_timer[i] << SAR_CHAN_CONFIG_SAMPLE_TIME_SEL_Pos;
}
}
return result;
}
static const cyhal_resource_pin_mapping_t* _cyhal_adc_find_matching_resource(cyhal_resource_inst_t* adc, cyhal_gpio_t pin, const cyhal_resource_pin_mapping_t *pin_map, size_t count)
{
for (uint32_t i = 0; i < count; i++)
{
if (pin == pin_map[i].pin && _cyhal_utils_resources_equal(adc, pin_map[i].inst))
{
return &pin_map[i];
}
}
return NULL;
}
cy_rslt_t cyhal_adc_configure(cyhal_adc_t *obj, const cyhal_adc_config_t *config)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
#if defined(CYHAL_PIN_MAP_PASS_SAR_EXT_VREF0)
if(NC != config->ext_vref)
{
// If this pin wasn't used in the previous config for either vref or bypass, reserve it
if(NC == obj->ext_vref && config->ext_vref != obj->bypass_pin)
{
const cyhal_resource_pin_mapping_t* ext_vref_map =
_cyhal_adc_find_matching_resource(&(obj->resource), config->ext_vref, cyhal_pin_map_pass_sar_ext_vref0,
sizeof(cyhal_pin_map_pass_sar_ext_vref0)/sizeof(cyhal_pin_map_pass_sar_ext_vref0[0]));
if (NULL == ext_vref_map)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
else
{
result = _cyhal_utils_reserve_and_connect(config->ext_vref, ext_vref_map);
}
if(CY_RSLT_SUCCESS == result)
{
obj->ext_vref = config->ext_vref;
}
}
}
else
{
if(NC != obj->ext_vref) // We used to have an external vref pin - free it
{
// If the same pin was used as bypass, mark it freed now too
if(obj->ext_vref == obj->bypass_pin)
{
obj->bypass_pin = NC;
}
// It is okay to do this without checking if the pin is still used for bypass,
// because in that case we will just re-reserve the pin below
cyhal_gpio_free(obj->ext_vref);
obj->ext_vref = NC;
}
// If external vref exists as a GPIO, it's an error to set vref to external without passing in the pin
if(CYHAL_ADC_REF_EXTERNAL == config->vref)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
}
if(NC != config->bypass_pin)
{
if(CY_RSLT_SUCCESS == result)
{
// Bypass and ext_vref share the same hard-wired IO connection
const cyhal_resource_pin_mapping_t* bypass_map =
_cyhal_adc_find_matching_resource(&(obj->resource), config->bypass_pin, cyhal_pin_map_pass_sar_ext_vref0,
sizeof(cyhal_pin_map_pass_sar_ext_vref0)/sizeof(cyhal_pin_map_pass_sar_ext_vref0[0]));
if (NULL == bypass_map)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
else if(config->bypass_pin != config->ext_vref) // It's valid to use the same pin for both ext_vref and bypass
{
result = _cyhal_utils_reserve_and_connect(config->bypass_pin, bypass_map);
}
}
if(CY_RSLT_SUCCESS == result)
{
obj->bypass_pin = config->bypass_pin;
}
}
else
{
// We used to have an external vref pin - free it, unless it's still used for ext_vref
if(NC != obj->bypass_pin && obj->ext_vref != obj->bypass_pin)
{
cyhal_gpio_free(obj->bypass_pin);
obj->bypass_pin = NC;
}
// If bypass exists as a GPIO, it's an error to enable bypass without passing in the pin
if(config->is_bypassed)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
}
#else
/* No GPIO pins for VREF - it must be using a dedicated pad */
if(config->bypass_pin != NC || config->ext_vref != NC)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
#endif
if(false == ((CYHAL_ADC_REF_EXTERNAL == config->vref) ^ (0u == config->ext_vref_mv)))
{
/* Must have exactly one of: ext vref selected, ext vref voltage unspecified */
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
cy_stc_sar_config_t pdl_config;
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
if(CY_RSLT_SUCCESS == result)
{
obj->resolution = config->resolution;
cy_stc_sar_channel_config_t chan_configs[CY_SAR_SEQ_NUM_CHANNELS];
_cyhal_adc_extract_channel_conf(obj, chan_configs);
result = _cyhal_adc_populate_pdl_config(config, &pdl_config, chan_configs);
}
#else
if(CY_RSLT_SUCCESS == result)
{
result = _cyhal_adc_populate_pdl_config(config, &pdl_config);
}
#endif
if(CY_RSLT_SUCCESS == result)
{
/* Save and restore channel configs */
pdl_config.chanEn = obj->base->CHAN_EN;
/* Don't deinit routing or change the channel config - we're going
* to turn the SAR back on in a minute */
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
pdl_config.routingConfig = NULL;
#else
pdl_config.configRouting = false;
/* On M0S8 we already extracted the channel configuration as part
* of assembling the PDL config struct. On MxS40 we need to do it here */
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
pdl_config.chanConfig[i] = obj->base->CHAN_CONFIG[i];
}
#endif
result = (cy_rslt_t)Cy_SAR_Init(obj->base, &pdl_config);
_cyhal_adc_populate_acquisition_timers(obj);
Cy_SAR_SetInterruptMask(obj->base, CY_SAR_INTR_EOS);
Cy_SAR_Enable(obj->base);
}
if(obj->continuous_scanning)
{
obj->conversion_complete = false;
Cy_SAR_StartConvert(obj->base, CY_SAR_START_CONVERT_CONTINUOUS);
}
else
{
Cy_SAR_StopConvert(obj->base);
}
return result;
}
cy_rslt_t cyhal_adc_set_power(cyhal_adc_t *obj, cyhal_power_level_t power)
{
// The SAR doesn't have selectable power levels in the same way that the opamps do.
if(CYHAL_POWER_LEVEL_OFF == power)
{
Cy_SAR_Disable(obj->base);
}
else
{
Cy_SAR_Enable(obj->base);
}
return CY_RSLT_SUCCESS;
}
static uint16_t _cyhal_adc_get_average_count(cyhal_adc_t* obj, int channel_idx)
{
uint32_t average_count = 1;
/* If averaging is in interleaved mode, it does not impact the sample time */
#if defined(CY_IP_MXS40PASS_SAR_INSTANCES)
bool is_interleaved = CY_SAR_AVG_MODE_INTERLEAVED == (SAR_SAMPLE_CTRL(obj->base) & SAR_SAMPLE_CTRL_AVG_MODE_Msk);
#else
bool is_interleaved = false;
#endif
if(false == is_interleaved)
{
average_count = (SAR_SAMPLE_CTRL(obj->base) & SAR_SAMPLE_CTRL_AVG_CNT_Msk) >> SAR_SAMPLE_CTRL_AVG_CNT_Pos;
average_count = (1uL << (average_count + 1uL));
}
return (obj->base->CHAN_CONFIG[channel_idx] & SAR_CHAN_CONFIG_AVG_EN_Msk) ? average_count : 1;
}
/* Gets acquisition times and conversion clocks for all enabled channels, factoring in averaging */
static void _cyhal_adc_get_sample_times(cyhal_adc_t* obj, uint32_t* min_acquisition_ns, uint32_t* conversion_clock_cycles)
{
*min_acquisition_ns = *conversion_clock_cycles = 0;
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
cyhal_adc_channel_t* chan_config = obj->channel_config[i];
if(NULL != chan_config && (0u != (obj->base->CHAN_EN & 1 << i)))
{
uint32_t min_time = chan_config->minimum_acquisition_ns;
uint8_t clock_cycles = _CYHAL_ADC_CONVERSION_CYCLES;
uint16_t average_count = _cyhal_adc_get_average_count(obj, i);
min_time *= average_count;
clock_cycles *= average_count;
*min_acquisition_ns += min_time;
*conversion_clock_cycles += clock_cycles;
}
}
}
uint32_t _cyhal_adc_calc_optimal_clock_rate(cyhal_adc_t* obj, uint32_t target_sample_hz)
{
/* From the architecture TRM */
const uint32_t ADC_CLOCK_MAX_HZ = 60000000;
const uint32_t ADC_CLOCK_MIN_HZ = 1000000;
uint32_t sample_period_ns = _CYHAL_UTILS_NS_PER_SECOND / target_sample_hz;
uint32_t total_acquisition_ns, conversion_clock_cycles;
_cyhal_adc_get_sample_times(obj, &total_acquisition_ns, &conversion_clock_cycles);
uint32_t conversion_budget_ns;
if(sample_period_ns < total_acquisition_ns)
{
// Requested sampling rate is impossible - go as fast as we can.
conversion_budget_ns = 1;
}
else
{
conversion_budget_ns = sample_period_ns - total_acquisition_ns;
}
uint32_t target_period_ns = conversion_budget_ns / conversion_clock_cycles;
uint32_t target_clock_hz = _CYHAL_UTILS_NS_PER_SECOND / target_period_ns;
if(target_clock_hz > ADC_CLOCK_MAX_HZ)
{
target_clock_hz = ADC_CLOCK_MAX_HZ;
}
else if(target_clock_hz < ADC_CLOCK_MIN_HZ)
{
target_clock_hz = ADC_CLOCK_MIN_HZ;
}
return target_clock_hz;
}
uint32_t _cyhal_adc_compute_actual_sample_rate(cyhal_adc_t* obj)
{
/* Assumes that the acquisition timers and clock frequency are already set */
uint32_t clock_frequency_hz = cyhal_clock_get_frequency(&obj->clock);
uint32_t clock_period_ns = (clock_frequency_hz > 0)
? _CYHAL_UTILS_NS_PER_SECOND / clock_frequency_hz
: 0;
uint16_t sample_timer[] =
{
(obj->base->SAMPLE_TIME01 & SAR_SAMPLE_TIME01_SAMPLE_TIME0_Msk) >> SAR_SAMPLE_TIME01_SAMPLE_TIME0_Pos,
(obj->base->SAMPLE_TIME01 & SAR_SAMPLE_TIME01_SAMPLE_TIME1_Msk) >> SAR_SAMPLE_TIME01_SAMPLE_TIME1_Pos,
(obj->base->SAMPLE_TIME23 & SAR_SAMPLE_TIME23_SAMPLE_TIME2_Msk) >> SAR_SAMPLE_TIME23_SAMPLE_TIME2_Pos,
(obj->base->SAMPLE_TIME23 & SAR_SAMPLE_TIME23_SAMPLE_TIME3_Msk) >> SAR_SAMPLE_TIME23_SAMPLE_TIME3_Pos,
};
uint32_t total_sample_time_ns = 0;
for(uint8_t i = 0; i < CY_SAR_SEQ_NUM_CHANNELS; ++i)
{
if(0u == (obj->base->CHAN_EN & 1u << i))
{
continue;
}
uint8_t sample_time_idx =
(obj->base->CHAN_CONFIG[i] & SAR_CHAN_CONFIG_SAMPLE_TIME_SEL_Msk) >> SAR_CHAN_CONFIG_SAMPLE_TIME_SEL_Pos;
/* Per the register map, the register value is one more than the actual cycle number. */
uint32_t sample_cycles = sample_timer[sample_time_idx] - 1;
uint32_t total_cycles = sample_cycles + _CYHAL_ADC_CONVERSION_CYCLES;
uint32_t sample_time_ns = total_cycles * clock_period_ns;
sample_time_ns *= _cyhal_adc_get_average_count(obj, i);
total_sample_time_ns += sample_time_ns;
}
uint32_t sample_frequency_hz = (total_sample_time_ns > 0)
? _CYHAL_UTILS_NS_PER_SECOND / total_sample_time_ns
: 0;
return sample_frequency_hz;
}
cy_rslt_t cyhal_adc_set_sample_rate(cyhal_adc_t* obj, uint32_t desired_sample_rate_hz, uint32_t* achieved_sample_rate_hz)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
/* If we don't own the clock, the caller needs to adjust it and/or the acquisition times to achive the desired rate */
if(obj->dedicated_clock)
{
uint32_t desired_hz = _cyhal_adc_calc_optimal_clock_rate(obj, desired_sample_rate_hz);
result = cyhal_clock_set_frequency(&(obj->clock), desired_hz, NULL);
}
if(CY_RSLT_SUCCESS == result)
{
result = _cyhal_adc_populate_acquisition_timers(obj);
}
if(CY_RSLT_SUCCESS == result)
{
*achieved_sample_rate_hz = _cyhal_adc_compute_actual_sample_rate(obj);
}
else
{
*achieved_sample_rate_hz = 0u;
}
return result;
}
/*******************************************************************************
* ADC Channel HAL Functions
*******************************************************************************/
uint32_t _cyhal_adc_channel_convert_config(const cyhal_adc_channel_config_t* config, const cyhal_adc_t* adc,
cyhal_gpio_t vplus, cyhal_gpio_t vminus)
{
uint32_t result =
_BOOL2FLD(SAR_CHAN_CONFIG_AVG_EN, config->enable_averaging)
| _VAL2FLD(SAR_CHAN_CONFIG_SAMPLE_TIME_SEL, 0u); /* Placeholder, will be updated by populate_acquisition_timers */
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
CY_UNUSED_PARAMETER(vminus); // On M0S8, vminus is implied by vplus
result |= _cyhal_adc_get_pin_addr(vplus);
bool resolution_override = (adc->resolution != _CYHAL_ADC_RESOLUTION);
result |= _VAL2FLD(SAR_CHAN_CONFIG_RESOLUTION, (resolution_override ? CY_SAR_SUB_RES : CY_SAR_MAX_RES));
result |= _BOOL2FLD(SAR_CHAN_CONFIG_DIFFERENTIAL_EN, NC != vminus);
#else
CY_UNUSED_PARAMETER(adc); // MXS40 doesn't support resolution override, so ADC is not used
result |= _cyhal_adc_get_pin_addr(vplus, true);
if(vminus == NC)
{
/* Single-ended channel */
result |= (uint32_t)CY_SAR_CHAN_SINGLE_ENDED;
}
else
{
/* Differential */
result |= _cyhal_adc_get_pin_addr(vminus, false);
result |= (uint32_t)CY_SAR_CHAN_DIFFERENTIAL_UNPAIRED;
}
#endif
return result;
}
static void _cyhal_adc_update_chan_offset(cyhal_adc_channel_t* obj)
{
/* Normally, the PDL sets the offset in the ADC init. But we change the channel config after we initialize
* the ADC itself, so we need to set the offset appropriately here. Otherwise the _uv functions will not
* work correctly.
* The conditions in the PDL are: singleEnded && vrefNegSelect && singleEndedSigned. We always operate
* in signed mode so we only need to check the first two.
*/
bool single_ended = (CYHAL_ADC_VNEG == obj->vminus);
uint32_t neg_sel_vref_val = (uint32_t)CY_SAR_NEG_SEL_VREF;
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
// The NEG_SEL values in the M0S8 pdl are not pre-shifted
neg_sel_vref_val = neg_sel_vref_val << SAR_CTRL_NEG_SEL_Pos;
#endif
bool neg_sel_vref = (neg_sel_vref_val == (obj->adc->base->CTRL & SAR_CTRL_NEG_SEL_Msk));
int16_t offset = (single_ended && neg_sel_vref) ? (-1 * ((int16_t) (CY_SAR_WRK_MAX_12BIT / 2))) : 0;
#if defined(CY_IP_MXS40PASS_SAR_INSTANCES) && CY_IP_MXS40PASS_SAR_INSTANCES < 2
Cy_SAR_SetOffset(obj->channel_idx, offset);
#else
Cy_SAR_SetChannelOffset(obj->adc->base, obj->channel_idx, offset);
#endif
}
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)
{
CY_ASSERT(obj != NULL);
CY_ASSERT(adc != NULL);
const uint32_t CYHAL_ADC_MIN_ACQUISITION_TIME_NS = 167;
cy_rslt_t result = CY_RSLT_SUCCESS;
memset(obj, 0, sizeof(cyhal_adc_channel_t));
obj->vplus = NC;
obj->vminus = NC;
// Check for invalid pin or pin belonging to a different SAR
const cyhal_resource_pin_mapping_t *vplus_map = _cyhal_adc_find_matching_resource(&(adc->resource), vplus,
cyhal_pin_map_pass_sarmux_pads, sizeof(cyhal_pin_map_pass_sarmux_pads)/sizeof(cyhal_pin_map_pass_sarmux_pads[0]));
const cyhal_resource_pin_mapping_t *vminus_map = NULL;
if(NULL == vplus_map)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
if(CY_RSLT_SUCCESS == result && CYHAL_ADC_VNEG != vminus)
{
vminus_map = _cyhal_adc_find_matching_resource(&(adc->resource), vminus, cyhal_pin_map_pass_sarmux_pads,
sizeof(cyhal_pin_map_pass_sarmux_pads)/sizeof(cyhal_pin_map_pass_sarmux_pads[0]));
if (NULL == vminus_map)
{
result = CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
}
if(CY_RSLT_SUCCESS == result)
{
result = _cyhal_utils_reserve_and_connect(vplus, vplus_map);
}
if(CY_RSLT_SUCCESS == result)
{
obj->vplus = vplus;
if(CYHAL_ADC_VNEG != vminus)
{
result = _cyhal_utils_reserve_and_connect(vminus, vminus_map);
}
}
uint8_t chosen_channel = 0;
if (CY_RSLT_SUCCESS == result)
{
obj->vminus = vminus;
// Find the first available channel
for(chosen_channel = 0; chosen_channel < CY_SAR_SEQ_NUM_CHANNELS; ++chosen_channel)
{
if(NULL == adc->channel_config[chosen_channel])
{
break;
}
}
if (chosen_channel >= CY_SAR_SEQ_NUM_CHANNELS) // No channels available
result = CYHAL_ADC_RSLT_NO_CHANNELS;
}
if(CY_RSLT_SUCCESS == result)
{
// Don't set the ADC until here so that free knows whether we have allocated
// the channel on the parent ADC instance (and therefore doesn't try to free it if
// something fails further up)
obj->adc = adc;
obj->channel_idx = chosen_channel;
obj->adc->channel_config[chosen_channel] = obj;
obj->minimum_acquisition_ns = (cfg->min_acquisition_ns > CYHAL_ADC_MIN_ACQUISITION_TIME_NS)
? cfg->min_acquisition_ns : CYHAL_ADC_MIN_ACQUISITION_TIME_NS;
}
if(CY_RSLT_SUCCESS == result)
{
result = _cyhal_adc_populate_acquisition_timers(obj->adc);
}
if(CY_RSLT_SUCCESS == result)
{
uint32_t fw_ctrl_plus = _cyhal_adc_get_fw_switch_control(vplus, true);
uint32_t mux_ctrl_plus = _cyhal_adc_get_mux_switch_control(vplus);
_CYHAL_ADC_SET_SWITCH(obj->adc->base, fw_ctrl_plus, true);
Cy_SAR_SetSwitchSarSeqCtrl(obj->adc->base, mux_ctrl_plus, _CYHAL_ADC_SARSEQ_STATE(true));
if(CYHAL_ADC_VNEG != vminus)
{
uint32_t fw_ctrl_minus = _cyhal_adc_get_fw_switch_control(vminus, false);
uint32_t mux_ctrl_minus = _cyhal_adc_get_mux_switch_control(vminus);
_CYHAL_ADC_SET_SWITCH(obj->adc->base, fw_ctrl_minus, true);
Cy_SAR_SetSwitchSarSeqCtrl(obj->adc->base, mux_ctrl_minus, _CYHAL_ADC_SARSEQ_STATE(true));
}
result = cyhal_adc_channel_configure(obj, cfg);
}
if(CY_RSLT_SUCCESS != result)
{
cyhal_adc_channel_free(obj);
}
return result;
}
cy_rslt_t cyhal_adc_channel_configure(cyhal_adc_channel_t *obj, const cyhal_adc_channel_config_t *config)
{
CY_ASSERT(NULL != obj);
obj->adc->base->CHAN_CONFIG[obj->channel_idx] = _cyhal_adc_channel_convert_config(config, obj->adc, obj->vplus, obj->vminus);
if(config->enabled)
{
obj->adc->base->CHAN_EN |= 1u << obj->channel_idx;
}
else
{
obj->adc->base->CHAN_EN &= ~(1u << obj->channel_idx);
}
_cyhal_adc_update_chan_offset(obj);
return _cyhal_adc_populate_acquisition_timers(obj->adc);
}
void cyhal_adc_channel_free(cyhal_adc_channel_t *obj)
{
if(obj->adc != NULL)
{
// Disable the channel, the unconfigure it
obj->adc->channel_config[obj->channel_idx] = NULL;
if(NC != obj->vplus)
{
uint32_t fw_ctrl_plus = _cyhal_adc_get_fw_switch_control(obj->vplus, true);
uint32_t mux_ctrl_plus = _cyhal_adc_get_mux_switch_control(obj->vplus);
_CYHAL_ADC_SET_SWITCH(obj->adc->base, fw_ctrl_plus, false);
Cy_SAR_SetSwitchSarSeqCtrl(obj->adc->base, mux_ctrl_plus, _CYHAL_ADC_SARSEQ_STATE(false));
}
if(NC != obj->vminus)
{
uint32_t mux_ctrl_minus = _cyhal_adc_get_mux_switch_control(obj->vminus);
uint32_t fw_ctrl_minus = _cyhal_adc_get_fw_switch_control(obj->vminus, false);
_CYHAL_ADC_SET_SWITCH(obj->adc->base, fw_ctrl_minus, false);
Cy_SAR_SetSwitchSarSeqCtrl(obj->adc->base, mux_ctrl_minus, _CYHAL_ADC_SARSEQ_STATE(false));
}
obj->adc->base->CHAN_CONFIG[obj->channel_idx] = 0;
obj->adc = NULL;
}
_cyhal_utils_release_if_used(&(obj->vplus));
_cyhal_utils_release_if_used(&(obj->vminus));
}
uint16_t cyhal_adc_read_u16(const cyhal_adc_channel_t *obj)
{
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
const uint8_t RESULT_SCALING_FACTOR = UINT16_MAX / ((1 << obj->adc->resolution) - 1); // 12-bit SAR resolution
#else
const uint8_t RESULT_SCALING_FACTOR = UINT16_MAX / 0xFFF; // constant 12-bit SAR resolution
#endif
int32_t signed_result = cyhal_adc_read(obj);
/* Legacy API for BWC. Convert from signed to unsigned by adding 0x800 to
* convert the lowest signed 12-bit number to 0x0.
*/
uint16_t unsigned_result = (uint16_t)(signed_result + 0x800);
/* The SAR provides a 12-bit result, but this API is defined to fill a full 16-bit range */
uint16_t scaled_result = unsigned_result * RESULT_SCALING_FACTOR;
return scaled_result;
}
int32_t cyhal_adc_read(const cyhal_adc_channel_t *obj)
{
uint32_t old_en_mask = 0u;
#if defined(CY_IP_MXS40PASS_SAR_INSTANCES)
bool isInterleaved = (CY_SAR_AVG_MODE_INTERLEAVED == (SAR_SAMPLE_CTRL(obj->adc->base) & SAR_SAMPLE_CTRL_AVG_MODE_Msk));
#else
bool isInterleaved = false;
#endif
bool isChannelAveraging = (obj->adc->base->CHAN_CONFIG[obj->channel_idx] & SAR_CHAN_CONFIG_AVG_EN_Msk);
if(!obj->adc->continuous_scanning)
{
/* Enable the selected channel only, then perform an on-demand conversion.
* Save the old enabled channel set to restore after we're done */
old_en_mask = SAR_CHAN_EN(obj->adc->base);
Cy_SAR_SetChanMask(obj->adc->base, 1U << obj->channel_idx);
obj->adc->conversion_complete = false;
// If interleaved averaging and average is enabled for this channel, set for
// continuous scanning and then stop the scan once we get a result. This is
// because the ADC hardware has a special case where it will not raise
// the EOC interrupt until AVG_COUNT scans have occurred when all enabled
// channels are using interleaved channels. This means that for the first AVG_COUNT - 1
// scans there will be no interrupt, therefore conversion_complete will never
// be set true, and therefore the loop below would be stuck waiting forever,
// never able to trigger a subsequent scan.
Cy_SAR_StartConvert(obj->adc->base, (isInterleaved && isChannelAveraging) ? CY_SAR_START_CONVERT_CONTINUOUS : CY_SAR_START_CONVERT_SINGLE_SHOT);
}
/* Cy_SAR_IsEndConversion relies on and clears the EOS interrupt status bit.
* We don't know how this read will be used in combination with interrupts,
* so implement our own interrupt-driven EOS flag
*/
while(!obj->adc->conversion_complete) { }
int32_t result = Cy_SAR_GetResult32(obj->adc->base, obj->channel_idx);
if(!obj->adc->continuous_scanning)
{
if(isInterleaved && isChannelAveraging)
{
Cy_SAR_StopConvert(obj->adc->base);
}
Cy_SAR_SetChanMask(obj->adc->base, old_en_mask);
}
return result;
}
int32_t cyhal_adc_read_uv(const cyhal_adc_channel_t *obj)
{
CY_ASSERT(NULL != obj);
int32_t counts = cyhal_adc_read(obj);
return Cy_SAR_CountsTo_uVolts(obj->adc->base, obj->channel_idx, counts);
}
void _cyhal_adc_start_async_read(cyhal_adc_t* obj, size_t num_scan, int32_t* result_list)
{
CY_ASSERT(NULL == obj->async_buff_next); /* Transfer already in progress */
uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
obj->async_scans_remaining = num_scan;
obj->async_buff_next = obj->async_buff_orig = result_list;
if(false == obj->continuous_scanning)
{
Cy_SAR_StartConvert(obj->base, CY_SAR_START_CONVERT_SINGLE_SHOT);
}
cyhal_system_critical_section_exit(savedIntrStatus);
}
cy_rslt_t cyhal_adc_read_async(cyhal_adc_t* obj, size_t num_scan, int32_t* result_list)
{
CY_ASSERT(NULL != obj);
obj->async_transfer_in_uv = false;
_cyhal_adc_start_async_read(obj, num_scan, result_list);
return CY_RSLT_SUCCESS;
}
cy_rslt_t cyhal_adc_read_async_uv(cyhal_adc_t* obj, size_t num_scan, int32_t* result_list)
{
CY_ASSERT(NULL != obj);
obj->async_transfer_in_uv = true;
_cyhal_adc_start_async_read(obj, num_scan, result_list);
return CY_RSLT_SUCCESS;
}
cy_rslt_t cyhal_adc_set_async_mode(cyhal_adc_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)
{
CY_ASSERT(NULL != obj);
CY_ASSERT(NULL == obj->async_buff_next); /* Can't swap mode while a transfer is running */
cy_rslt_t result = CY_RSLT_SUCCESS;
if(mode == CYHAL_ASYNC_DMA)
{
#if defined(CY_IP_M0S8CPUSSV3_DMAC) || defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M4CPUSS_DMAC)
result = cyhal_dma_init(&(obj->dma), CYHAL_DMA_PRIORITY_DEFAULT, CYHAL_DMA_DIRECTION_PERIPH2MEM);
if(CY_RSLT_SUCCESS == result)
{
cyhal_dma_register_callback(&(obj->dma), &_cyhal_adc_dma_handler, obj);
cyhal_dma_enable_event(&(obj->dma), CYHAL_DMA_TRANSFER_COMPLETE, dma_priority, true);
}
#else
result = CYHAL_ADC_RSLT_BAD_ARGUMENT; // DMA not supported
#endif
}
else
{
#if defined(CY_IP_M0S8CPUSSV3_DMAC) || defined(CY_IP_M4CPUSS_DMA) || defined(CY_IP_M4CPUSS_DMAC)
/* Free the DMA instances if we reserved them but don't need them anymore */
if(CYHAL_RSC_INVALID != obj->dma.resource.type)
{
cyhal_dma_free(&obj->dma);
obj->dma.resource.type = CYHAL_RSC_INVALID;
}
#endif
}
if(CY_RSLT_SUCCESS == result)
{
obj->async_mode = mode;
}
return CY_RSLT_SUCCESS;
}
void cyhal_adc_register_callback(cyhal_adc_t *obj, cyhal_adc_event_callback_t callback, void *callback_arg)
{
CY_ASSERT(NULL != obj);
uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
obj->callback_data.callback = (cy_israddress) callback;
obj->callback_data.callback_arg = callback_arg;
cyhal_system_critical_section_exit(savedIntrStatus);
}
void cyhal_adc_enable_event(cyhal_adc_t *obj, cyhal_adc_event_t event, uint8_t intr_priority, bool enable)
{
/* We always listen to EOS internally, so no need to update a hardware interrupt mask */
if(enable)
{
obj->user_enabled_events |= event;
}
else
{
obj->user_enabled_events &= ~event;
}
IRQn_Type irqn = _cyhal_adc_irq_n[obj->resource.block_num];
NVIC_SetPriority(irqn, intr_priority);
}
static cyhal_dest_t _cyhal_adc_calculate_dest(uint8_t block_num)
{
CY_ASSERT(block_num < _CYHAL_ADC_SAR_INSTANCES);
return _cyhal_adc_tr_in[block_num];
}
static cyhal_source_t _cyhal_adc_calculate_source(uint8_t block_num)
{
CY_ASSERT(block_num < _CYHAL_ADC_SAR_INSTANCES);
return _cyhal_adc_tr_out[block_num];
}
cy_rslt_t cyhal_adc_connect_digital(cyhal_adc_t *obj, cyhal_source_t source, cyhal_adc_input_t input)
{
if(input == CYHAL_ADC_INPUT_START_SCAN)
{
Cy_SAR_SetConvertMode(obj->base, CY_SAR_TRIGGER_MODE_FW_AND_HWEDGE);
cyhal_dest_t dest = _cyhal_adc_calculate_dest(obj->resource.block_num);
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
// On M0S8 the trigger type is not configurable so the type argument to connect_signal is ignored
// Therefore, we arbitrarily pick EDGE to satisfy the interface
return _cyhal_connect_signal(source, dest, CYHAL_SIGNAL_TYPE_EDGE);
#else
return _cyhal_connect_signal(source, dest, (cyhal_signal_type_t)TRIGGER_TYPE_PASS_TR_SAR_OUT);
#endif
}
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
cy_rslt_t cyhal_adc_enable_output(cyhal_adc_t *obj, cyhal_adc_output_t output, cyhal_source_t *source)
{
if(output == CYHAL_ADC_OUTPUT_SCAN_COMPLETE)
{
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
SAR_SAMPLE_CTRL(obj->base) |= SAR_SAMPLE_CTRL_EOS_DSI_OUT_EN_Msk;
#else
SAR_SAMPLE_CTRL(obj->base) |= SAR_SAMPLE_CTRL_TRIGGER_OUT_EN_Msk;
#endif
*source = _cyhal_adc_calculate_source(obj->resource.block_num);
return CY_RSLT_SUCCESS;
}
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
cy_rslt_t cyhal_adc_disconnect_digital(cyhal_adc_t *obj, cyhal_source_t source, cyhal_adc_input_t input)
{
if(input == CYHAL_ADC_INPUT_START_SCAN)
{
Cy_SAR_SetConvertMode(obj->base, CY_SAR_TRIGGER_MODE_FW_ONLY);
cyhal_dest_t dest = _cyhal_adc_calculate_dest(obj->resource.block_num);
return _cyhal_disconnect_signal(source, dest);
}
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
cy_rslt_t cyhal_adc_disable_output(cyhal_adc_t *obj, cyhal_adc_output_t output)
{
if(output != CYHAL_ADC_OUTPUT_SCAN_COMPLETE)
{
return CYHAL_ADC_RSLT_BAD_ARGUMENT;
}
#if defined(CY_IP_M0S8PASS4A_INSTANCES)
SAR_SAMPLE_CTRL(obj->base) &= ~SAR_SAMPLE_CTRL_EOS_DSI_OUT_EN_Msk;
#else
SAR_SAMPLE_CTRL(obj->base) &= ~SAR_SAMPLE_CTRL_TRIGGER_OUT_EN_Msk;
#endif
return CY_RSLT_SUCCESS;
}
#if defined(__cplusplus)
}
#endif
#endif /* defined(CY_IP_MXS40PASS_SAR_INSTANCES) */