/** * \file * * \brief AVR XMEGA Analog to Digital Converter driver * * Copyright (C) 2010-2012 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 * */ #include #include /** * \ingroup adc_module_group * @{ */ /** \name ADC interrupt callback function */ /** @{ */ #ifdef ADCA /** * \internal * \brief ADC A enable counter * * This is used to ensure that ADC A is not inadvertently disabled when its * module or channel configurations are updated. */ static uint8_t adca_enable_count; # ifdef CONFIG_ADC_CALLBACK_ENABLE /** * \internal * \brief ADC A interrupt callback function pointer */ adc_callback_t adca_callback; # endif #endif #ifdef ADCB /** * \internal * \brief ADC B enable counter * * This is used to ensure that ADC B is not inadvertently disabled when its * module or channel configurations are updated. */ static uint8_t adcb_enable_count; # ifdef CONFIG_ADC_CALLBACK_ENABLE /** * \internal * \brief ADC B interrupt callback function pointer */ adc_callback_t adcb_callback; # endif #endif #if defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__) /** * \brief Set ADC interrupt callback function * * Sets a new callback function for interrupts on the specified ADC. * * \param adc Pointer to ADC module. * \param callback Pointer to the callback function to set. */ void adc_set_callback(ADC_t * adc, adc_callback_t callback) { irqflags_t flags; Assert(callback); flags = cpu_irq_save(); #ifdef ADCA if ((uintptr_t) adc == (uintptr_t) & ADCA) { adca_callback = callback; } else #endif #ifdef ADCB if ((uintptr_t) adc == (uintptr_t) & ADCB) { adcb_callback = callback; } else #endif { Assert(0); } cpu_irq_restore(flags); } #endif /* CONFIG_ADC_CALLBACK_ENABLE */ /** @} */ /** \name Internal functions for driver */ /** @{ */ /** * \internal * \brief Enable peripheral clock for ADC * * Checks if the enable count for the ADC is zero, then increments it. If the * count was zero, the peripheral clock is enabled. Otherwise, it is already * enabled. * * \param adc Pointer to ADC module. */ void adc_enable_clock(ADC_t * adc); void adc_enable_clock(ADC_t * adc) { #ifdef ADCA if ((uintptr_t) adc == (uintptr_t) (&ADCA)) { Assert(adca_enable_count < 0xff); if (!adca_enable_count++) { sysclk_enable_module(SYSCLK_PORT_A, SYSCLK_ADC); } } else #endif #ifdef ADCB if ((uintptr_t) adc == (uintptr_t) (&ADCB)) { Assert(adcb_enable_count < 0xff); if (!adcb_enable_count++) { sysclk_enable_module(SYSCLK_PORT_B, SYSCLK_ADC); } } else #endif { Assert(0); } } /** * \internal * \brief Disable peripheral clock for ADC * * Decrements the enable count for the ADC, then disables its peripheral clock * if the count hit zero. If the count did not hit zero, it indicates the ADC is * enabled. * * \param adc Pointer to ADC module */ void adc_disable_clock(ADC_t * adc); void adc_disable_clock(ADC_t * adc) { #ifdef ADCA if ((uintptr_t) adc == (uintptr_t) (&ADCA)) { Assert(adca_enable_count); if (!--adca_enable_count) { sysclk_disable_module(SYSCLK_PORT_A, SYSCLK_ADC); } } else #endif #ifdef ADCB if ((uintptr_t) adc == (uintptr_t) (&ADCB)) { Assert(adcb_enable_count); if (!--adcb_enable_count) { sysclk_disable_module(SYSCLK_PORT_B, SYSCLK_ADC); } } else #endif { Assert(0); } } /** @} */ /** \name ADC module management */ /** @{ */ /** * \brief Enable ADC * * Enables the ADC and locks IDLE mode for the sleep manager. * * \param adc Pointer to ADC module * * \note 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. */ void adc_enable(ADC_t * adc) { irqflags_t flags = cpu_irq_save(); adc_enable_clock(adc); adc->CTRLA |= ADC_ENABLE_bm; cpu_irq_restore(flags); sleepmgr_lock_mode(SLEEPMGR_IDLE); } /** * \brief Disable ADC * * Disables the ADC and unlocks IDLE mode for the sleep manager. * * \param adc Pointer to ADC module */ void adc_disable(ADC_t * adc) { irqflags_t flags = cpu_irq_save(); adc->CTRLA &= ~ADC_ENABLE_bm; adc_disable_clock(adc); cpu_irq_restore(flags); sleepmgr_unlock_mode(SLEEPMGR_IDLE); } /** * \brief Check if the ADC is enabled * * \param adc Pointer to ADC module. * * \retval true if ADC is enabled. * \retval false if ADC is disabled. */ bool adc_is_enabled(ADC_t * adc) { /* It is sufficient to return the state of the ADC enable counters * since all driver functions that change the counts are protected * against interrupts and only the enable/disable functions leave the * counts incremented/decremented upon return. */ #ifdef ADCA if ((uintptr_t) adc == (uintptr_t) & ADCA) { return adca_enable_count; } else #endif #ifdef ADCB if ((uintptr_t) adc == (uintptr_t) & ADCB) { return adcb_enable_count; } else #endif { Assert(0); return false; } } /** @} */ /** @} */