/** * \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 #include #include #include #include #include #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 */