Files
bacnet_stack/ports/xplained/ASF/xmega/drivers/adc/adc.c
T
2019-10-08 23:47:53 -05:00

298 lines
6.9 KiB
C

/**
* \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 <compiler.h>
#include <adc.h>
/**
* \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;
}
}
/** @} */
/** @} */