blob: 3ba2812a1809a536b27c2e65abe4864e854c04e9 [file] [log] [blame]
/***************************************************************************//**
* @file em_idac.c
* @brief Current Digital to Analog Converter (IDAC) peripheral API
* @version 5.1.2
*******************************************************************************
* @section License
* <b>Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.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: Silicon Labs has no
* obligation to support this Software. Silicon Labs 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.
*
* Silicon Labs 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 "em_idac.h"
#if defined(IDAC_COUNT) && (IDAC_COUNT > 0)
#include "em_cmu.h"
#include "em_assert.h"
#include "em_bus.h"
/***************************************************************************//**
* @addtogroup emlib
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup IDAC
* @{
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* Fix for errata IDAC_E101 - IDAC output current degradation */
#if defined(_SILICON_LABS_32B_SERIES_0) \
&& (defined(_EFM32_ZERO_FAMILY) || defined(_EFM32_HAPPY_FAMILY))
#define ERRATA_FIX_IDAC_E101_EN
#endif
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Initialize IDAC.
*
* @details
* Initializes IDAC according to the initialization structure parameter, and
* sets the default calibration value stored in the DEVINFO structure.
*
* @note
* This function will disable the IDAC prior to configuration.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
*
* @param[in] init
* Pointer to IDAC initialization structure.
******************************************************************************/
void IDAC_Init(IDAC_TypeDef *idac, const IDAC_Init_TypeDef *init)
{
uint32_t tmp;
EFM_ASSERT(IDAC_REF_VALID(idac));
tmp = (uint32_t)(init->prsSel);
tmp |= init->outMode;
if (init->enable)
{
tmp |= IDAC_CTRL_EN;
}
if (init->prsEnable)
{
#if defined(_IDAC_CTRL_OUTENPRS_MASK)
tmp |= IDAC_CTRL_OUTENPRS;
#else
tmp |= IDAC_CTRL_APORTOUTENPRS;
#endif
}
if (init->sinkEnable)
{
tmp |= IDAC_CTRL_CURSINK;
}
idac->CTRL = tmp;
}
/***************************************************************************//**
* @brief
* Enable/disable IDAC.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
*
* @param[in] enable
* true to enable IDAC, false to disable.
******************************************************************************/
void IDAC_Enable(IDAC_TypeDef *idac, bool enable)
{
EFM_ASSERT(IDAC_REF_VALID(idac));
BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_EN_SHIFT, enable);
}
/***************************************************************************//**
* @brief
* Reset IDAC to same state as after a HW reset.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
******************************************************************************/
void IDAC_Reset(IDAC_TypeDef *idac)
{
EFM_ASSERT(IDAC_REF_VALID(idac));
#if defined(ERRATA_FIX_IDAC_E101_EN)
/* Fix for errata IDAC_E101 - IDAC output current degradation:
Instead of disabling it we will put it in it's lowest power state (50 nA)
to avoid degradation over time */
/* Make sure IDAC is enabled with disabled output */
idac->CTRL = _IDAC_CTRL_RESETVALUE | IDAC_CTRL_EN;
/* Set lowest current (50 nA) */
idac->CURPROG = IDAC_CURPROG_RANGESEL_RANGE0 |
(0x0 << _IDAC_CURPROG_STEPSEL_SHIFT);
/* Enable duty-cycling for all energy modes */
idac->DUTYCONFIG = IDAC_DUTYCONFIG_DUTYCYCLEEN;
#else
idac->CTRL = _IDAC_CTRL_RESETVALUE;
idac->CURPROG = _IDAC_CURPROG_RESETVALUE;
idac->DUTYCONFIG = _IDAC_DUTYCONFIG_RESETVALUE;
#endif
#if defined ( _IDAC_CAL_MASK )
idac->CAL = _IDAC_CAL_RESETVALUE;
#endif
}
/***************************************************************************//**
* @brief
* Enable/disable Minimal Output Transition mode.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
*
* @param[in] enable
* true to enable Minimal Output Transition mode, false to disable.
******************************************************************************/
void IDAC_MinimalOutputTransitionMode(IDAC_TypeDef *idac, bool enable)
{
EFM_ASSERT(IDAC_REF_VALID(idac));
BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_MINOUTTRANS_SHIFT, enable);
}
/***************************************************************************//**
* @brief
* Set the current range of the IDAC output.
*
* @details
* This function sets the current range of the IDAC output. The function
* also updates the IDAC calibration register (IDAC_CAL) with the default
* calibration value from DEVINFO (factory calibration) corresponding to the
* specified range.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
*
* @param[in] range
* Current range value.
******************************************************************************/
void IDAC_RangeSet(IDAC_TypeDef *idac, const IDAC_Range_TypeDef range)
{
uint32_t tmp;
#if defined( _IDAC_CURPROG_TUNING_MASK )
uint32_t diCal0;
uint32_t diCal1;
#endif
EFM_ASSERT(IDAC_REF_VALID(idac));
EFM_ASSERT(((uint32_t)range >> _IDAC_CURPROG_RANGESEL_SHIFT)
<= (_IDAC_CURPROG_RANGESEL_MASK >> _IDAC_CURPROG_RANGESEL_SHIFT));
#if defined ( _IDAC_CAL_MASK )
/* Load proper calibration data depending on selected range */
switch ((IDAC_Range_TypeDef)range)
{
case idacCurrentRange0:
idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE0_MASK)
>> _DEVINFO_IDAC0CAL0_RANGE0_SHIFT;
break;
case idacCurrentRange1:
idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE1_MASK)
>> _DEVINFO_IDAC0CAL0_RANGE1_SHIFT;
break;
case idacCurrentRange2:
idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE2_MASK)
>> _DEVINFO_IDAC0CAL0_RANGE2_SHIFT;
break;
case idacCurrentRange3:
idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE3_MASK)
>> _DEVINFO_IDAC0CAL0_RANGE3_SHIFT;
break;
}
tmp = idac->CURPROG & ~_IDAC_CURPROG_RANGESEL_MASK;
tmp |= (uint32_t)range;
#elif defined( _IDAC_CURPROG_TUNING_MASK )
/* Load calibration data depending on selected range and sink/source mode */
/* TUNING (calibration) field in CURPROG register. */
EFM_ASSERT(idac == IDAC0);
diCal0 = DEVINFO->IDAC0CAL0;
diCal1 = DEVINFO->IDAC0CAL1;
tmp = idac->CURPROG & ~(_IDAC_CURPROG_TUNING_MASK
| _IDAC_CURPROG_RANGESEL_MASK);
if (idac->CTRL & IDAC_CTRL_CURSINK)
{
switch (range)
{
case idacCurrentRange0:
tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_MASK)
>> _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
case idacCurrentRange1:
tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_MASK)
>> _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
case idacCurrentRange2:
tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_MASK)
>> _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
case idacCurrentRange3:
tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_MASK)
>> _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
}
}
else
{
switch (range)
{
case idacCurrentRange0:
tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_MASK)
>> _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
case idacCurrentRange1:
tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_MASK)
>> _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
case idacCurrentRange2:
tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_MASK)
>> _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
case idacCurrentRange3:
tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_MASK)
>> _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_SHIFT)
<< _IDAC_CURPROG_TUNING_SHIFT;
break;
}
}
tmp |= (uint32_t)range;
#else
#warning "IDAC calibration register definition unknown."
#endif
idac->CURPROG = tmp;
}
/***************************************************************************//**
* @brief
* Set the current step of the IDAC output.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
*
* @param[in] step
* Step value for IDAC output. Valid range is 0-31.
******************************************************************************/
void IDAC_StepSet(IDAC_TypeDef *idac, const uint32_t step)
{
uint32_t tmp;
EFM_ASSERT(IDAC_REF_VALID(idac));
EFM_ASSERT(step <= (_IDAC_CURPROG_STEPSEL_MASK >> _IDAC_CURPROG_STEPSEL_SHIFT));
tmp = idac->CURPROG & ~_IDAC_CURPROG_STEPSEL_MASK;
tmp |= step << _IDAC_CURPROG_STEPSEL_SHIFT;
idac->CURPROG = tmp;
}
/***************************************************************************//**
* @brief
* Enable/disable the IDAC OUT pin.
*
* @param[in] idac
* Pointer to IDAC peripheral register block.
*
* @param[in] enable
* true to enable the IDAC OUT pin, false to disable.
******************************************************************************/
void IDAC_OutEnable(IDAC_TypeDef *idac, bool enable)
{
EFM_ASSERT(IDAC_REF_VALID(idac));
#if defined(_IDAC_CTRL_OUTEN_MASK)
BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_OUTEN_SHIFT, enable);
#else
BUS_RegBitWrite(&idac->CTRL, _IDAC_CTRL_APORTOUTEN_SHIFT, enable);
#endif
}
/** @} (end addtogroup IDAC) */
/** @} (end addtogroup emlib) */
#endif /* defined(IDAC_COUNT) && (IDAC_COUNT > 0) */