rt-thread/bsp/efm32/Libraries/emlib/src/em_acmp.c

299 lines
11 KiB
C

/***************************************************************************//**
* @file
* @brief Analog Comparator (ACMP) Peripheral API
* @author Energy Micro AS
* @version 3.0.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
******************************************************************************/
#include <stdbool.h>
#include "em_acmp.h"
#include "em_bitband.h"
#include "em_assert.h"
/***************************************************************************//**
* @addtogroup EM_Library
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup ACMP
* @brief Analog comparator (ACMP) Peripheral API
* @{
******************************************************************************/
/*******************************************************************************
******************************* 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))
#else
#error Undefined number of analog comparators (ACMP).
#endif
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Sets up the ACMP for use in capacative sense applications.
*
* @details
* This function sets up the ACMP for use in capacacitve sense applications.
* To use the capacative sense functionality in the ACMP you need to use
* the PRS output of the ACMP module to count the number of oscillations
* in the capacative sense circuit (possibly using a TIMER).
*
* @note
* A basic example of capacative sensing can be found in the STK BSP
* (capsense demo).
*
* @param[in] acmp
* Pointer to ACMP peripheral register block.
*
* @param[in] init
* Pointer to initialization structure used to configure ACMP for capacative
* sensing operation.
******************************************************************************/
void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init)
{
/* Make sure the module exists on the selected chip */
EFM_ASSERT(ACMP_REF_VALID(acmp));
/* Make sure that vddLevel is within bounds */
EFM_ASSERT(init->vddLevel < 64);
/* Make sure biasprog is within bounds */
EFM_ASSERT(init->biasProg < 16);
/* Set 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 capacative 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;
/* Enable ACMP if requested.
* Note: BITBAND_Peripheral() function is used for setting/clearing single
* bit peripheral register bitfields. */
BITBAND_Peripheral(&(acmp->CTRL),
(uint32_t)_ACMP_CTRL_EN_SHIFT,
(uint32_t)init->enable);
}
/***************************************************************************//**
* @brief
* Sets the ACMP channel used for capacative sensing.
*
* @note
* A basic example of capacative sensing can be found in the STK BSP
* (capsense demo).
*
* @param[in] acmp
* Pointer to ACMP peripheral register block.
*
* @param[in] channel
* The ACMP channel to use for capacative sensing (Possel).
******************************************************************************/
void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel)
{
/* Make sure that only external channels are used */
EFM_ASSERT(channel < _ACMP_INPUTSEL_NEGSEL_1V25);
/* Set channel as positive channel in ACMP */
SET_BIT_FIELD(acmp->INPUTSEL, _ACMP_INPUTSEL_POSSEL_MASK, channel,
_ACMP_INPUTSEL_POSSEL_SHIFT);
}
/***************************************************************************//**
* @brief
* Disables the ACMP.
*
* @param[in] acmp
* Pointer to ACMP peripheral register block.
******************************************************************************/
void ACMP_Disable(ACMP_TypeDef *acmp)
{
acmp->CTRL &= ~ACMP_CTRL_EN;
}
/***************************************************************************//**
* @brief
* Enables the ACMP.
*
* @param[in] acmp
* Pointer to ACMP peripheral register block.
******************************************************************************/
void ACMP_Enable(ACMP_TypeDef *acmp)
{
acmp->CTRL |= ACMP_CTRL_EN;
}
/***************************************************************************//**
* @brief
* Reset ACMP to same state as after a HW reset.
*
* @note
* The ROUTE register is NOT reset by this function, in order to allow for
* centralized setup of this feature.
*
* @param[in] acmp
* 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));
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
acmp->INPUTSEL = _ACMP_INPUTSEL_RESETVALUE;
acmp->IEN = _ACMP_IEN_RESETVALUE;
acmp->IFC = _ACMP_IF_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 location
* The pin location to use. See the datasheet 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)
{
/* Sanity checking of location */
EFM_ASSERT(location < 4);
/* Set GPIO inversion */
SET_BIT_FIELD(acmp->CTRL, _ACMP_CTRL_GPIOINV_MASK, invert,
_ACMP_CTRL_GPIOINV_SHIFT);
acmp->ROUTE = (location << _ACMP_ROUTE_LOCATION_SHIFT)
| (enable << _ACMP_ROUTE_ACMPPEN_SHIFT);
}
/***************************************************************************//**
* @brief
* Sets which channels should be used in ACMP comparisons.
*
* @param[in] acmp
* Pointer to the ACMP peripheral register block.
*
* @param negSel
* Channel to use on the negative input to the ACMP.
*
* @param posSel
* 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 that only external channels are used as ACMP positive input */
EFM_ASSERT(posSel < _ACMP_INPUTSEL_NEGSEL_1V25);
/* Sanity checking of ACMP negative input */
EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_VDD);
acmp->INPUTSEL = (acmp->INPUTSEL & ~(_ACMP_INPUTSEL_POSSEL_MASK |
_ACMP_INPUTSEL_NEGSEL_MASK))
| (negSel << _ACMP_INPUTSEL_NEGSEL_SHIFT)
| (posSel << _ACMP_INPUTSEL_POSSEL_SHIFT);
}
/***************************************************************************//**
* @brief
*
*
* @param[in] acmp
* Pointer to the ACMP peripheral register block.
*
* @param[in] init
* Pointer to initialization structure used to configure ACMP for capacative
* sensing operation.
******************************************************************************/
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));
/* Make sure biasprog is within bounds */
EFM_ASSERT(init->biasProg < 16);
/* Set 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->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);
/* Enable ACMP if requested.
* Note: BITBAND_Peripheral() function is used for setting/clearing single
* bit peripheral register bitfields. */
BITBAND_Peripheral(&(acmp->CTRL),
(uint32_t)_ACMP_CTRL_EN_SHIFT,
(uint32_t)init->enable);
}
/** @} (end addtogroup ACMP) */
/** @} (end addtogroup EM_Library) */