/***************************************************************************//**
* @file
* @brief Analog Comparator (ACMP) Peripheral API
*******************************************************************************
* # License
* Copyright 2018 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "em_acmp.h"
#if defined(ACMP_COUNT) && (ACMP_COUNT > 0)
#include
#include "em_bus.h"
#include "sl_assert.h"
#include "em_gpio.h"
/***************************************************************************//**
* @addtogroup acmp ACMP - Analog Comparator
* @brief Analog comparator (ACMP) Peripheral API
*
* @details
* The Analog Comparator is used to compare voltage of two analog inputs
* with a digital output indicating which input voltage is higher. Inputs can
* either be one of the selectable internal references or from external pins.
* Response time and current consumption can be configured by
* altering the current supply to the comparator.
*
* ACMP is available down to EM3 and is able to wake up the system when
* input signals pass a certain threshold. Use @ref ACMP_IntEnable() to enable
* an edge interrupt to use this functionality.
*
* This example shows how to use the em_acmp.h API for comparing an input
* pin to an internal 2.5 V reference voltage.
*
* @if DOXYDOC_P1_DEVICE
* @include em_acmp_compare_s0.c
* @endif
*
* @if DOXYDOC_P2_DEVICE
* @include em_acmp_compare_s1.c
* @endif
*
* @if DOXYDOC_S2_DEVICE
* @include em_acmp_compare_s2.c
* @endif
*
* @note
* ACMP can also be used to compare two separate input pins.
*
* @details
* ACMP also contains specialized hardware for capacitive sensing. This
* module contains the @ref ACMP_CapsenseInit() function to initialize
* ACMP for capacitive sensing and the @ref ACMP_CapsenseChannelSet() function
* to select the current capsense channel.
*
* For applications that require capacitive sensing it is recommended to use a
* library, such as cslib, which is provided by Silicon Labs.
*
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/** Validation of ACMP register block pointer reference
* for assert statements. */
#if (ACMP_COUNT == 1)
#define ACMP_REF_VALID(ref) ((ref) == ACMP0)
#elif (ACMP_COUNT == 2)
#define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1))
#elif (ACMP_COUNT == 3)
#define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1) || ((ref) == ACMP2))
#elif (ACMP_COUNT == 4)
#define ACMP_REF_VALID(ref) (((ref) == ACMP0) \
|| ((ref) == ACMP1) \
|| ((ref) == ACMP2) \
|| ((ref) == ACMP3))
#else
#error Undefined number of analog comparators (ACMP).
#endif
/** The maximum value that can be inserted in the route location register
* for the specific device. */
#if defined(_ACMP_ROUTE_LOCATION_LOC3)
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC3
#elif defined(_ACMP_ROUTE_LOCATION_LOC2)
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC2
#elif defined(_ACMP_ROUTE_LOCATION_LOC1)
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC1
#elif defined(_ACMP_ROUTELOC0_OUTLOC_LOC31)
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_LOC31
#elif defined(_ACMP_ROUTELOC0_OUTLOC_MASK)
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_MASK
#endif
/** Map ACMP reference to index of device. */
#if (ACMP_COUNT == 1)
#define ACMP_DEVICE_ID(acmp) ( \
(acmp) == ACMP0 ? 0 \
: 0)
#elif (ACMP_COUNT == 2)
#define ACMP_DEVICE_ID(acmp) ( \
(acmp) == ACMP0 ? 0 \
: (acmp) == ACMP1 ? 1 \
: 0)
#endif
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Set up ACMP for use in capacitive sense applications.
*
* @details
* This function sets up ACMP for use in capacitive sense applications.
* To use the capacitive sense functionality in the ACMP, use
* the PRS output of the ACMP module to count the number of oscillations
* in the capacitive sense circuit (possibly using a TIMER).
*
* @note
* A basic example of capacitive sensing can be found in the STK BSP
* (capsense demo).
*
* @cond DOXYDOC_S2_DEVICE
* @note
* A call to ACMP_CapsenseInit will enable and disable the ACMP peripheral,
* which can cause side effects if it was previously set up.
* @endcond
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param[in] init
* A pointer to the initialization structure used to configure ACMP for capacitive
* sensing operation.
******************************************************************************/
void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init)
{
EFM_ASSERT(ACMP_REF_VALID(acmp));
#if defined(_SILICON_LABS_32B_SERIES_2)
EFM_ASSERT(init->vrefDiv < 64);
EFM_ASSERT(init->biasProg
<= (_ACMP_CFG_BIAS_MASK >> _ACMP_CFG_BIAS_SHIFT));
ACMP_Disable(acmp);
acmp->CFG = (init->biasProg << _ACMP_CFG_BIAS_SHIFT)
| (init->hysteresisLevel << _ACMP_CFG_HYST_SHIFT);
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
ACMP_Enable(acmp);
acmp->INPUTCTRL = (init->resistor << _ACMP_INPUTCTRL_CSRESSEL_SHIFT)
| (init->vrefDiv << _ACMP_INPUTCTRL_VREFDIV_SHIFT)
| (ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
if (!init->enable) {
ACMP_Disable(acmp);
}
#elif defined(_SILICON_LABS_32B_SERIES_1)
EFM_ASSERT(init->vddLevelLow < 64);
EFM_ASSERT(init->vddLevelHigh < 64);
EFM_ASSERT(init->biasProg
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
/* Set the control register. No need to set interrupt modes. */
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
| ACMP_CTRL_ACCURACY_HIGH;
acmp->HYSTERESIS0 = (init->vddLevelHigh << _ACMP_HYSTERESIS0_DIVVA_SHIFT)
| (init->hysteresisLevel_0 << _ACMP_HYSTERESIS0_HYST_SHIFT);
acmp->HYSTERESIS1 = (init->vddLevelLow << _ACMP_HYSTERESIS1_DIVVA_SHIFT)
| (init->hysteresisLevel_1 << _ACMP_HYSTERESIS1_HYST_SHIFT);
/* Select capacitive sensing mode by selecting a resistor and enabling it. */
acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
| ACMP_INPUTSEL_CSRESEN
| ACMP_INPUTSEL_VASEL_VDD
| ACMP_INPUTSEL_NEGSEL_VADIV;
BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, init->enable);
#elif defined(_SILICON_LABS_32B_SERIES_0)
EFM_ASSERT(init->vddLevel < 64);
EFM_ASSERT(init->biasProg
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
/* Set the control register. No need to set interrupt modes. */
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
| (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
| (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
| (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT);
/* Select capacitive sensing mode by selecting a resistor and enabling it. */
acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
| ACMP_INPUTSEL_CSRESEN
| (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
| (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT)
| ACMP_INPUTSEL_NEGSEL_CAPSENSE;
BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, init->enable);
#endif
}
/***************************************************************************//**
* @brief
* Set the ACMP channel used for capacitive sensing.
*
* @note
* A basic example of capacitive sensing can be found in the STK BSP
* (capsense demo).
*
* @cond DOXYDOC_S2_DEVICE
* @note
* Can only be called when the peripheral is enabled.
* @endcond
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param[in] channel
* The ACMP channel to use for capacitive sensing (Possel).
******************************************************************************/
void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel)
{
/* Make sure the module exists on the selected chip. */
EFM_ASSERT(ACMP_REF_VALID(acmp));
#if defined(_ACMP_INPUTSEL_POSSEL_CH7)
/* Make sure that only external channels are used. */
EFM_ASSERT(channel <= _ACMP_INPUTSEL_POSSEL_CH7);
#elif defined(_ACMP_INPUTCTRL_POSSEL_PD15)
EFM_ASSERT(channel != _ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
EFM_ASSERT(_ACMP_INPUTCTRL_POSSEL_PA0 <= channel);
EFM_ASSERT(channel <= _ACMP_INPUTCTRL_POSSEL_PD15);
#endif
#if defined(_ACMP_INPUTCTRL_MASK)
/* Make sure that the ACMP is enabled before changing INPUTCTRL. */
EFM_ASSERT(acmp->EN & ACMP_EN_EN);
while (acmp->SYNCBUSY != 0U) {
/* Wait for synchronization to finish */
}
/* Set channel as positive channel in ACMP */
BUS_RegMaskedWrite(&acmp->INPUTCTRL, _ACMP_INPUTCTRL_POSSEL_MASK,
channel << _ACMP_INPUTCTRL_POSSEL_SHIFT);
#else
/* Set channel as a positive channel in ACMP. */
BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_POSSEL_MASK,
channel << _ACMP_INPUTSEL_POSSEL_SHIFT);
#endif
}
/***************************************************************************//**
* @brief
* Disable ACMP.
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
******************************************************************************/
void ACMP_Disable(ACMP_TypeDef *acmp)
{
/* Make sure the module exists on the selected chip. */
EFM_ASSERT(ACMP_REF_VALID(acmp));
#if defined(_ACMP_EN_MASK)
while ((acmp->EN != 0U) && (acmp->SYNCBUSY != 0U)) {
/* Wait for synchronization to finish */
}
acmp->EN_CLR = ACMP_EN_EN;
#if defined(_ACMP_EN_DISABLING_MASK)
while (acmp->EN & _ACMP_EN_DISABLING_MASK) {
// Wait for disabling to finish
}
#endif
#else
acmp->CTRL &= ~ACMP_CTRL_EN;
#endif
}
/***************************************************************************//**
* @brief
* Enable ACMP.
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
******************************************************************************/
void ACMP_Enable(ACMP_TypeDef *acmp)
{
/* Make sure the module exists on the selected chip. */
EFM_ASSERT(ACMP_REF_VALID(acmp));
#if defined(_ACMP_EN_MASK)
acmp->EN_SET = ACMP_EN_EN;
#else
acmp->CTRL |= ACMP_CTRL_EN;
#endif
}
#if defined(_ACMP_EXTIFCTRL_MASK)
/***************************************************************************//**
* @brief
* Select and enable external input.
*
* @details
* This is used when an external module needs to take control of the ACMP
* POSSEL field to configure the APORT input for the ACMP. Modules,
* such as LESENSE, use this to change the ACMP input during a scan sequence.
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param[in] aport
* This parameter decides which APORT(s) the ACMP will use when it's
* controlled by an external module.
******************************************************************************/
void ACMP_ExternalInputSelect(ACMP_TypeDef *acmp, ACMP_ExternalInput_Typedef aport)
{
acmp->EXTIFCTRL = (aport << _ACMP_EXTIFCTRL_APORTSEL_SHIFT)
| ACMP_EXTIFCTRL_EN;
while (!(acmp->STATUS & ACMP_STATUS_EXTIFACT)) {
}
}
#endif
/***************************************************************************//**
* @brief
* Reset ACMP to the same state that it was in after a hardware reset.
*
* @note
* The GPIO ACMP ROUTE register is NOT reset by this function to allow for
* centralized setup of this feature.
*
* @note
* The peripheral may be enabled and disabled during reset.
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
******************************************************************************/
void ACMP_Reset(ACMP_TypeDef *acmp)
{
/* Make sure the module exists on the selected chip */
EFM_ASSERT(ACMP_REF_VALID(acmp));
#if defined(_SILICON_LABS_32B_SERIES_2)
#if defined(ACMP_SWRST_SWRST)
acmp->SWRST_SET = ACMP_SWRST_SWRST;
while (acmp->SWRST & _ACMP_SWRST_RESETTING_MASK) {
}
#else
acmp->IEN = _ACMP_IEN_RESETVALUE;
ACMP_Enable(acmp);
acmp->INPUTCTRL = _ACMP_INPUTCTRL_RESETVALUE;
ACMP_Disable(acmp);
acmp->CFG = PM5507_ACMP_CFG_RESETVALUE;
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
acmp->IF_CLR = _ACMP_IF_MASK;
#endif
#else // Series 0 and Series 1 devices
acmp->IEN = _ACMP_IEN_RESETVALUE;
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
acmp->INPUTSEL = _ACMP_INPUTSEL_RESETVALUE;
#if defined(_ACMP_HYSTERESIS0_HYST_MASK)
acmp->HYSTERESIS0 = _ACMP_HYSTERESIS0_RESETVALUE;
acmp->HYSTERESIS1 = _ACMP_HYSTERESIS1_RESETVALUE;
#endif
acmp->IFC = _ACMP_IF_MASK;
#endif
}
#if defined(_GPIO_ACMP_ROUTEEN_MASK)
/***************************************************************************//**
* @brief
* Sets up GPIO output from the ACMP.
*
* @note
* GPIO must be enabled in the CMU before this function call, i.e.
* @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
*
* @param[in] acmp
* Pointer to the ACMP peripheral register block.
*
* @param port
* The GPIO port to use.
*
* @param pin
* The GPIO pin to use.
*
* @param enable
* Enable or disable pin output.
*
* @param invert
* Invert output.
******************************************************************************/
void ACMP_GPIOSetup(ACMP_TypeDef *acmp, GPIO_Port_TypeDef port,
unsigned int pin, bool enable, bool invert)
{
int acmpIndex = ACMP_DEVICE_ID(acmp);
/* Make sure the module exists on the selected chip */
EFM_ASSERT(ACMP_REF_VALID(acmp));
/* Make sure that the port/pin combination is valid. */
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
/* Set GPIO inversion */
acmp->CTRL = (acmp->CTRL & _ACMP_CTRL_NOTRDYVAL_MASK)
| (invert << _ACMP_CTRL_GPIOINV_SHIFT);
GPIO->ACMPROUTE[acmpIndex].ACMPOUTROUTE = (port << _GPIO_ACMP_ACMPOUTROUTE_PORT_SHIFT)
| (pin << _GPIO_ACMP_ACMPOUTROUTE_PIN_SHIFT);
GPIO->ACMPROUTE[acmpIndex].ROUTEEN = enable ? GPIO_ACMP_ROUTEEN_ACMPOUTPEN : 0;
}
#else
/***************************************************************************//**
* @brief
* Set up GPIO output from ACMP.
*
* @note
* GPIO must be enabled in the CMU before this function call, i.e.,
* @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param location
* The pin location to use. See the data sheet for location to pin mappings.
*
* @param enable
* Enable or disable pin output.
*
* @param invert
* Invert output.
******************************************************************************/
void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert)
{
/* Make sure the module exists on the selected chip */
EFM_ASSERT(ACMP_REF_VALID(acmp));
/* Sanity checking of location */
EFM_ASSERT(location <= _ACMP_ROUTE_LOCATION_MAX);
/* Set GPIO inversion */
BUS_RegMaskedWrite(&acmp->CTRL, _ACMP_CTRL_GPIOINV_MASK,
invert << _ACMP_CTRL_GPIOINV_SHIFT);
#if defined(_ACMP_ROUTE_MASK)
acmp->ROUTE = (location << _ACMP_ROUTE_LOCATION_SHIFT)
| (enable << _ACMP_ROUTE_ACMPPEN_SHIFT);
#endif
#if defined(_ACMP_ROUTELOC0_MASK)
acmp->ROUTELOC0 = location << _ACMP_ROUTELOC0_OUTLOC_SHIFT;
acmp->ROUTEPEN = enable ? ACMP_ROUTEPEN_OUTPEN : 0;
#endif
}
#endif /* defined(_GPIO_ACMP_ROUTEEN_MASK) */
/***************************************************************************//**
* @brief
* Set which channels should be used in ACMP comparisons.
*
* @cond DOXYDOC_S2_DEVICE
* @note
* Can only be called when the peripheral is enabled.
*
* @note
* If GPIO is used for both posSel and negSel, they cannot both use even
* or odd pins.
* @endcond
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param negSel
* A channel to use on the negative input to the ACMP.
*
* @param posSel
* A channel to use on the positive input to the ACMP.
******************************************************************************/
void ACMP_ChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef negSel,
ACMP_Channel_TypeDef posSel)
{
/* Make sure the module exists on the selected chip. */
EFM_ASSERT(ACMP_REF_VALID(acmp));
/* Make sure that posSel and negSel channel selectors are valid. */
#if defined(_ACMP_INPUTSEL_NEGSEL_DAC0CH1)
EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_DAC0CH1);
#elif defined(_ACMP_INPUTSEL_NEGSEL_CAPSENSE)
EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_CAPSENSE);
#endif
#if defined(_ACMP_INPUTSEL_POSSEL_CH7)
EFM_ASSERT(posSel <= _ACMP_INPUTSEL_POSSEL_CH7);
#endif
/* Make sure that posSel and negSel channel selectors are valid. */
#if defined(_ACMP_INPUTCTRL_POSSEL_PD15)
EFM_ASSERT(negSel <= _ACMP_INPUTCTRL_POSSEL_PD15);
EFM_ASSERT(posSel <= _ACMP_INPUTCTRL_POSSEL_PD15);
EFM_ASSERT(posSel != _ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
/* Make sure that posSel and negSel channel selectors don't both
* use odd or even pins. */
#if (_SILICON_LABS_32B_SERIES_2_CONFIG > 2)
EFM_ASSERT(!((((posSel >= _ACMP_INPUTCTRL_POSSEL_EXTPA)
&& (posSel <= _ACMP_INPUTCTRL_POSSEL_EXTPD))
|| (posSel >= _ACMP_INPUTCTRL_POSSEL_PA0))
&& (negSel >= _ACMP_INPUTCTRL_NEGSEL_PA0)
&& (posSel % 2 == negSel % 2)));
#else
EFM_ASSERT(!((posSel >= _ACMP_INPUTCTRL_POSSEL_PA0)
&& (negSel >= _ACMP_INPUTCTRL_NEGSEL_PA0)
&& (posSel % 2 == negSel % 2)));
#endif
#endif
#if defined(_ACMP_INPUTCTRL_MASK)
/* Make sure that the ACMP is enabled before changing INPUTCTRL. */
EFM_ASSERT(acmp->EN & ACMP_EN_EN);
while (acmp->SYNCBUSY != 0U) {
/* Wait for synchronization to finish */
}
acmp->INPUTCTRL = (acmp->INPUTCTRL & ~(_ACMP_INPUTCTRL_POSSEL_MASK
| _ACMP_INPUTCTRL_NEGSEL_MASK))
| (negSel << _ACMP_INPUTCTRL_NEGSEL_SHIFT)
| (posSel << _ACMP_INPUTCTRL_POSSEL_SHIFT);
#else
acmp->INPUTSEL = (acmp->INPUTSEL & ~(_ACMP_INPUTSEL_POSSEL_MASK
| _ACMP_INPUTSEL_NEGSEL_MASK))
| (negSel << _ACMP_INPUTSEL_NEGSEL_SHIFT)
| (posSel << _ACMP_INPUTSEL_POSSEL_SHIFT);
#endif
}
/***************************************************************************//**
* @brief
* Initialize ACMP.
*
* @cond DOXYDOC_S2_DEVICE
* @note
* A call to ACMP_Init can cause side effects since it can enable/disable
* the peripheral.
* @endcond
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param[in] init
* A pointer to the initialization structure used to configure ACMP.
******************************************************************************/
void ACMP_Init(ACMP_TypeDef *acmp, const ACMP_Init_TypeDef *init)
{
/* Make sure the module exists on the selected chip. */
EFM_ASSERT(ACMP_REF_VALID(acmp));
#if defined(_SILICON_LABS_32B_SERIES_2)
EFM_ASSERT(init->biasProg
<= (_ACMP_CFG_BIAS_MASK >> _ACMP_CFG_BIAS_SHIFT));
// PM-5507: enforce that biasProg is a functional value
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
EFM_ASSERT(init->biasProg >= 4);
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
// Allow customer to use BIASPROG in [2; 3]
EFM_ASSERT(init->biasProg >= 2);
#else
// Allow customer to use BIASPROG in [0; 3]
// but the implementation of the wait operation would be their responsibility
#endif
/* Make sure the ACMP is disabled since ACMP power source might be changed.*/
ACMP_Disable(acmp);
acmp->CFG = (init->biasProg << _ACMP_CFG_BIAS_SHIFT)
| (init->inputRange << _ACMP_CFG_INPUTRANGE_SHIFT)
| (init->accuracy << _ACMP_CFG_ACCURACY_SHIFT)
| (init->hysteresisLevel << _ACMP_CFG_HYST_SHIFT);
acmp->CTRL = init->inactiveValue << _ACMP_CTRL_NOTRDYVAL_SHIFT;
ACMP_Enable(acmp);
BUS_RegMaskedWrite(&acmp->INPUTCTRL, _ACMP_INPUTCTRL_VREFDIV_MASK,
init->vrefDiv << _ACMP_INPUTCTRL_VREFDIV_SHIFT);
#elif defined(_SILICON_LABS_32B_SERIES_1)
EFM_ASSERT(init->biasProg
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
/* Make sure the ACMP is disabled since ACMP power source might be changed.*/
ACMP_Disable(acmp);
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
| (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
| (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
| (init->inputRange << _ACMP_CTRL_INPUTRANGE_SHIFT)
| (init->accuracy << _ACMP_CTRL_ACCURACY_SHIFT)
| (init->powerSource << _ACMP_CTRL_PWRSEL_SHIFT)
| (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
acmp->INPUTSEL = init->vlpInput << _ACMP_INPUTSEL_VLPSEL_SHIFT;
acmp->HYSTERESIS0 = init->hysteresisLevel_0;
acmp->HYSTERESIS1 = init->hysteresisLevel_1;
#elif defined(_SILICON_LABS_32B_SERIES_0)
EFM_ASSERT(init->biasProg
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
/* Make sure the ACMP is disabled since ACMP power source might be changed.*/
ACMP_Disable(acmp);
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
| (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
| (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
| (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
| (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
| (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT)
| (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
acmp->INPUTSEL = (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
| (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT);
#endif
if (init->enable) {
ACMP_Enable(acmp);
} else {
ACMP_Disable(acmp);
}
}
#if defined(_ACMP_INPUTSEL_VASEL_MASK)
/***************************************************************************//**
* @brief
* Set up the VA source.
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param[in] vaconfig
* A pointer to the structure used to configure the VA source. This structure
* contains the input source and the 2 divider values.
******************************************************************************/
void ACMP_VASetup(ACMP_TypeDef *acmp, const ACMP_VAConfig_TypeDef *vaconfig)
{
EFM_ASSERT(vaconfig->div0 < 64);
EFM_ASSERT(vaconfig->div1 < 64);
BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VASEL_MASK,
vaconfig->input << _ACMP_INPUTSEL_VASEL_SHIFT);
BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVA_MASK,
vaconfig->div0 << _ACMP_HYSTERESIS0_DIVVA_SHIFT);
BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVA_MASK,
vaconfig->div1 << _ACMP_HYSTERESIS1_DIVVA_SHIFT);
}
#endif
#if defined(_ACMP_INPUTSEL_VBSEL_MASK)
/***************************************************************************//**
* @brief
* Set up the VB Source.
*
* @param[in] acmp
* A pointer to the ACMP peripheral register block.
*
* @param[in] vbconfig
* A pointer to the structure used to configure the VB source. This structure
* contains the input source and the 2 divider values.
******************************************************************************/
void ACMP_VBSetup(ACMP_TypeDef *acmp, const ACMP_VBConfig_TypeDef *vbconfig)
{
EFM_ASSERT(vbconfig->div0 < 64);
EFM_ASSERT(vbconfig->div1 < 64);
BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VBSEL_MASK,
vbconfig->input << _ACMP_INPUTSEL_VBSEL_SHIFT);
BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVB_MASK,
vbconfig->div0 << _ACMP_HYSTERESIS0_DIVVB_SHIFT);
BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVB_MASK,
vbconfig->div1 << _ACMP_HYSTERESIS1_DIVVB_SHIFT);
}
#endif
/** @} (end addtogroup acmp) */
#endif /* defined(ACMP_COUNT) && (ACMP_COUNT > 0) */