/***************************************************************************//**
 * @file em_idac.h
 * @brief Current Digital to Analog Converter (IDAC) peripheral API
 * @version 5.0.0
 *******************************************************************************
 * @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.
 *
 ******************************************************************************/

#ifndef EM_IDAC_H
#define EM_IDAC_H

#include "em_device.h"

#if defined(IDAC_COUNT) && (IDAC_COUNT > 0)
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************//**
 * @addtogroup emlib
 * @{
 ******************************************************************************/

/***************************************************************************//**
 * @addtogroup IDAC
 * @brief
 *  Current Digital-to-Analog Converter (IDAC) Peripheral API
 *
 * @details
 *  The current digital-to-analog converter (IDAC) can source or sink a configurable
 *  constant current, which can be output on, or sinked from pin or ADC. The current
 *  is configurable with several ranges of various step sizes. The IDAC can be used
 *  with PRS and it can operate down to EM3.
 *
 *  The following steps are necessary for basic operation:
 *
 *  Clock enable:
 *  @include em_idac_clock_enable.c
 *
 *  Initialize the peripheral with default settings and modify selected fields such as
 *  output select:
 *  @if DOXYDOC_P1_DEVICE
 *  @include em_idac_init_adc.c
 *  @endif
 *  @if DOXYDOC_P2_DEVICE
 *  @include em_idac_init_aport.c
 *  @endif
 *
 *  Set output:
 *  @include em_idac_basic_usage.c
 *
 * @{
 ******************************************************************************/

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */

/** Validation of IDAC register block pointer reference for assert statements. */
#define IDAC_REF_VALID(ref)    ((ref) == IDAC0)

/** @endcond */

/*******************************************************************************
 ********************************   ENUMS   ************************************
 ******************************************************************************/

/** Output mode. */
typedef enum
{
#if defined( _IDAC_CTRL_OUTMODE_MASK )
  idacOutputPin     = IDAC_CTRL_OUTMODE_PIN,     /**< Output to IDAC OUT pin */
  idacOutputADC     = IDAC_CTRL_OUTMODE_ADC      /**< Output to ADC */
#elif ( _IDAC_CTRL_APORTOUTSEL_MASK )
  idacOutputAPORT1XCH0 = IDAC_CTRL_APORTOUTSEL_APORT1XCH0, /**< Output to APORT 1X CH0 */
  idacOutputAPORT1YCH1 = IDAC_CTRL_APORTOUTSEL_APORT1YCH1, /**< Output to APORT 1Y CH1 */
  idacOutputAPORT1XCH2 = IDAC_CTRL_APORTOUTSEL_APORT1XCH2, /**< Output to APORT 1X CH2 */
  idacOutputAPORT1YCH3 = IDAC_CTRL_APORTOUTSEL_APORT1YCH3, /**< Output to APORT 1Y CH3 */
  idacOutputAPORT1XCH4 = IDAC_CTRL_APORTOUTSEL_APORT1XCH4, /**< Output to APORT 1X CH4 */
  idacOutputAPORT1YCH5 = IDAC_CTRL_APORTOUTSEL_APORT1YCH5, /**< Output to APORT 1Y CH5 */
  idacOutputAPORT1XCH6 = IDAC_CTRL_APORTOUTSEL_APORT1XCH6, /**< Output to APORT 1X CH6 */
  idacOutputAPORT1YCH7 = IDAC_CTRL_APORTOUTSEL_APORT1YCH7, /**< Output to APORT 1Y CH7 */
  idacOutputAPORT1XCH8 = IDAC_CTRL_APORTOUTSEL_APORT1XCH8, /**< Output to APORT 1X CH8 */
  idacOutputAPORT1YCH9 = IDAC_CTRL_APORTOUTSEL_APORT1YCH9, /**< Output to APORT 1Y CH9 */
  idacOutputAPORT1XCH10 = IDAC_CTRL_APORTOUTSEL_APORT1XCH10, /**< Output to APORT 1X CH10 */
  idacOutputAPORT1YCH11 = IDAC_CTRL_APORTOUTSEL_APORT1YCH11, /**< Output to APORT 1Y CH11 */
  idacOutputAPORT1XCH12 = IDAC_CTRL_APORTOUTSEL_APORT1XCH12, /**< Output to APORT 1X CH12 */
  idacOutputAPORT1YCH13 = IDAC_CTRL_APORTOUTSEL_APORT1YCH13, /**< Output to APORT 1Y CH13 */
  idacOutputAPORT1XCH14 = IDAC_CTRL_APORTOUTSEL_APORT1XCH14, /**< Output to APORT 1X CH14 */
  idacOutputAPORT1YCH15 = IDAC_CTRL_APORTOUTSEL_APORT1YCH15, /**< Output to APORT 1Y CH15 */
  idacOutputAPORT1XCH16 = IDAC_CTRL_APORTOUTSEL_APORT1XCH16, /**< Output to APORT 1X CH16 */
  idacOutputAPORT1YCH17 = IDAC_CTRL_APORTOUTSEL_APORT1YCH17, /**< Output to APORT 1Y CH17 */
  idacOutputAPORT1XCH18 = IDAC_CTRL_APORTOUTSEL_APORT1XCH18, /**< Output to APORT 1X CH18 */
  idacOutputAPORT1YCH19 = IDAC_CTRL_APORTOUTSEL_APORT1YCH19, /**< Output to APORT 1Y CH19 */
  idacOutputAPORT1XCH20 = IDAC_CTRL_APORTOUTSEL_APORT1XCH20, /**< Output to APORT 1X CH20 */
  idacOutputAPORT1YCH21 = IDAC_CTRL_APORTOUTSEL_APORT1YCH21, /**< Output to APORT 1Y CH21 */
  idacOutputAPORT1XCH22 = IDAC_CTRL_APORTOUTSEL_APORT1XCH22, /**< Output to APORT 1X CH22 */
  idacOutputAPORT1YCH23 = IDAC_CTRL_APORTOUTSEL_APORT1YCH23, /**< Output to APORT 1Y CH23 */
  idacOutputAPORT1XCH24 = IDAC_CTRL_APORTOUTSEL_APORT1XCH24, /**< Output to APORT 1X CH24 */
  idacOutputAPORT1YCH25 = IDAC_CTRL_APORTOUTSEL_APORT1YCH25, /**< Output to APORT 1Y CH25 */
  idacOutputAPORT1XCH26 = IDAC_CTRL_APORTOUTSEL_APORT1XCH26, /**< Output to APORT 1X CH26 */
  idacOutputAPORT1YCH27 = IDAC_CTRL_APORTOUTSEL_APORT1YCH27, /**< Output to APORT 1Y CH27 */
  idacOutputAPORT1XCH28 = IDAC_CTRL_APORTOUTSEL_APORT1XCH28, /**< Output to APORT 1X CH28 */
  idacOutputAPORT1YCH29 = IDAC_CTRL_APORTOUTSEL_APORT1YCH29, /**< Output to APORT 1Y CH29 */
  idacOutputAPORT1XCH30 = IDAC_CTRL_APORTOUTSEL_APORT1XCH30, /**< Output to APORT 1X CH30 */
  idacOutputAPORT1YCH31 = IDAC_CTRL_APORTOUTSEL_APORT1YCH31, /**< Output to APORT 1Y CH31 */
#endif
} IDAC_OutMode_TypeDef;


/** Selects which Peripheral Reflex System (PRS) signal to use when
    PRS is set to control the IDAC output. */
typedef enum
{
  idacPRSSELCh0 = IDAC_CTRL_PRSSEL_PRSCH0,      /**< PRS channel 0. */
  idacPRSSELCh1 = IDAC_CTRL_PRSSEL_PRSCH1,      /**< PRS channel 1. */
  idacPRSSELCh2 = IDAC_CTRL_PRSSEL_PRSCH2,      /**< PRS channel 2. */
  idacPRSSELCh3 = IDAC_CTRL_PRSSEL_PRSCH3,      /**< PRS channel 3. */
#if defined( IDAC_CTRL_PRSSEL_PRSCH4 )
  idacPRSSELCh4 = IDAC_CTRL_PRSSEL_PRSCH4,      /**< PRS channel 4. */
  idacPRSSELCh5 = IDAC_CTRL_PRSSEL_PRSCH5,      /**< PRS channel 5. */
#endif
#if defined( IDAC_CTRL_PRSSEL_PRSCH6 )
  idacPRSSELCh6 = IDAC_CTRL_PRSSEL_PRSCH6,      /**< PRS channel 6. */
  idacPRSSELCh7 = IDAC_CTRL_PRSSEL_PRSCH7,      /**< PRS channel 7. */
  idacPRSSELCh8 = IDAC_CTRL_PRSSEL_PRSCH8,      /**< PRS channel 8. */
  idacPRSSELCh9 = IDAC_CTRL_PRSSEL_PRSCH9,      /**< PRS channel 9. */
  idacPRSSELCh10 = IDAC_CTRL_PRSSEL_PRSCH10,    /**< PRS channel 10 */
  idacPRSSELCh11 = IDAC_CTRL_PRSSEL_PRSCH11,    /**< PRS channel 11 */
#endif
} IDAC_PRSSEL_TypeDef;


/** Selects which current range to use. */
typedef enum
{
  idacCurrentRange0 = IDAC_CURPROG_RANGESEL_RANGE0, /**< current range 0. */
  idacCurrentRange1 = IDAC_CURPROG_RANGESEL_RANGE1, /**< current range 1. */
  idacCurrentRange2 = IDAC_CURPROG_RANGESEL_RANGE2, /**< current range 2. */
  idacCurrentRange3 = IDAC_CURPROG_RANGESEL_RANGE3, /**< current range 3. */
} IDAC_Range_TypeDef;

/*******************************************************************************
 *******************************   STRUCTS   ***********************************
 ******************************************************************************/

/** IDAC init structure, common for both channels. */
typedef struct
{
  /** Enable IDAC. */
  bool                  enable;

  /** Output mode */
  IDAC_OutMode_TypeDef  outMode;

  /**
   * Enable Peripheral reflex system (PRS) to control IDAC output. If false,
   * the IDAC output is controlled by writing to IDAC_OUTEN in IDAC_CTRL or
   * by calling IDAC_OutEnable().
   */
  bool                  prsEnable;

  /**
   * Peripheral reflex system channel selection. Only applicable if @p prsEnable
   * is enabled.
   */
  IDAC_PRSSEL_TypeDef   prsSel;

  /** Enable/disable current sink mode. */
  bool                  sinkEnable;

} IDAC_Init_TypeDef;

/** Default config for IDAC init structure. */
#if defined( _IDAC_CTRL_OUTMODE_MASK )
#define IDAC_INIT_DEFAULT                                              \
{                                                                      \
  false,          /**< Leave IDAC disabled when init done. */          \
  idacOutputPin,  /**< Output to IDAC output pin. */                   \
  false,          /**< Disable PRS triggering. */                      \
  idacPRSSELCh0,  /**< Select PRS ch0 (if PRS triggering enabled). */  \
  false           /**< Disable current sink mode. */                   \
}
#elif ( _IDAC_CTRL_APORTOUTSEL_MASK )
#define IDAC_INIT_DEFAULT                                              \
{                                                                      \
  false,          /**< Leave IDAC disabled when init done. */          \
  idacOutputAPORT1XCH0, /**< Output to APORT. */                       \
  false,          /**< Disable PRS triggering. */                      \
  idacPRSSELCh0,  /**< Select PRS ch0 (if PRS triggering enabled). */  \
  false           /**< Disable current sink mode. */                   \
}
#endif


/*******************************************************************************
 *****************************   PROTOTYPES   **********************************
 ******************************************************************************/


void IDAC_Init(IDAC_TypeDef *idac, const IDAC_Init_TypeDef *init);
void IDAC_Enable(IDAC_TypeDef *idac, bool enable);
void IDAC_Reset(IDAC_TypeDef *idac);
void IDAC_MinimalOutputTransitionMode(IDAC_TypeDef *idac, bool enable);
void IDAC_RangeSet(IDAC_TypeDef *idac, const IDAC_Range_TypeDef range);
void IDAC_StepSet(IDAC_TypeDef *idac, const uint32_t step);
void IDAC_OutEnable(IDAC_TypeDef *idac, bool enable);


#if defined( _IDAC_IEN_MASK )
/***************************************************************************//**
 * @brief
 *   Clear one or more pending IDAC interrupts.
 *
 * @param[in] idac
 *   Pointer to IDAC peripheral register block.
 *
 * @param[in] flags
 *   Pending IDAC interrupt source(s) to clear. Use one or more valid
 *   interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together.
 ******************************************************************************/
__STATIC_INLINE void IDAC_IntClear(IDAC_TypeDef *idac, uint32_t flags)
{
  idac->IFC = flags;
}


/***************************************************************************//**
 * @brief
 *   Disable one or more IDAC interrupts.
 *
 * @param[in] idac
 *   Pointer to IDAC peripheral register block.
 *
 * @param[in] flags
 *   IDAC interrupt source(s) to disable. Use one or more valid
 *   interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together.
 ******************************************************************************/
__STATIC_INLINE void IDAC_IntDisable(IDAC_TypeDef *idac, uint32_t flags)
{
  idac->IEN &= ~flags;
}


/***************************************************************************//**
 * @brief
 *   Enable one or more IDAC interrupts.
 *
 * @note
 *   Depending on the use, a pending interrupt may already be set prior to
 *   enabling the interrupt. Consider using IDAC_IntClear() prior to enabling
 *   if such a pending interrupt should be ignored.
 *
 * @param[in] idac
 *   Pointer to IDAC peripheral register block.
 *
 * @param[in] flags
 *   IDAC interrupt source(s) to enable. Use one or more valid
 *   interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together.
 ******************************************************************************/
__STATIC_INLINE void IDAC_IntEnable(IDAC_TypeDef *idac, uint32_t flags)
{
  idac->IEN |= flags;
}


/***************************************************************************//**
 * @brief
 *   Get pending IDAC interrupt flags.
 *
 * @note
 *   The event bits are not cleared by the use of this function.
 *
 * @param[in] idac
 *   Pointer to IDAC peripheral register block.
 *
 * @return
 *   IDAC interrupt source(s) pending. Returns one or more valid
 *   interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together.
 ******************************************************************************/
__STATIC_INLINE uint32_t IDAC_IntGet(IDAC_TypeDef *idac)
{
  return idac->IF;
}


/***************************************************************************//**
 * @brief
 *   Get enabled and pending IDAC interrupt flags.
 *   Useful for handling more interrupt sources in the same interrupt handler.
 *
 * @param[in] idac
 *   Pointer to IDAC peripheral register block.
 *
 * @note
 *   Interrupt flags are not cleared by the use of this function.
 *
 * @return
 *   Pending and enabled IDAC interrupt sources.
 *   The return value is the bitwise AND combination of
 *   - the OR combination of enabled interrupt sources in IDACx_IEN_nnn
 *     register (IDACx_IEN_nnn) and
 *   - the OR combination of valid interrupt flags of the IDAC module
 *     (IDACx_IF_nnn).
 ******************************************************************************/
__STATIC_INLINE uint32_t IDAC_IntGetEnabled(IDAC_TypeDef *idac)
{
  uint32_t ien;

  /* Store flags in temporary variable in order to define explicit order
   * of volatile accesses. */
  ien = idac->IEN;

  /* Bitwise AND of pending and enabled interrupts */
  return idac->IF & ien;
}


/***************************************************************************//**
 * @brief
 *   Set one or more pending IDAC interrupts from SW.
 *
 * @param[in] idac
 *   Pointer to IDAC peripheral register block.
 *
 * @param[in] flags
 *   IDAC interrupt source(s) to set to pending. Use one or more valid
 *   interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together.
 ******************************************************************************/
__STATIC_INLINE void IDAC_IntSet(IDAC_TypeDef *idac, uint32_t flags)
{
  idac->IFS = flags;
}
#endif


/** @} (end addtogroup IDAC) */
/** @} (end addtogroup emlib) */

#ifdef __cplusplus
}
#endif

#endif /* defined(IDAC_COUNT) && (IDAC_COUNT > 0) */

#endif /* EM_IDAC_H */
