/**
 * \file
 *
 * \brief AVR XMEGA Analog to Digital Converter driver
 *
 * Copyright (C) 2010-2013 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \asf_license_stop
 *
 */
#ifndef ADC_H
#define ADC_H

#include <compiler.h>
#include <conf_adc.h>
#include <nvm.h>
#include <parts.h>
#include <sleepmgr.h>
#include <sysclk.h>

#ifdef __cplusplus
extern "C"
{
#endif

/* Fix header error in ADC_CH_t structure about missing SCAN register */
#ifndef ADC_CH_OFFSET_gp
# define ADC_CH_OFFSET_gp  4	/* Positive MUX setting offset group position. */
# if XMEGA_A || XMEGA_D
#   ifdef __ICCAVR__
#     define SCAN   reserved_0x06
#   else
#     define SCAN   reserved_0x6
#   endif
# endif
#endif

/* Fix header error */
#define ADC_EVACT_SYNCSWEEP_gc  (0x06 << 0)
#define ADC_REFSEL_INTVCC_gc   (0x01 << 4)
#define ADC_REFSEL_VCCDIV2_gc  (0x04 << 4)
#define ADC_CH_GAIN_DIV2_gc    (0x07 << 2)
#if (!XMEGA_A)
/* ADC.CTRLB  bit masks and bit positions */
#  define ADC_CURRLIMIT_NO_gc   (0x00 << 5)
#  define ADC_CURRLIMIT_LOW_gc  (0x01 << 5)
#  define ADC_CURRLIMIT_MED_gc  (0x02 << 5)
#  define ADC_CURRLIMIT_HIGH_gc (0x03 << 5)
#endif
#if (!XMEGA_A) && (!defined ADC_CURRLIMIT_gm)
/* ADC.CTRLB  bit masks and bit positions */
#  define ADC_CURRLIMIT_gm  0x60	/* Current limit group mask. */
#endif
#if (!XMEGA_E)
/* Negative input multiplexer selection without gain */
  typedef enum ADC_CH_MUXNEG_MODE10_enum
  {
    ADC_CH_MUXNEG_MODE10_PIN0_gc = (0x00 << 0),	/* Input pin 0 */
    ADC_CH_MUXNEG_MODE10_PIN1_gc = (0x01 << 0),	/* Input pin 1 */
    ADC_CH_MUXNEG_MODE10_PIN2_gc = (0x02 << 0),	/* Input pin 2 */
    ADC_CH_MUXNEG_MODE10_PIN3_gc = (0x03 << 0),	/* Input pin 3 */
    ADC_CH_MUXNEG_MODE10_GND_gc = (0x05 << 0),	/* PAD ground */
    ADC_CH_MUXNEG_MODE10_INTGND_gc = (0x07 << 0),	/* Internal ground */
  }
  ADC_CH_MUXNEGL_t;

/* Negative input multiplexer selection with gain */
  typedef enum ADC_CH_MUXNEG_MODE11_enum
  {
    ADC_CH_MUXNEG_MODE11_PIN4_gc = (0x00 << 0),	/* Input pin 4 */
    ADC_CH_MUXNEG_MODE11_PIN5_gc = (0x01 << 0),	/* Input pin 5 */
    ADC_CH_MUXNEG_MODE11_PIN6_gc = (0x02 << 0),	/* Input pin 6 */
    ADC_CH_MUXNEG_MODE11_PIN7_gc = (0x03 << 0),	/* Input pin 7 */
    ADC_CH_MUXNEG_MODE11_INTGND_gc = (0x04 << 0),	/* Internal ground */
    ADC_CH_MUXNEG_MODE11_GND_gc = (0x05 << 0),	/* PAD ground */
  }
  ADC_CH_MUXNEGH_t;
#endif

/**
 * \defgroup adc_group Analog to Digital Converter (ADC)
 *
 * See \ref adc_quickstart.
 *
 * This is a driver for the AVR XMEGA ADC. It provides functions for enabling,
 * disabling and configuring the ADC modules and their individual channels.
 *
 * The driver API is split in two parts:
 * - \ref adc_channel_group
 * - \ref adc_module_group
 *
 * Both APIs use structures that contain the configuration. These structures
 * must be set up before the configuration is written to either an ADC module or
 * one of their channels.
 *
 * After the ADC has been configured it must be enabled before any conversions
 * may be performed. To ensure accurate conversions, please wait for at least
 * the specified start-up time between enabling the ADC module, and starting
 * a conversion. For most XMEGA devices the start-up time is specified
 * to be a maximum of 24 ADC clock cycles. Please verify the start-up time for
 * the device in use.
 *
 * \note Not all of the documented functions are available on all devices. This
 * is due to differences in the ADC feature set. Refer to the device manual and
 * datasheet for details on which features are available for a specific device.
 *
 * \note The functions for creating/changing configurations are not protected
 * against interrupts. The functions that read from or write to the ADC's
 * registers are protected unless otherwise noted.
 *
 * \section dependencies Dependencies
 * This driver depends on the following modules:
 * - \ref sysclk_group for peripheral clock control.
 * - \ref sleepmgr_group for setting allowed sleep mode.
 * - \ref nvm_group for getting factory calibration data.
 * - \ref interrupt_group for ISR definition and disabling interrupts during
 * critical code sections.
 * @{
 */

/**
 * \defgroup adc_module_group ADC module
 *
 * Management and configuration functions for the ADC module.
 *
 * The API functions and definitions can be divided in three groups:
 * - interrupt callback: configure and set interrupt callback function.
 * - module management: direct access for enabling and disabling the ADC,
 * starting conversions, getting interrupt flags, etc.
 * - module configuration: create/change configurations and write/read them
 * to/from an ADC.
 *
 * @{
 */

/**
 * \def ADC_NR_OF_CHANNELS
 * \brief Number of channels per ADC
 */
#if XMEGA_A || XMEGA_AU || defined(__DOXYGEN__)
#  define ADC_NR_OF_CHANNELS    4
#elif XMEGA_B || XMEGA_C || XMEGA_D || XMEGA_E
#  define ADC_NR_OF_CHANNELS    1
#endif

/** ADC configuration */
  struct adc_config
  {
#if ADC_NR_OF_CHANNELS > 1
    /* DMA group request is stored in CTRLA */
    uint8_t ctrla;
#endif
    uint8_t ctrlb;
    uint8_t refctrl;
    uint8_t evctrl;
    uint8_t prescaler;
    uint16_t cmp;
#if XMEGA_E
    /* XMEGA E sample time value stored in SAMPCTRL */
    uint8_t sampctrl;
#endif
  };

/**
 * \name Calibration data addresses
 * \note The temperature sensor calibration is sampled at 85 degrees Celsius
 * with unsigned, 12-bit conversion.
 */
/** @{ */

/** ADC A, calibration byte 0. */
#define ADCACAL0      offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0)
/** ADC A, calibration byte 1. */
#define ADCACAL1      offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1)
/** ADC B, calibration byte 0. */
#define ADCBCAL0      offsetof(NVM_PROD_SIGNATURES_t, ADCBCAL0)
/** ADC B, calibration byte 1. */
#define ADCBCAL1      offsetof(NVM_PROD_SIGNATURES_t, ADCBCAL1)
/** Temperature sensor calibration byte 0. */
#define TEMPSENSE0    offsetof(NVM_PROD_SIGNATURES_t, TEMPSENSE0)
/** Temperature sensor calibration byte 1. */
#define TEMPSENSE1    offsetof(NVM_PROD_SIGNATURES_t, TEMPSENSE1)

/** @} */

/** \brief ADC calibration data */
  enum adc_calibration_data
  {
    ADC_CAL_ADCA,	 /**< ADC A pipeline calibration data. */
    ADC_CAL_ADCB,	 /**< ADC B pipeline calibration data. */

	/**
	 * \brief Temperature sensor calibration data.
	 * \note 12-bit unsigned, measured at 85 degrees Celsius, equivalent to
	 * 358.15 kelvin.
	 */
    ADC_CAL_TEMPSENSE,
  };

/** \name ADC channel masks */
/** @{ */

#define ADC_CH0      (1U << 0)		       /**< ADC channel 0. */

#if XMEGA_A || XMEGA_AU || defined(__DOXYGEN__)
#  define ADC_CH1    (1U << 1)		       /**< ADC channel 1. */
#  define ADC_CH2    (1U << 2)		       /**< ADC channel 2. */
#  define ADC_CH3    (1U << 3)		       /**< ADC channel 3. */
#endif

/** @} */

/** \name Internal ADC input masks */
/** @{ */

#define ADC_INT_TEMPSENSE    ADC_TEMPREF_bm    /**< Temperature sensor. */
#define ADC_INT_BANDGAP      ADC_BANDGAP_bm    /**< Bandgap reference. */

/** @} */

/**
 * \brief ADC conversion trigger settings
 *
 * \note The choice in conversion triggers varies between device families.
 * Refer to the device manual for detailed information.
 */
  enum adc_trigger
  {
	/** Manually triggered conversions */
    ADC_TRIG_MANUAL,

	/** Freerun mode conversion */
    ADC_TRIG_FREERUN,

	/**
	 * \brief Event-triggered conversions on individual channels
	 * Pairs each event channel with an ADC channel.
	 * \note The maximum base event channel that can be used is determined
	 * by the number of channels to trigger conversions on.
	 */
    ADC_TRIG_EVENT_SINGLE,

#if ADC_NR_OF_CHANNELS > 1
	/**
	 * \brief Freerunning conversion sweeps
	 * \note These will start as soon as the ADC is enabled.
	 */
    ADC_TRIG_FREERUN_SWEEP,

	/**
	 * \brief Event-triggered conversion sweeps
	 * \note Only the base event channel is used in this mode.
	 */
    ADC_TRIG_EVENT_SWEEP,
#endif

	/**
	 * \brief Event-triggered, synchronized conversion sweeps
	 * \note Only the base event channel is used in this mode.
	 */
    ADC_TRIG_EVENT_SYNCSWEEP,
  };

/** \brief ADC signedness settings */
  enum adc_sign
  {
    ADC_SIGN_OFF,			 /**< Unsigned conversions. */
    ADC_SIGN_ON = ADC_CONMODE_bm,	 /**< Signed conversions. */
  };

/** \brief ADC resolution settings */
  enum adc_resolution
  {
	/** 8-bit resolution, right-adjusted. */
    ADC_RES_8 = ADC_RESOLUTION_8BIT_gc,
	/** 12-bit resolution, right-adjusted. */
    ADC_RES_12 = ADC_RESOLUTION_12BIT_gc,
	/** 12-bit resolution, left-adjusted. */
    ADC_RES_12_LEFT = ADC_RESOLUTION_LEFT12BIT_gc,
#if XMEGA_E

	/** More than 12-bit resolution.
	 * Must be used when adcch_enable_averaging() or
	 * adcch_enable_oversampling() is used.
	 */
    ADC_RES_MT12 = ADC_RESOLUTION_MT12BIT_gc,
#endif
  };

/**
 * \brief ADC reference settings
 *
 * \note The choice in voltage reference varies between device families.
 * Refer to the device manual for detailed information.
 */
  enum adc_reference
  {
	/** Internal 1 V from bandgap reference. */
    ADC_REF_BANDGAP = ADC_REFSEL_INT1V_gc,
	/** VCC divided by 1.6. */
    ADC_REF_VCC = ADC_REFSEL_INTVCC_gc,
	/** External reference on AREFA pin. */
    ADC_REF_AREFA = ADC_REFSEL_AREFA_gc,
#if XMEGA_E
	/** External reference on AREFD pin. */
    ADC_REF_AREFD = ADC_REFSEL_AREFD_gc,
#else
	/** External reference on AREFB pin. */
    ADC_REF_AREFB = ADC_REFSEL_AREFB_gc,
#endif
	/** VCC divided by 2. */
    ADC_REF_VCCDIV2 = ADC_REFSEL_VCCDIV2_gc,
  };

/** \name Internal functions for driver */
/** @{ */

/**
 * \internal
 * \brief Get ADC channel pointer from channel mask
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel
 * can be given in mask)
 *
 * \return Pointer to ADC channel
 */
  __always_inline ADC_CH_t *adc_get_channel (ADC_t * adc, uint8_t ch_mask);

  __always_inline ADC_CH_t *adc_get_channel (ADC_t * adc, uint8_t ch_mask)
  {
    uint8_t index = 0;

      Assert (ch_mask & ((1 << ADC_NR_OF_CHANNELS) - 1));

    /* Use a conditional inline ctz for optimization. */
#if ADC_NR_OF_CHANNELS > 4
    if (!(ch_mask & 0x0f))
      {
	index += 4;
	ch_mask >>= 4;
      }
#endif
#if ADC_NR_OF_CHANNELS > 2
    if (!(ch_mask & 0x03))
      {
	index += 2;
	ch_mask >>= 2;
      }
#endif
#if ADC_NR_OF_CHANNELS > 1
    if (!(ch_mask & 0x01))
      {
	index++;
      }
#endif

    return (ADC_CH_t *) (&adc->CH0 + index);
  }

/** @} */

#if defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__)
/** \name ADC interrupt callback function */
/** @{ */

/**
 * \def CONFIG_ADC_CALLBACK_ENABLE
 * \brief Configuration symbol to enable callback on ADC interrupts
 *
 * Define this symbol in \ref conf_adc.h to enable callbacks on ADC interrupts.
 * A function of type \ref adc_callback_t must be defined by the user, and the
 * driver be configured to use it with \ref adc_set_callback.
 */
#if !defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__)
#  define CONFIG_ADC_CALLBACK_ENABLE
#endif

/**
 * \def CONFIG_ADC_CALLBACK_TYPE
 * \brief Configuration symbol for datatype of result parameter for callback
 *
 * Define the datatype of the ADC conversion result parameter for callback
 * functions. This should be defined according to the signedness and resolution
 * of the conversions:
 * - \c int16_t for signed, 12-bit
 * - \c uint16_t for unsigned, 12-bit (the default type)
 * - \c int8_t for signed, 8-bit
 * - \c uint8_t for unsigned, 8-bit
 *
 * Define this in \ref conf_adc.h if the default datatype is not desired.
 */
#if !defined(CONFIG_ADC_CALLBACK_TYPE) || defined(__DOXYGEN__)
#  define CONFIG_ADC_CALLBACK_TYPE    uint16_t
#endif

/** Datatype of ADC conversion result parameter for callback */
  typedef CONFIG_ADC_CALLBACK_TYPE adc_result_t;

/**
 * \brief ADC interrupt callback function pointer
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel
 * can be given in mask)
 * \param res ADC conversion result.
 */
  typedef void (*adc_callback_t) (ADC_t * adc, uint8_t ch_mask,
				  adc_result_t res);

  void adc_set_callback (ADC_t * adc, adc_callback_t callback);

/** @} */
#endif

/** \name ADC module management */
/** @{ */

  void adc_enable (ADC_t * adc);
  void adc_disable (ADC_t * adc);
  bool adc_is_enabled (ADC_t * adc);

/**
 * \brief Start one-shot conversion on ADC channel(s)
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed
 * together.)
 *
 * \note The ADC must be enabled for this function to have any effect.
 */
  static inline void adc_start_conversion (ADC_t * adc, uint8_t ch_mask)
  {
    irqflags_t flags = cpu_irq_save ();
#if !XMEGA_E
    adc->CTRLA |= ch_mask << ADC_CH0START_bp;
#else
    adc->CTRLA |= ch_mask << ADC_START_bp;
#endif
    cpu_irq_restore (flags);
  }

/**
 * \brief Get result from ADC channel
 *
 * Gets the latest conversion result from the ADC channel.
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel
 * can be given in mask)
 *
 * \return Latest conversion result of ADC channel. Signedness does not matter.
 *
 * \note This macro does not protect the 16-bit read from interrupts. If an
 * interrupt may do a 16-bit read or write to the ADC while this macro is
 * executing, interrupts \a must be temporarily disabled to avoid corruption of
 * the read.
 */
#define adc_get_result(adc, ch_mask)    (adc_get_channel(adc, ch_mask)->RES)

/**
 * \brief Get signed result from ADC channel
 *
 * Returns the latest conversion result from the ADC channel as a signed type,
 * with interrupt protection of the 16-bit read.
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel
 * can be given in mask)
 *
 * \return Latest conversion result of ADC channel, as signed 16-bit integer.
 */
  static inline int16_t adc_get_signed_result (ADC_t * adc, uint8_t ch_mask)
  {
    int16_t val;
    irqflags_t flags;
    ADC_CH_t *adc_ch;

    adc_ch = adc_get_channel (adc, ch_mask);

    flags = cpu_irq_save ();
    val = adc_ch->RES;
    cpu_irq_restore (flags);

    return val;
  }

/**
 * \brief Get unsigned result from ADC channel
 *
 * Returns the latest conversion result from the ADC channel as an unsigned
 * type, with interrupt protection of the 16-bit read.
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel
 * can be given in mask)
 *
 * \return Latest conversion result of ADC channel, as unsigned 16-bit integer.
 */
  static inline uint16_t adc_get_unsigned_result (ADC_t * adc,
						  uint8_t ch_mask)
  {
    uint16_t val;
    irqflags_t flags;
    ADC_CH_t *adc_ch;

    adc_ch = adc_get_channel (adc, ch_mask);

    flags = cpu_irq_save ();
    val = adc_ch->RES;
    cpu_irq_restore (flags);

    return val;
  }

/**
 * \brief Get interrupt flag of ADC channel(s)
 *
 * Returns the interrupt flag of the masked channels. The meaning of the
 * interrupt flag depends on what mode the individual channels are in.
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed
 * together.)
 *
 * \return Mask with interrupt flags.
 */
  static inline uint8_t adc_get_interrupt_flag (ADC_t * adc, uint8_t ch_mask)
  {
    return (adc->INTFLAGS >> ADC_CH0IF_bp) & ch_mask;
  }

/**
 * \brief Clear interrupt flag of ADC channel(s)
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed
 * together.)
 *
 * \note The ADC must be enabled for this function to have any effect.
 */
  static inline void adc_clear_interrupt_flag (ADC_t * adc, uint8_t ch_mask)
  {
    adc->INTFLAGS = ch_mask << ADC_CH0IF_bp;
  }

/**
 * \brief Wait for interrupt flag of ADC channel(s)
 *
 * Waits for the interrupt flag of the specified channel(s) to be set, then
 * clears it before returning. If several channels are masked, the function will
 * wait for \a all interrupt flags to be set.
 *
 * \param adc Pointer to ADC module.
 * \param ch_mask Mask of ADC channel(s):
 * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed
 * together.)
 */
  static inline void adc_wait_for_interrupt_flag (ADC_t * adc,
						  uint8_t ch_mask)
  {
    do
      {
      }
    while (adc_get_interrupt_flag (adc, ch_mask) != ch_mask);
    adc_clear_interrupt_flag (adc, ch_mask);
  }

/**
 * \brief Flush the ADC
 *
 * Forces the ADC to abort any ongoing conversions and restart its clock on the
 * next peripheral clock cycle. Pending conversions are started after the
 * clock reset.
 *
 * \param adc Pointer to ADC module.
 *
 * \note The ADC must be enabled for this function to have any effect.
 */
  static inline void adc_flush (ADC_t * adc)
  {
    irqflags_t flags = cpu_irq_save ();
    adc->CTRLA |= ADC_FLUSH_bm;
    cpu_irq_restore (flags);
  }

/**
 * \brief Set compare value directly to ADC
 *
 * Sets the compare value directly to the ADC, for quick access while the ADC is
 * enabled.
 *
 * \param adc Pointer to ADC module.
 * \param val Compare value to set, either signed or unsigned.
 *
 * \note The ADC must be enabled for this function to have any effect.
 */
#define adc_set_compare_value(adc, val)	\
	do { \
		irqflags_t ATPASTE2(adc_flags, __LINE__) =  cpu_irq_save(); \
		(adc)->CMP = val; \
		cpu_irq_restore(ATPASTE2(adc_flags, __LINE__));	\
	} \
	while (0)

/**
 * \brief Get compare value directly from ADC
 *
 * Gets the compare value directly from the ADC, for quick access while the ADC
 * is enabled.
 *
 * \param adc Pointer to ADC module.
 *
 * \return Current compare value of the ADC. Signedness does not matter.
 *
 * \note This macro does not protect the 16-bit read from interrupts. If an
 * interrupt may do a 16-bit read or write to the ADC while this macro is
 * executing, interrupts \a must be temporarily disabled to avoid corruption of
 * the read.
 */
#define adc_get_compare_value(adc)    ((adc)->CMP)

/**
 * \brief Get signed compare value directly from ADC
 *
 * Gets the signed compare value directly from the ADC, with interrupt
 * protection of the 16-bit read, for quick access while the ADC is enabled.
 *
 * \param adc Pointer to ADC module.
 */
  static inline int16_t adc_get_signed_compare_value (ADC_t * adc)
  {
    int16_t val;
    irqflags_t flags;

    flags = cpu_irq_save ();
    val = adc->CMP;
    cpu_irq_restore (flags);

    return val;
  }

/**
 * \brief Get unsigned compare value directly from ADC
 *
 * Gets the unsigned compare value directly from the ADC, with interrupt
 * protection of the 16-bit read, for quick access while the ADC is enabled.
 *
 * \param adc Pointer to ADC module.
 */
  static inline uint16_t adc_get_unsigned_compare_value (ADC_t * adc)
  {
    uint16_t val;
    irqflags_t flags;

    flags = cpu_irq_save ();
    val = adc->CMP;
    cpu_irq_restore (flags);

    return val;
  }

#if XMEGA_E

/**
 * \brief Set sample time value directly to ADC
 *
 * Sets the sample time value directly to the ADC, for quick access while the
 * ADC is enabled.
 *
 * \param adc Pointer to ADC module.
 * \param val Sample time value to set.
 *
 * \note The ADC must be enabled for this function to have any effect.
 */
  static inline void adc_set_sample_value (ADC_t * adc, uint8_t val)
  {
    irqflags_t flags;

    flags = cpu_irq_save ();
    adc->SAMPCTRL = (uint8_t) val;
    cpu_irq_restore (flags);
  }

/**
 * \brief Get sample time value directly from ADC
 *
 * Gets the sample time value directly from the ADC, for quick access while the
 * ADC is enabled.
 *
 * \param adc Pointer to ADC module.
 *
 * \return Current sample time value of the ADC.
 *
 * \note This macro does not protect the 8-bit read from interrupts. If an
 * interrupt may do a 8-bit read or write to the ADC while this macro is
 * executing, interrupts \a must be temporarily disabled to avoid corruption of
 * the read.
 */
  static inline uint8_t adc_get_sample_value (ADC_t * adc)
  {
    return adc->SAMPCTRL;
  }

#endif

/**
 * \brief Get calibration data
 *
 * \param cal Identifier for calibration data to get.
 */
  static inline uint16_t adc_get_calibration_data (enum adc_calibration_data
						   cal)
  {
    uint16_t data;

    switch (cal)
      {
#ifdef ADCA
      case ADC_CAL_ADCA:
	data = nvm_read_production_signature_row (ADCACAL1);
	data <<= 8;
	data |= nvm_read_production_signature_row (ADCACAL0);
	break;
#endif

#ifdef ADCB
      case ADC_CAL_ADCB:
	data = nvm_read_production_signature_row (ADCBCAL1);
	data <<= 8;
	data |= nvm_read_production_signature_row (ADCBCAL0);
	break;
#endif

#if defined(ADCA) || defined(ADCB)
      case ADC_CAL_TEMPSENSE:
	data = nvm_read_production_signature_row (TEMPSENSE1);
	data <<= 8;
	data |= nvm_read_production_signature_row (TEMPSENSE0);
	break;
#endif

      default:
	Assert (0);
	data = 0;
      }

    return data;
  }

/** @} */

/** \name ADC module configuration */
/** @{ */

  void adc_write_configuration (ADC_t * adc, const struct adc_config *conf);
  void adc_read_configuration (ADC_t * adc, struct adc_config *conf);

/**
 * \brief Set ADC prescaler to get desired clock rate
 *
 * Sets the ADC prescaling so that its clock rate becomes _at most_
 * \a clk_adc_hz. This is done by computing the ratio of the peripheral clock
 * rate to the desired ADC clock rate, and rounding it upward to the nearest
 * prescaling factor.
 *
 * \param conf Pointer to ADC module configuration.
 * \param clk_adc Desired ADC clock rate.
 *
 * \note The sample rate is not determined solely by the ADC clock rate for all
 * devices. Setting the current limit mode on some devices will also affect the
 * maximum ADC sampling rate. Refer to the device manual for detailed
 * information on conversion timing and/or the current limitation mode.
 */
  static inline void adc_set_clock_rate (struct adc_config *conf,
					 uint32_t clk_adc)
  {
    uint32_t clk_per;
    uint16_t ratio;
    uint8_t psc;

    Assert (clk_adc);
#if XMEGA_A || XMEGA_AU
    Assert (clk_adc <= 2000000UL);
#elif XMEGA_D
    Assert (clk_adc <= 1400000UL);
#elif XMEGA_B || XMEGA_C || XMEGA_E
    Assert (clk_adc <= 1800000UL);
#endif

    clk_per = sysclk_get_per_hz ();
    ratio = clk_per / clk_adc;

    /* Round ratio up to the nearest prescaling factor. */
    if (ratio <= 4)
      {
	psc = ADC_PRESCALER_DIV4_gc;
      }
    else if (ratio <= 8)
      {
	psc = ADC_PRESCALER_DIV8_gc;
      }
    else if (ratio <= 16)
      {
	psc = ADC_PRESCALER_DIV16_gc;
      }
    else if (ratio <= 32)
      {
	psc = ADC_PRESCALER_DIV32_gc;
      }
    else if (ratio <= 64)
      {
	psc = ADC_PRESCALER_DIV64_gc;
      }
    else if (ratio <= 128)
      {
	psc = ADC_PRESCALER_DIV128_gc;
      }
    else if (ratio <= 256)
      {
	psc = ADC_PRESCALER_DIV256_gc;
      }
    else
      {
	psc = ADC_PRESCALER_DIV512_gc;
      }

    conf->prescaler = psc;
  }

/**
 * \brief Set ADC conversion parameters
 *
 * Sets the signedness, resolution and voltage reference for conversions in the
 * ADC module configuration.
 *
 * \param conf Pointer to ADC module configuration.
 * \param sign Conversion signedness.
 * \param res Resolution of conversions.
 * \param ref Voltage reference to use.
 */
  static inline void adc_set_conversion_parameters (struct adc_config *conf,
						    enum adc_sign sign,
						    enum adc_resolution res,
						    enum adc_reference ref)
  {
    /* Preserve all but conversion and resolution config. */
    conf->ctrlb &= ~(ADC_CONMODE_bm | ADC_RESOLUTION_gm);
    conf->ctrlb |= (uint8_t) res | (uint8_t) sign;

    conf->refctrl &= ~ADC_REFSEL_gm;
    conf->refctrl |= ref;
  }

/**
 * \brief Set ADC conversion trigger
 *
 * Configures the conversion triggering of the ADC.
 *
 * For automatic triggering modes, the number of channels to start conversions
 * on must be specified with \a nr_of_ch. The channel selection for these
 * modes is incrementally inclusive, always starting with channel 0.
 *
 * For event triggered modes, the base event channel must also be specified with
 * \a base_ev_ch. The event channels are assigned to the ADC channels in an
 * incremental fashion \a without \a wrap-around (in single-trigger event mode).
 * This means that the maximum base event channel that can be used is determined
 * by the number of ADC channels to start conversions on, i.e., \a nr_of_ch.
 *
 * \param conf Pointer to ADC module configuration.
 * \param trig Conversion trigger to set.
 * \param nr_of_ch Number of ADC channels to trigger conversions on:
 * \arg \c 1 - \c ADC_NR_OF_CHANNELS (must be non-zero).
 * \param base_ev_ch Base event channel, if used.
 */
  static inline void adc_set_conversion_trigger (struct adc_config *conf,
						 enum adc_trigger trig,
						 uint8_t nr_of_ch,
						 uint8_t base_ev_ch)
  {
    Assert (nr_of_ch);
    Assert (nr_of_ch <= ADC_NR_OF_CHANNELS);
#if XMEGA_A || XMEGA_AU || XMEGA_E
    Assert (base_ev_ch <= 7);
#elif XMEGA_B || XMEGA_C || XMEGA_D
    Assert (base_ev_ch <= 3);
#endif

    switch (trig)
      {
      case ADC_TRIG_MANUAL:
	conf->ctrlb &= ~ADC_FREERUN_bm;
	conf->evctrl = ADC_EVACT_NONE_gc;
	break;

      case ADC_TRIG_EVENT_SINGLE:
	conf->ctrlb &= ~ADC_FREERUN_bm;
	conf->evctrl = (base_ev_ch << ADC_EVSEL_gp) |
	  (nr_of_ch << ADC_EVACT_gp);
	break;

      case ADC_TRIG_FREERUN:
	conf->ctrlb |= ADC_FREERUN_bm;
	break;

#if ADC_NR_OF_CHANNELS > 1
      case ADC_TRIG_FREERUN_SWEEP:
	conf->ctrlb |= ADC_FREERUN_bm;
	conf->evctrl = (nr_of_ch - 1) << ADC_SWEEP_gp;
	break;

      case ADC_TRIG_EVENT_SWEEP:
	conf->ctrlb &= ~ADC_FREERUN_bm;
	conf->evctrl = (nr_of_ch - 1) << ADC_SWEEP_gp |
	  (base_ev_ch << ADC_EVSEL_gp) | ADC_EVACT_SWEEP_gc;
	break;
#endif

      case ADC_TRIG_EVENT_SYNCSWEEP:
	conf->ctrlb &= ~ADC_FREERUN_bm;
	conf->evctrl =
#if ADC_NR_OF_CHANNELS > 1
	  ((nr_of_ch - 1) << ADC_SWEEP_gp) |
#endif
	  (base_ev_ch << ADC_EVSEL_gp) | ADC_EVACT_SYNCSWEEP_gc;
	break;

      default:
	Assert (0);
      }
  }

#if ADC_NR_OF_CHANNELS > 1

/**
 * \brief Set DMA request group
 *
 * Configures the DMA group request for the specified number of ADC channels.
 * The channel group selection is incrementally inclusive, always starting with
 * channel 0.
 *
 * \param conf Pointer to ADC module configuration.
 * \param nr_of_ch Number of channels for group request:
 * \arg 0 to disable.
 * \arg 2, 3 or 4 to enable.
 *
 * \note The number of channels in the DMA request group cannot be 1.
 * \note Not all device families feature this setting.
 */
  static inline void adc_set_dma_request_group (struct adc_config *conf,
						uint8_t nr_of_ch)
  {
    Assert (nr_of_ch <= ADC_NR_OF_CHANNELS);
    Assert (nr_of_ch != 1);

    if (nr_of_ch)
      {
	conf->ctrla = (nr_of_ch - 1) << ADC_DMASEL_gp;
      }
    else
      {
	conf->ctrla = ADC_DMASEL_OFF_gc;
      }
  }

#endif

/**
 * \brief Enable internal ADC input
 *
 * \param conf Pointer to ADC module configuration.
 * \param int_inp Internal input to enable:
 * \arg \c ADC_INT_TEMPSENSE for temperature sensor.
 * \arg \c ADC_INT_BANDGAP for bandgap reference.
 */
  static inline void adc_enable_internal_input (struct adc_config *conf,
						uint8_t int_inp)
  {
    conf->refctrl |= int_inp;
  }

/**
 * \brief Disable internal ADC input
 *
 * \param conf Pointer to ADC module configuration.
 * \param int_inp Internal input to disable:
 * \arg \c ADC_INT_TEMPSENSE for temperature sensor.
 * \arg \c ADC_INT_BANDGAP for bandgap reference.
 */
  static inline void adc_disable_internal_input (struct adc_config *conf,
						 uint8_t int_inp)
  {
    conf->refctrl &= ~int_inp;
  }

#if XMEGA_AU || defined(__DOXYGEN__)
/** \brief ADC gain stage impedance settings */
  enum adc_gainstage_impmode
  {
	/** High impedance sources */
    ADC_GAIN_HIGHIMPEDANCE,
	/** Low impedance sources */
    ADC_GAIN_LOWIMPEDANCE,
  };

/**
 * \brief Set ADC gain stage impedance mode
 *
 * \param conf Pointer to ADC module configuration.
 * \param impmode Gain stage impedance mode.
 *
 * \note Not all device families feature this setting.
 */
  static inline void adc_set_gain_impedance_mode (struct adc_config *conf,
						  enum adc_gainstage_impmode
						  impmode)
  {
    switch (impmode)
      {
      case ADC_GAIN_HIGHIMPEDANCE:
	conf->ctrlb &= ~ADC_IMPMODE_bm;
	break;

      case ADC_GAIN_LOWIMPEDANCE:
	conf->ctrlb |= ADC_IMPMODE_bm;
	break;

      default:
	Assert (0);
      }
  }

#endif

#if !XMEGA_A
/** \brief ADC current limit settings */
  enum adc_current_limit
  {
	/** No current limit */
    ADC_CURRENT_LIMIT_NO,
	/** Low current limit, max sampling rate 1.5 MSPS */
    ADC_CURRENT_LIMIT_LOW,
	/** Medium current limit, max sampling rate 1 MSPS */
    ADC_CURRENT_LIMIT_MED,
	/** High current limit, max sampling rate 0.5 MSPS */
    ADC_CURRENT_LIMIT_HIGH
  };

/**
 * \brief Set ADC current limit
 *
 * Set the current limit mode for the ADC module. This setting affects the max
 * sampling rate of the ADC.
 *
 * \note See the device datasheet and manual for detailed information about
 * current consumption and sample rate limit.
 *
 * \param conf Pointer to ADC module configuration.
 * \param currlimit Current limit setting.
 *
 * \note Not all device families feature this setting.
 */
  static inline void adc_set_current_limit (struct adc_config *conf,
					    enum adc_current_limit currlimit)
  {
    conf->ctrlb &= ~ADC_CURRLIMIT_gm;

    switch (currlimit)
      {
      case ADC_CURRENT_LIMIT_NO:
	conf->ctrlb |= ADC_CURRLIMIT_NO_gc;
	break;

      case ADC_CURRENT_LIMIT_LOW:
	conf->ctrlb |= ADC_CURRLIMIT_LOW_gc;
	break;

      case ADC_CURRENT_LIMIT_MED:
	conf->ctrlb |= ADC_CURRLIMIT_MED_gc;
	break;

      case ADC_CURRENT_LIMIT_HIGH:
	conf->ctrlb |= ADC_CURRLIMIT_HIGH_gc;
	break;

      default:
	Assert (0);
      }
  }

#endif

/**
 * \brief Set ADC compare value in configuration
 *
 * \param conf Pointer to ADC module configuration.
 * \param val Compare value to set.
 */
#define adc_set_config_compare_value(conf, val)	\
	do { \
		conf->cmp = (uint16_t)val; \
	} \
	while (0)

/**
 * \brief Get ADC compare value from configuration
 *
 * \param conf Pointer to ADC module configuration.
 */
#define adc_get_config_compare_value(conf)    (conf->cmp)

#if XMEGA_E

/**
 * \brief Set ADC sample time value in configuration
 *
 * \param conf Pointer to ADC module configuration.
 * \param val Sample time value to set.
 */
#define adc_set_config_sample_value(conf, val) \
	do { \
		conf->sampctrl = (uint8_t)val; \
	} \
	while (0)

/**
 * \brief Get ADC sample time value from configuration
 *
 * \param conf Pointer to ADC module configuration.
 */
#define adc_get_config_sample_value(conf)    (conf->sampctrl)
#endif

/** @} */

/** @} */

/**
 * \defgroup adc_channel_group ADC channel
 *
 * Management and configuration functions for the individual ADC channels.
 *
 * The API functions and definitions can be divided in two groups:
 * - channel management: direct access for getting conversion result.
 * - channel configuration: create/change configurations and write/read them
 * to/from ADC channels.
 *
 * @{
 */

/**
 * \brief Default ADC channel interrupt level
 *
 * \note To override the channel interrupt level, define this symbol as the
 * desired level in \ref conf_adc.h.
 */
#if !defined(CONFIG_ADC_INTLVL) || defined(__DOXYGEN__)
#  define CONFIG_ADC_INTLVL    ADC_CH_INTLVL_LO_gc
#endif

/** ADC channel configuration */
  struct adc_channel_config
  {
    uint8_t ctrl;
    uint8_t muxctrl;
    uint8_t intctrl;
    uint8_t scan;
#if XMEGA_E
    uint8_t corrctrl;
    uint8_t offsetcorr0;
    uint8_t offsetcorr1;
    uint8_t gaincorr0;
    uint8_t gaincorr1;
    uint8_t avgctrl;
#endif
  };

/**
 * \brief ADC channel positive input
 *
 * Identifies the external and internal signals that can be used as positive
 * input to the ADC channels.
 */
  enum adcch_positive_input
  {
    ADCCH_POS_PIN0,
    ADCCH_POS_PIN1,
    ADCCH_POS_PIN2,
    ADCCH_POS_PIN3,
    ADCCH_POS_PIN4,
    ADCCH_POS_PIN5,
    ADCCH_POS_PIN6,
    ADCCH_POS_PIN7,
    ADCCH_POS_PIN8,
    ADCCH_POS_PIN9,
    ADCCH_POS_PIN10,
    ADCCH_POS_PIN11,
    ADCCH_POS_PIN12,
    ADCCH_POS_PIN13,
    ADCCH_POS_PIN14,
    ADCCH_POS_PIN15,

	/** \name Internal inputs. */
	/** @{ */
    ADCCH_POS_TEMPSENSE,	 /**< Temperature sensor. */
    ADCCH_POS_BANDGAP,		 /**< Bandgap reference. */
    ADCCH_POS_SCALED_VCC,	 /**< VCC scaled down by 10. */
#if XMEGA_A || XMEGA_AU || XMEGA_E || defined(__DOXYGEN__)
    ADCCH_POS_DAC,		 /**< DAC output. */
#endif
	/** @} */
  };

/**
 * \brief ADC channel negative input
 *
 * Identifies the signals that can be used as negative input to the ADC channels
 * in differential mode. Some of the input signals are only available with
 * certain gain settings, e.g., 1x gain.
 *
 * \note The ADC must be set in signed mode to use differential measurements.
 * For single-ended measurements, ADDCH_NEG_NONE should be specified as negative
 * input.
 *
 * \note Pad and internal GND are not available on all devices. See the device
 * manual for an overview of available input signals.
 */
  enum adcch_negative_input
  {
	/** \name Input pins for differential measurements with 1x gain. */
	/** @{ */
	/** ADC0 pin */
    ADCCH_NEG_PIN0,
	/** ADC1 pin */
    ADCCH_NEG_PIN1,
	/** ADC2 pin */
    ADCCH_NEG_PIN2,
	/** ADC3 pin */
    ADCCH_NEG_PIN3,
	/** @} */

	/** \name Input pins for differential measurements with any gain. */
	/** @{ */
	/** ADC4 pin */
    ADCCH_NEG_PIN4,
	/** ADC5 pin */
    ADCCH_NEG_PIN5,
	/** ADC6 pin */
    ADCCH_NEG_PIN6,
	/** ADC7 pin */
    ADCCH_NEG_PIN7,
	/** @} */

	/** \name GND signals for differential measurements. */
	/** @{ */
	/** PAD ground */
    ADCCH_NEG_PAD_GND,
	/** Internal ground */
    ADCCH_NEG_INTERNAL_GND,
	/** @} */

	/** Single ended mode */
    ADCCH_NEG_NONE,
  };

/** \brief ADC channel interrupt modes */
  enum adcch_mode
  {
	/** Set interrupt flag when conversions complete. */
    ADCCH_MODE_COMPLETE = ADC_CH_INTMODE_COMPLETE_gc,
	/** Set interrupt flag when conversion result is below compare value. */
    ADCCH_MODE_BELOW = ADC_CH_INTMODE_BELOW_gc,
	/** Set interrupt flag when conversion result is above compare value. */
    ADCCH_MODE_ABOVE = ADC_CH_INTMODE_ABOVE_gc,
  };

/** \name ADC channel configuration */
/** @{ */

  void adcch_write_configuration (ADC_t * adc, uint8_t ch_mask,
				  const struct adc_channel_config *ch_conf);
  void adcch_read_configuration (ADC_t * adc, uint8_t ch_mask,
				 struct adc_channel_config *ch_conf);

/** Force enabling of gainstage with unity gain. */
#define ADCCH_FORCE_1X_GAINSTAGE 0xff

/**
 * \internal
 * \brief Get ADC channel setting for specified gain
 *
 * Returns the setting that corresponds to specified gain.
 *
 * \param gain Valid gain factor for the measurement.
 *
 * \return Gain setting of type ADC_CH_GAIN_t.
 */
  static inline uint8_t adcch_get_gain_setting (uint8_t gain)
  {
    switch (gain)
      {
      case 0:
	return ADC_CH_GAIN_DIV2_gc;

      case 1:
	return ADC_CH_GAIN_1X_gc;

      case 2:
	return ADC_CH_GAIN_2X_gc;

      case 4:
	return ADC_CH_GAIN_4X_gc;

      case 8:
	return ADC_CH_GAIN_8X_gc;

      case 16:
	return ADC_CH_GAIN_16X_gc;

      case 32:
	return ADC_CH_GAIN_32X_gc;

      case 64:
	return ADC_CH_GAIN_64X_gc;

      case ADCCH_FORCE_1X_GAINSTAGE:
	return ADC_CH_GAIN_1X_gc;

      default:
	Assert (0);
	return 0;
      }
  }

/**
 * \brief Set ADC channel input mode, multiplexing and gain
 *
 * Sets up an ADC channel's input mode and multiplexing according to specified
 * input signals, as well as the gain.
 *
 * \param ch_conf Pointer to ADC channel configuration.
 * \param pos Positive input signal.
 * \param neg Negative input signal:
 * \arg \c ADCCH_NEG_NONE for single-ended measurements.
 * \arg \c ADCCH_NEG_PINn , where \c n specifies a pin, for differential
 * measurements.
 * \arg \c ADDCH_x_GND , where \c x specified pad or internal GND, for
 * differential measurements.
 * \param gain Gain factor for measurements:
 * \arg 1 for single-ended or differential with pin 0, 1, 2 or 3, pad or
 * internal GND as negative
 * input.
 * \arg 0 (0.5x), 1, 2, 4, 8, 16, 32 or 64 for differential with pin 4, 5, 6 or
 * 7, pad or internal GND as negative input.
 * \arg ADCCH_FORCE_1X_GAINSTAGE to force the gain stage to be enabled with
 * unity gain for differential measurement.
 *
 * \note The GND signals are not available on all devices. Refer to the device
 * manual for information on available input signals.
 *
 * \note With unity (1x) gain, some input selections may be possible both with
 * and without the gain stage enabled. The driver will default to the
 * configuration without gainstage to keep the current consumption as low as
 * possible unless the user specifies \ref ADCCH_FORCE_1X_GAINSTAGE as \a gain.
 */
  static inline void adcch_set_input (struct adc_channel_config *ch_conf,
				      enum adcch_positive_input pos,
				      enum adcch_negative_input neg,
				      uint8_t gain)
  {
    if (pos >= ADCCH_POS_TEMPSENSE)
      {
	/* Configure for internal input. */
	Assert (gain == 1);
	Assert (neg == ADCCH_NEG_NONE);

	ch_conf->ctrl = ADC_CH_INPUTMODE_INTERNAL_gc;
	ch_conf->muxctrl = (pos - ADCCH_POS_TEMPSENSE) << ADC_CH_MUXPOS_gp;
      }
    else if (neg == ADCCH_NEG_NONE)
      {
	/* Configure for single-ended measurement. */
	Assert (gain == 1);

	ch_conf->ctrl = ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ch_conf->muxctrl = pos << ADC_CH_MUXPOS_gp;
      }
    else if (neg <= ADCCH_NEG_PIN3)
      {
	/* Configure for differential measurement.
	 * Pins 0-3 can only be used for negative input if the gain
	 * stage is not used, i.e., unity gain (except XMEGA E).
	 */
#if XMEGA_E
	ch_conf->ctrl = adcch_get_gain_setting (gain) |
	  ADC_CH_INPUTMODE_DIFFWGAINL_gc;
#else
	Assert (gain == 1);
	ch_conf->ctrl = ADC_CH_INPUTMODE_DIFF_gc;
#endif
	ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
	  (neg << ADC_CH_MUXNEG_gp);
      }
    else if (neg <= ADCCH_NEG_PIN7)
      {
	/* Configure for differential measurement.
	 * Pins 4-7 can be used for all gain settings,
	 * including unity gain, which is available even if
	 * the gain stage is active.
	 */
#if XMEGA_E
	ch_conf->ctrl = adcch_get_gain_setting (gain) |
	  ADC_CH_INPUTMODE_DIFFWGAINH_gc;
#else
	ch_conf->ctrl = adcch_get_gain_setting (gain) |
	  ADC_CH_INPUTMODE_DIFFWGAIN_gc;
#endif
	ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
	  ((neg - ADCCH_NEG_PIN4) << ADC_CH_MUXNEG_gp);
      }
    else
      {
	Assert ((neg == ADCCH_NEG_PAD_GND) ||
		(neg == ADCCH_NEG_INTERNAL_GND));
#if XMEGA_E

	/* Configure for differential measurement through PAD GND or
	 * internal GND.
	 * DIFFWGAINH (INPUTMODE) is not used because it support
	 * only PAD GND.
	 */
	ch_conf->ctrl = ADC_CH_INPUTMODE_DIFFWGAINL_gc |
	  adcch_get_gain_setting (gain);
	ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
	  ((neg == ADCCH_NEG_INTERNAL_GND) ?
	   ADC_CH_MUXNEGL_INTGND_gc : ADC_CH_MUXNEGL_GND_gc);
#else

	/* Configure for differential measurement through GND or
	 * internal GND.
	 * The bitmasks for the on-chip GND signals change when
	 * gain is enabled. To avoid unnecessary current consumption,
	 * do not enable gainstage for unity gain unless user explicitly
	 * specifies it with the ADCCH_FORCE_1X_GAINSTAGE macro.
	 */
	if (gain == 1)
	  {
	    ch_conf->ctrl = ADC_CH_INPUTMODE_DIFF_gc;
	    ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
	      ((neg == ADCCH_NEG_PAD_GND) ?
	       ADC_CH_MUXNEG_MODE10_GND_gc : ADC_CH_MUXNEG_MODE10_INTGND_gc);
	  }
	else
	  {
	    ch_conf->ctrl = ADC_CH_INPUTMODE_DIFFWGAIN_gc |
	      adcch_get_gain_setting (gain);
	    ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
	      ((neg == ADCCH_NEG_INTERNAL_GND) ?
	       ADC_CH_MUXNEG_MODE11_INTGND_gc : ADC_CH_MUXNEG_MODE11_GND_gc);
	  }

#endif
      }
  }

/**
 * \brief Set ADC channel 0 pin scan
 *
 * Sets the parameters for pin scan, which enables measurements on multiple,
 * successive input pins without any reconfiguration between conversions.
 *
 * Pin scan works by adding a offset to the positive MUX setting to get the
 * current input pin. The offset is incremented for each conversion, and is
 * reset to 0 once a conversion with the maximum offset is done.
 *
 * \param ch_conf Pointer to the ADC channel configuration structure
 * \param start_offset Initial offset to start pin scan at
 * \arg \c 0 - \c max_offset
 * \param max_offset Maximum offset for the pin scan
 * \arg \c 0 to disable
 * \arg \c 1 - \c 15 to enable
 *
 * \note Only the AVR XMEGA AU family features this setting.
 * \note Pin scan is only available on ADC channel 0.
 */
  static inline void adcch_set_pin_scan (struct adc_channel_config *ch_conf,
					 uint8_t start_offset,
					 uint8_t max_offset)
  {
    Assert (start_offset < 16);
    Assert (max_offset < 16);
    Assert (start_offset <= max_offset);

    ch_conf->scan = max_offset | (start_offset << ADC_CH_OFFSET_gp);
  }

/**
 * \brief Set ADC channel interrupt mode
 *
 * \param ch_conf Pointer to ADC channel configuration.
 * \param mode Interrupt mode to set.
 */
  static inline void adcch_set_interrupt_mode (struct adc_channel_config
					       *ch_conf, enum adcch_mode mode)
  {
    ch_conf->intctrl &= ~ADC_CH_INTMODE_gm;
    ch_conf->intctrl |= mode;
  }

/**
 * \brief Enable interrupts on ADC channel
 *
 * \param ch_conf Pointer to ADC channel configuration.
 */
  static inline void adcch_enable_interrupt (struct adc_channel_config
					     *ch_conf)
  {
    ch_conf->intctrl &= ~ADC_CH_INTLVL_gm;
    ch_conf->intctrl |= CONFIG_ADC_INTLVL;
  }

/**
 * \brief Disable interrupts on ADC channel
 *
 * \param ch_conf Pointer to ADC channel configuration.
 */
  static inline void adcch_disable_interrupt (struct adc_channel_config
					      *ch_conf)
  {
    ch_conf->intctrl &= ~ADC_CH_INTLVL_gm;
    ch_conf->intctrl |= ADC_CH_INTLVL_OFF_gc;
  }

#if XMEGA_E

/**
 * \brief Enable gain & offset corrections on ADC channel
 *
 * \param ch_conf         Pointer to ADC channel configuration.
 * \param offset_corr     Offset correction value to set.
 * \param expected_value  Expected value for a specific input voltage
 * \param captured_value  Captured value for a specific input voltage
 *
 * \Note
 * Gived "expected_value = captured_value = 1" to ignore the gain correction
 * Gain correction is equal to "expected_value / captured_value"
 */
  static inline void adcch_enable_correction (struct adc_channel_config
					      *ch_conf, uint16_t offset_corr,
					      uint16_t expected_value,
					      uint16_t captured_value)
  {
    uint32_t gain_corr;

    gain_corr = (2048L * expected_value) / captured_value;
    ch_conf->offsetcorr0 = LSB (offset_corr);
    ch_conf->offsetcorr1 = MSB (offset_corr);
    ch_conf->gaincorr0 = LSB (gain_corr);
    ch_conf->gaincorr1 = MSB (gain_corr);
    ch_conf->corrctrl = ADC_CH_CORREN_bm;
  }

/**
 * \brief Disable gain & offset correction on ADC channel
 *
 * \param ch_conf Pointer to ADC channel configuration.
 */
  static inline void adcch_disable_correction (struct adc_channel_config
					       *ch_conf)
  {
    ch_conf->corrctrl = ADC_CH_CORREN_bp;
  }

/** \brief ADC channel sample number settings */
  enum adcch_sampnum
  {
	/** 2 samples to accumulate. */
    ADC_SAMPNUM_2X = ADC_SAMPNUM_2X_gc,
	/** 4 samples to accumulate. */
    ADC_SAMPNUM_4X = ADC_SAMPNUM_4X_gc,
	/** 8 samples to accumulate. */
    ADC_SAMPNUM_8X = ADC_SAMPNUM_8X_gc,
	/** 16 samples to accumulate. */
    ADC_SAMPNUM_16X = ADC_SAMPNUM_16X_gc,
	/** 32 samples to accumulate. */
    ADC_SAMPNUM_32X = ADC_SAMPNUM_32X_gc,
	/** 64 samples to accumulate. */
    ADC_SAMPNUM_64X = ADC_SAMPNUM_64X_gc,
	/** 128 samples to accumulate. */
    ADC_SAMPNUM_128X = ADC_SAMPNUM_128X_gc,
	/** 256 samples to accumulate. */
    ADC_SAMPNUM_256X = ADC_SAMPNUM_256X_gc,
	/** 512 samples to accumulate. */
    ADC_SAMPNUM_512X = ADC_SAMPNUM_512X_gc,
	/** 1024 samples to accumulate. */
    ADC_SAMPNUM_1024X = ADC_SAMPNUM_1024X_gc,
  };

/**
 * \brief Enables ADC channel averaging
 *
 * Sets the parameters number of samples used during averaging.
 *
 * \param ch_conf  Pointer to the ADC channel configuration structure
 * \param sample   Number of samples to accumulate
 *
 * \note Only the AVR XMEGA E family features this setting.
 * \note Check that "ADC_RES_MT12" param is used
 * in adc_set_conversion_parameters() call.
 */
  static inline void adcch_enable_averaging (struct adc_channel_config
					     *ch_conf,
					     enum adcch_sampnum sample)
  {
    uint8_t rshift;

    Assert (sample >= ADC_SAMPNUM_2X);

    if (sample >= ADC_SAMPNUM_16X)
      {
	rshift = 4;
      }
    else if (sample == ADC_SAMPNUM_8X)
      {
	rshift = 3;
      }
    else if (sample == ADC_SAMPNUM_4X)
      {
	rshift = 2;
      }
    else
      {
	rshift = 1;
      }

    ch_conf->avgctrl = sample | (rshift << ADC_CH_RIGHTSHIFT_gp);
  }

/**
 * \brief Disables ADC channel averaging
 *
 * \param ch_conf    Pointer to the ADC channel configuration structure
 *
 * \note Only the AVR XMEGA E family features this setting.
 * \note Check that "ADC_RES_MT12" param is not used
 * in adc_set_conversion_parameters() call.
 */
  static inline void adcch_disable_averaging (struct adc_channel_config
					      *ch_conf)
  {
    ch_conf->avgctrl = 0;
  }

/**
 * \brief Enables ADC channel over-sampling
 *
 * Sets the parameters number of samples and result resolution
 * used during over-sampling.
 *
 * \param ch_conf    Pointer to the ADC channel configuration structure
 * \param sample     Number of samples to accumulate
 * \param resolution result resolution (12 bits to 16 bits)
 *                   15 bits maximum if sample = 8
 *                   14 bits maximum if sample = 4
 *                   13 bits maximum if sample = 2
 *
 * \note Only the AVR XMEGA E family features this setting.
 * \note Check that "ADC_RES_MT12" param is used
 * in adc_set_conversion_parameters() call.
 */
  static inline void adcch_enable_oversampling (struct adc_channel_config
						*ch_conf,
						enum adcch_sampnum sample,
						uint8_t resolution)
  {
    uint8_t rshift;

    Assert ((resolution >= 12) && (resolution <= 16));

    if (sample >= ADC_SAMPNUM_16X)
      {
	rshift = 4;
      }
    else if (sample == ADC_SAMPNUM_8X)
      {
	rshift = 3;
      }
    else if (sample == ADC_SAMPNUM_4X)
      {
	rshift = 2;
      }
    else
      {
	rshift = 1;
      }

    Assert (rshift >= resolution - 12);
    rshift -= resolution - 12;
    ch_conf->avgctrl = sample | (rshift << ADC_CH_RIGHTSHIFT_gp);
  }

/**
 * \brief Disables ADC channel over-sampling
 *
 * \param ch_conf    Pointer to the ADC channel configuration structure
 *
 * \note Only the AVR XMEGA E family features this setting.
 * \note Check that "ADC_RES_MT12" param is not used
 * in adc_set_conversion_parameters() call.
 */
  static inline void adcch_disable_oversampling (struct adc_channel_config
						 *ch_conf)
  {
    ch_conf->avgctrl = 0;
  }

#endif

/** @} */

/** @} */

/** @} */

#ifdef __cplusplus
}
#endif

/**
 * \page adc_quickstart Quick start guide for XMEGA ADC
 *
 * This is the quick start guide for the \ref adc_group, with step-by-step
 * instructions on how to configure and use the driver in a selection of use
 * cases.
 *
 * The use cases are described with "setup" and "usage" sections, which each
 * have "example code" and "workflow" subsections. This documentation first
 * presents code fragments and function definitions along with instructions on
 * where they can be placed, e.g., into the application C-file or the main()
 * function, then follows up with explanations for all the lines of code.
 *
 * \section adc_use_cases Use cases
 *
 * In addition to the basic use case below, refer to the following use cases for
 * demonstrations of the ADC's features:
 * - \subpage adc_use_case_1
 * - \subpage adc_use_case_2
 *
 * We recommend reading all the use cases for the sake of all the notes on
 * considerations, limitations and other helpful details.
 *
 * \section adc_basic_use_case Basic use case
 *
 * In this basic use case, ADCA is configured for:
 * - sampling on a single channel (0)
 *    - I/O pin as single-ended input (PA0)
 * - unsigned conversions
 * - 12-bit resolution
 * - internal 1V reference
 * - manual conversion triggering
 * - polled operation (no interrupts)
 *
 * Completed conversions are detected by waiting for the relevant interrupt flag
 * to get set. The ADC result is then stored in a local variable.
 *
 * \section adc_basic_use_case_setup Setup steps
 *
 * \subsection adc_basic_use_case_setup_code Example code
 *
 * Add to application C-file:
 * \code
 * #define MY_ADC    ADCA
 * #define MY_ADC_CH ADC_CH0
 *
 * static void adc_init(void)
 * {
 *     struct adc_config adc_conf;
 *     struct adc_channel_config adcch_conf;
 *
 *     adc_read_configuration(&MY_ADC, &adc_conf);
 *     adcch_read_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf);
 *
 *     adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12,
 *             ADC_REF_BANDGAP);
 *     adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0);
 *     adc_set_clock_rate(&adc_conf, 200000UL);
 *
 *     adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1);
 *
 *     adc_write_configuration(&MY_ADC, &adc_conf);
 *     adcch_write_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf);
 * }
 * \endcode
 *
 * Add to \c main():
 * \code
 * sysclk_init();
 * adc_init();
 * \endcode
 *
 * \subsection adc_basic_use_case_setup_flow Workflow
 *
 * -# Add macros for the ADC and its channel to use, so they are easy to change:
 *     - \code
 * #define MY_ADC    ADCA
 * #define MY_ADC_CH ADC_CH0
 *       \endcode
 * -# Create a function \c adc_init() to intialize the ADC:
 *     - \code
 * static void adc_init(void)
 * {
 *     // ...
 * }
 *       \endcode
 * -# Allocate configuration structs for the ADC and its channel:
 *     - \code
 * struct adc_config adc_conf;
 * struct adc_channel_config adcch_conf;
 *       \endcode
 * -# Initialize the structs:
 *     - \code
 * adc_read_configuration(&MY_ADC, &adc_conf);
 * adcch_read_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf);
 *       \endcode
 *     \attention This step must not be skipped because uninitialized structs
 * may contain invalid configurations, thus giving unpredictable behavior.
 * -# Set conversion parameters to unsigned, 12-bit and internal 1V reference:
 *     - \code
 * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12,
 *         ADC_REF_BANDGAP);
 *       \endcode
 *     \note Only single-ended input is possible with unsigned conversions.
 * -# Set conversion trigger to manual triggering:
 *     - \code
 * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0);
 *       \endcode
 *     \note The number of channels to trigger (1) and base event channel (0)
 * don't affect operation in this trigger mode, but sane values should still be
 * supplied.
 * -# Set ADC clock rate to 200 KHz or less:
 *     - \code
 * adc_set_clock_rate(&adc_conf, 200000UL);
 *       \endcode
 *     \note The driver attempts to set the ADC clock rate to the fastest
 * possible without exceeding the specified limit. Refer to the applicable
 * device datasheet and manual for details on maximum ADC clock rate.
 * -# Set pin 0 on the associated port as the single-ended input:
 *     - \code
 * adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1);
 *       \endcode
 *     \note For single-ended input, the negative input must be none and the
 * gain must be unity (1x).
 * -# Write the configurations to ADC and channel:
 *     - \code
 * adc_write_configuration(&MY_ADC, &adc_conf);
 * adcch_write_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf);
 *       \endcode
 * -# Initialize the clock system:
 *      - \code sysclk_init(); \endcode
 *      \note The ADC driver requires the system clock driver to be
 * initialized in order to compute the correct ADC clock rate in step 6.
 * -# Call our ADC init function:
 *      - \code adc_init(); \endcode
 *
 * \section adc_basic_use_case_usage Usage steps
 *
 * \subsection adc_basic_use_case_usage_code Example code
 *
 * Add to, e.g., main-loop in application C-file:
 * \code
 * uint16_t result;
 *
 * adc_enable(&MY_ADC);
 *
 * adc_start_conversion(&MY_ADC, MY_ADC_CH);
 * adc_wait_for_interrupt_flag(&MY_ADC, MY_ADC_CH);
 *
 * result = adc_get_result(&MY_ADC, MY_ADC_CH);
 * \endcode
 *
 * \subsection adc_basic_use_case_usage_flow Workflow
 *
 * -# Allocate a variable to contain the ADC result:
 *     - \code uint16_t result; \endcode
 * -# Enable the configured ADC:
 *     - \code adc_enable(&MY_ADC); \endcode
 * -# Trigger a single conversion on the ADC channel:
 *     - \code adc_start_conversion(&MY_ADC, MY_ADC_CH); \endcode
 * -# Wait for the channel's interrupt flag to get set, indicating a completed
 * conversion:
 *     - \code adc_wait_for_interrupt_flag(&MY_ADC, MY_ADC_CH); \endcode
 *     \note The interrupt flags are set even if the interrupts are disabled.
 * Further, this function will clear the interrupt flag after it has been set,
 * so we do not need to clear it manually.
 * -# Read out the result of the ADC channel:
 *     - \code result = adc_get_result(&MY_ADC, MY_ADC_CH); \endcode
 * -# To do more conversions, go back to step 3.
 */

/**
 * \page adc_use_case_1 Free-running conversions with interrupt
 *
 * In this use case, ADCA is configured for:
 * \li sampling on two channels (0 and 1) with respective inputs:
 *     - I/O pin as single-ended input (PA0)
 *     - two I/O pins as differential input w/ 2x gain (PA1 and PA5)
 * \li signed conversions
 * \li 12-bit resolution
 * \li internal 1V reference
 * \li free-running conversions
 * \li interrupt-based conversion handling
 *
 * The ADC results are handled in an interrupt callback function which simply
 * stores the result in one of two channel-specific, global variables.
 *
 * \note This use case assumes that the device has multiple ADC channels. Refer
 * to the applicable device datasheet for information about the number of ADC
 * channels.
 *
 * \section adc_use_case_1_setup Setup steps
 *
 * \subsection adc_use_case_1_setup_code Example code
 *
 * Ensure that \ref conf_adc.h contains:
 * \code
 * #define CONFIG_ADC_CALLBACK_ENABLE
 * #define CONFIG_ADC_CALLBACK_TYPE int16_t
 * \endcode
 *
 * Add to application C-file:
 * \code
 * #define MY_ADC   ADCA
 *
 * int16_t ch0_result;
 * int16_t ch1_result;
 *
 * static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
 * {
 *     switch (ch_mask) {
 *     case ADC_CH0:
 *         ch0_result = result;
 *         break;
 *
 *     case ADC_CH1:
 *         ch1_result = result;
 *         break;
 *
 *     default:
 *         break;
 *     }
 * }
 *
 * static void adc_init(void)
 * {
 *     struct adc_config adc_conf;
 *     struct adc_channel_config adcch_conf;
 *
 *     adc_read_configuration(&MY_ADC, &adc_conf);
 *     adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *
 *     adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12,
 *             ADC_REF_BANDGAP);
 *     adc_set_conversion_trigger(&adc_conf, ADC_TRIG_FREERUN_SWEEP, 2, 0);
 *     adc_set_clock_rate(&adc_conf, 5000UL);
 *     adc_set_callback(&MY_ADC, &adc_handler);
 *     adc_write_configuration(&MY_ADC, &adc_conf);
 *
 *     adcch_enable_interrupt(&adcch_conf);
 *     adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1);
 *     adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *
 *     adcch_set_input(&adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_PIN5, 2);
 *     adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf);
 * }
 * \endcode
 *
 * Add to \c main():
 * \code
 * sysclk_init();
 * adc_init();
 * pmic_init();
 * \endcode
 *
 * \subsection adc_use_case_1_setup_flow Workflow
 *
 * -# Define a macro for the ADC to use, in case we want to change it later:
 *     - \code #define MY_ADC   ADCA \endcode
 * -# Define global variables to contain the ADC result of each channel:
 *     - \code
 * int16_t ch0_result;
 * int16_t ch1_result;
 *       \endcode
 * -# Create an ADC interrupt callback function that stores the results in the
 * channels' respective global variables:
 *     - \code
 * static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
 * {
 *     switch (ch_mask) {
 *     case ADC_CH0:
 *         ch0_result = result;
 *         break;
 *
 *     case ADC_CH1:
 *         ch1_result = result;
 *         break;
 *
 *     default:
 *         break;
 *     }
 * }
 *       \endcode
 *       \note Refer to \ref adc_callback_t for documentation on the interrupt
 * callback function type.
 * -# Create a function \c adc_init() to intialize the ADC:
 *     - \code
 * static void adc_init(void)
 * {
 *     // ...
 * }
 *       \endcode
 * -# Allocate configuration structs for ADC and channel, then initialize them:
 *     - \code
 * struct adc_config adc_conf;
 * struct adc_channel_config adcch_conf;
 *
 * adc_read_configuration(&MY_ADC, &adc_conf);
 * adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *     \endcode
 * -# Set signed, 12-bit conversions with internal 1V voltage reference:
 *     - \code
 * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12,
 *         ADC_REF_BANDGAP);
 *     \endcode
 *     \note With signed, 12-bit conversion, 1 bit is used to indicate
 * sign/polarity, so the resolution is halved in terms of Volt per LSB.
 * -# Set free-running conversions on the first two ADC channels:
 *     - \code
 * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_FREERUN_SWEEP, 2, 0);
 *       \endcode
 *     \note The base event channel (0) does not affect operation in this
 * mode.
 * -# Set ADC clock rate to maximum 5 KHz:
 *     - \code adc_set_clock_rate(&adc_conf, 5000UL); \endcode
 *     \note In free-running mode, it is wise to reduce the ADC clock so that
 * the device has time to handle the results, e.g., channel 0 does not complete
 * a new conversion before channel 1's result has been handled.
 * -# Set the interrupt callback function to use for the ADC:
 *     - \code adc_set_callback(&MY_ADC, &adc_handler); \endcode
 * -# Write the configuration to the ADC:
 *     - \code adc_write_configuration(&MY_ADC, &adc_conf); \endcode
 * -# Enable interrupts for the ADC channels:
 *     - \code adcch_enable_interrupt(&adcch_conf); \endcode
 * -# Set up single-ended input from pin 0 on port A, then write the config to
 * the first channel (0):
 *     - \code
 * adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1);
 * adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *       \endcode
 * -# Set up differential input from pins 1 and 5 on port A, with 2x gain, then
 * write the config to the second channel (1):
 *     - \code
 * adcch_set_input(&adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_PIN5, 2);
 * adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf);
 *       \endcode
 *     \note Not all input and gain combinations are valid. Refer to
 * \ref adcch_set_input() for documentation on the restrictions.
 * -# Initialize the clock system, the ADC, and the PMIC since we will be using
 * interrupts:
 *     - \code
 * sysclk_init();
 * adc_init();
 * pmic_init();
 *       \endcode
 *     \note The call to \ref pmic_init() does not enable interrupts globally,
 * which must be done explicitly with \ref cpu_irq_enable().
 *
 * \section adc_use_case_1_usage Usage steps
 *
 * \subsection adc_use_case_1_usage_code Example code
 *
 * Add to \c main.c():
 * \code
 * cpu_irq_enable();
 * adc_enable(&MY_ADC);
 *
 * do {
 * } while (true);
 * \endcode
 *
 * \subsection adc_use_case_1_usage_flow Workflow
 * -# Enable interrupts globally to allow the ADC interrupts to be handled:
 *     - \code cpu_irq_enable(); \endcode
 * -# Enable the ADC to start conversions:
 *     - \code adc_enable(&MY_ADC); \endcode
 *     \note When configured for free-running conversions, the ADC will start
 * doing conversions as soon as it is enabled, so we do not need to do it
 * manually.
 * -# Enter a busy-loop while interrupts handle the ADC results:
 *     - \code
 * do {
 * } while (true);
 *       \endcode
 */

/**
 * \page adc_use_case_2 Event-triggered conversions
 *
 * In this use case, ADCA is configured for:
 * \li sampling on two channels (0 and 1) with respective inputs:
 *     - internal temperature sensor
 *     - internal bandgap reference
 * \li unsigned conversions
 * \li 12-bit resolution
 * \li VCC/1.6 as voltage reference
 * \li event-triggered conversions
 * \li polled conversion handling
 *
 * Completed conversions are detected via non-blocking polling of the interrupt
 * flags. The ADC results are stored into local variables as soon as they are
 * available.
 *
 * A Timer/Counter is used to generate events that trigger the conversions.
 *
 * \note This use case assumes that the device has multiple ADC channels. Refer
 * to the applicable device datasheet for information about the number of ADC
 * channels.
 *
 * \section adc_use_case_2_setup Setup steps
 *
 * \subsection adc_use_case_2_setup_prereq Prerequisites
 *
 * This use case requires that the Timer/Counter driver is added to the project.
 *
 * \subsection adc_use_case_2_setup_code Example code
 *
 * Add to application C-file:
 * \code
 * #define MY_ADC    ADCA
 * #define MY_TIMER  TCC0
 *
 * static void evsys_init(void)
 * {
 *     sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS);
 *     EVSYS.CH3MUX = EVSYS_CHMUX_TCC0_OVF_gc;
 * }
 *
 * static void tc_init(void)
 * {
 *     tc_enable(&MY_TIMER);
 *     tc_set_wgm(&MY_TIMER, TC_WG_NORMAL);
 *     tc_write_period(&MY_TIMER, 200);
 *     tc_set_resolution(&MY_TIMER, 2000);
 * }
 *
 * static void adc_init(void)
 * {
 *     struct adc_config adc_conf;
 *     struct adc_channel_config adcch_conf;
 *
 *     adc_read_configuration(&MY_ADC, &adc_conf);
 *     adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *
 *     adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12,
 *             ADC_REF_VCC);
 *     adc_set_conversion_trigger(&adc_conf, ADC_TRIG_EVENT_SWEEP, 2, 3);
 *     adc_enable_internal_input(&adc_conf, ADC_INT_BANDGAP
 *             | ADC_INT_TEMPSENSE);
 *     adc_set_clock_rate(&adc_conf, 200000UL);
 *     adc_write_configuration(&MY_ADC, &adc_conf);
 *
 *     adcch_set_input(&adcch_conf, ADCCH_POS_TEMPSENSE, ADCCH_NEG_NONE, 1);
 *     adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *
 *     adcch_set_input(&adcch_conf, ADCCH_POS_BANDGAP, ADCCH_NEG_NONE, 1);
 *     adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf);
 * }
 * \endcode
 *
 * Add to \c main():
 * \code
 * sysclk_init();
 * evsys_init();
 * tc_init();
 * adc_init();
 * \endcode
 *
 * \subsection adc_use_case_2_setup_flow Workflow
 *
 * -# Add macros for the ADC and the conversion trigger timer to use, so they
 *    are easy to change:
 *     - \code
 * #define MY_ADC    ADCA
 * #define MY_TIMER  TCC0
 *       \endcode
 * -# Create a function \c evsys_init() to intialize the event system clocks and
 *    to link the conversion timer to the correct event channel:
 *     - \code
 * static void evsys_init(void)
 * {
 *     // ...
 * }
 *       \endcode
 * -# Use the sysclk service to enable the clock to the event system:
 *     - \code sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); \endcode
 * -# Connect the TCC0 overflow event to event channel 3:
 *     - \code EVSYS.CH3MUX = EVSYS_CHMUX_TCC0_OVF_gc; \endcode
 *    \note If the ADC trigger timer is changed from TCC0, the \c EVSYS_CHMUX_*
 *          mask here will also need to be altered.
 * -# Create a function \c tc_init() to intialize the ADC trigger timer:
 *     - \code
 * static void tc_init(void)
 * {
 *     // ...
 * }
 *       \endcode
 * -# Enable the clock to the ADC trigger timer:
 *     - \code tc_enable(&MY_TIMER); \endcode
 * -# Configure the ADC trigger timer in normal Waveform Generation mode:
 *     - \code tc_set_wgm(&MY_TIMER, TC_WG_NORMAL); \endcode
 * -# Configure the ADC trigger timer period to overflow at 200 counts:
 *     - \code tc_write_period(&MY_TIMER, 200); \endcode
 * -# Configure the ADC trigger timer resolution (frequency) for 2KHz:
 *     - \code tc_set_resolution(&MY_TIMER, 2000); \endcode
 * -# Create a function \c adc_init() to intialize the ADC ready for
 *    conversions on channels 0 and 1, triggered by the event system:
 *     - \code
 * static void adc_init(void)
 * {
 *     // ...
 * }
 *       \endcode
 * -# Allocate configuration structs for ADC and channel, then initialize them:
 *     - \code
 * struct adc_config adc_conf;
 * struct adc_channel_config adcch_conf;
 *
 * adc_read_configuration(&MY_ADC, &adc_conf);
 * adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *     \endcode
 * -# Set unsigned, 12-bit conversions with internal VCC/1.6 voltage reference:
 *     - \code
 * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12,
 * ADC_REF_VCC);
 *     \endcode
 * -# Set event system triggered conversions on the first two ADC channels,
 *    with conversions triggered by event system channel 3:
 *     - \code
 * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_EVENT_SWEEP, 2, 3);
 *       \endcode
 * \note The event system channel used here must match the channel linked to the
 *       conversion trigger timer set up earlier in \c tc_init().
 * -# Turn on the internal bandgap and temperature sensor ADC inputs:
 *     - \code
 * adc_enable_internal_input(&adc_conf, ADC_INT_BANDGAP | ADC_INT_TEMPSENSE);
 *       \endcode
 * -# Set ADC clock rate to maximum 200 KHz:
 *     - \code adc_set_clock_rate(&adc_conf, 200000UL); \endcode
 * -# Write the configuration to the ADC:
 *     - \code adc_write_configuration(&MY_ADC, &adc_conf); \endcode
 * -# Set up single-ended input from the internal temperature sensor, then write
 *    the config to the first channel (0):
 *     - \code
 * adcch_set_input(&adcch_conf, ADCCH_POS_TEMPSENSE, ADCCH_NEG_NONE, 1);
 * adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
 *       \endcode
 * -# Set up single-ended input from the internal bandgap voltage, then write
 *    the config to the second channel (1):
 *     - \code
 * adcch_set_input(&adcch_conf, ADCCH_POS_BANDGAP, ADCCH_NEG_NONE, 1);
 * adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf);
 *       \endcode
 * -# Initialize the clock system, event system, ADC trigger timer, and the ADC:
 *     - \code
 * sysclk_init();
 * evsys_init();
 * tc_init();
 * adc_init();
 *       \endcode
 *
 * \section adc_use_case_2_usage Usage steps
 *
 * \subsection adc_use_case_2_usage_code Example code
 *
 * Add to \c main():
 * \code
 * adc_enable(&MY_ADC);
 *
 * do {
 *     uint16_t tmp_result;
 *     uint16_t bg_result;
 *
 *     if (adc_get_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1)
 *             == (ADC_CH0 | ADC_CH1)) {
 *         tmp_result = adc_get_result(&MY_ADC, ADC_CH0);
 *         bg_result = adc_get_result(&MY_ADC, ADC_CH1);
 *
 *         adc_clear_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1);
 *     }
 * } while (true);
 * \endcode
 *
 * \subsection adc_use_case_2_usage_flow Workflow
 *
 * -# Enable the configured ADC module, so that it will begin conversions when
 *    triggered:
 *     - \code adc_enable(&MY_ADC); \endcode
 * -# Create an infinite loop so that conversions will be processed forever:
 *     - \code
 * do {
 *    // ...
 * } while (true);
 *       \endcode
 * -# Within the loop, create local variables to contain the ADC result of each
 *    channel (internal temperature sensor and internal bandgap voltage):
 *     - \code
 * int16_t temp_result;
 * int16_t bg_result;
 *       \endcode
 * -# Test if both ADC channel 0 and channel 1 have completed a conversion by
 *    testing the respective channel conversion complete interrupt flags:
 *     - \code
 * if (adc_get_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1)
 *         == (ADC_CH0 | ADC_CH1)) {
 *       \endcode
 * -# Store the channel result values into the local variables created earlier:
 *     - \code
 *      tmp_result = adc_get_result(&MY_ADC, ADC_CH0);
 *      bg_result = adc_get_result(&MY_ADC, ADC_CH1);
 *       \endcode
 * -# Clear both ADC channel conversion complete interrupt flags, so that we can
 *    detect future conversions at a later stage:
 *     - \code adc_clear_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1); \endcode
 */

#endif /* ADC_H */
